diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..fb0ecaa
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "typescript.tsdk": "src/node_modules/typescript/lib",
+ "typescript.enablePromptUseWorkspaceTsdk": true
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8c9a6b..213be9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased]
+## [v0.2.0] - 2026-05-24
### Added
-- Initial `@maksit/webui-contracts`, `@maksit/webui-core`, and `@maksit/webui-components` packages extracted from Certs UI and Vault WebUI.
+- Jest test suite (50 tests) covering `@maks-it.com/webui-core` utilities and `@maks-it.com/webui-contracts` schemas.
+- Root `npm test` script and per-package build tsconfigs (`tsconfig.build.json`) for TypeScript 6 declaration emit.
+
+### Changed
+
+- Updated dependencies to current majors: TypeScript 6, Jest 30, Zod 4.4, lucide-react 1.x, axios 1.16, and React 19.2.
+- Migrated Zod schemas to v4 APIs: `intersection()` replaces `.and()`, custom refinements use `'custom'` issue codes.
+- `@maks-it.com/webui-components` Toast IDs now use `crypto.randomUUID()`; lodash imports use `lodash/debounce` subpaths.
+- Peer dependency ranges: `zod` ^4.4, `axios` ^1.16, `lucide-react` ^1.0.
+
+### Removed
+
+- `uuid` runtime dependency from `@maks-it.com/webui-components`.
+
+## [v0.1.0] - 2026-05-24
+
+### Added
+
+- Initial `@maks-it.com/webui-contracts`, `@maks-it.com/webui-core`, and `@maks-it.com/webui-components` packages extracted from Certs UI and Vault WebUI.
+- npm publish under the `@maks-it.com` org scope on registry.npmjs.org.
diff --git a/README.md b/README.md
index 725307f..51af5dc 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# maksit-webui
+# MaksIT.WebUI
+
+  
Shared React UI library for **maksit-certs-ui** and **maksit-vault** WebUI apps.
@@ -6,9 +8,9 @@ Shared React UI library for **maksit-certs-ui** and **maksit-vault** WebUI apps.
| npm package | Description |
|-------------|-------------|
-| `@maksit/webui-contracts` | Shared TypeScript contracts (paging, gallery types, patch ops, scopes) |
-| `@maksit/webui-core` | Utilities (`deepDelta`, enum helpers, ACL parsers) and `useFormState` |
-| `@maksit/webui-components` | React components, layout, editors, DataTable, auth shell |
+| `@maks-it.com/webui-contracts` | Shared TypeScript contracts (paging, gallery types, patch ops, scopes) |
+| `@maks-it.com/webui-core` | Utilities (`deepDelta`, enum helpers, ACL parsers) and `useFormState` |
+| `@maks-it.com/webui-components` | React components, layout, editors, DataTable, auth shell |
Source lives under `src/` (npm workspaces). Release automation lives under `utils/` (from [maksit-repoutils](https://github.com/MAKS-IT-COM/maksit-repoutils)).
@@ -18,8 +20,11 @@ Source lives under `src/` (npm workspaces). Release automation lives under `util
cd src
npm install
npm run build
+npm test
```
+Tests and coverage badges: **`utils/Run-Tests/Run-Tests.bat`** (plugin config in `utils/Run-Tests/scriptsettings.json`; uses `NpmJestTest`).
+
## Release to npmjs
1. Set **`NPMJS_MAKS_IT`** to your npm automation token (same pattern as `NUGET_MAKS_IT` for NuGet).
@@ -41,7 +46,7 @@ Refresh shared utils from repoutils: **`utils/Update-RepoUtils/Update-RepoUtils.
## Consume in product repos
```bash
-npm install @maksit/webui-contracts @maksit/webui-core @maksit/webui-components
+npm install @maks-it.com/webui-contracts @maks-it.com/webui-core @maks-it.com/webui-components
```
Wrap the app with `WebUiProvider` and pass axios/redux adapters — see [assets/docs/NPM_CONSUMPTION.md](assets/docs/NPM_CONSUMPTION.md).
diff --git a/assets/badges/coverage-branches.svg b/assets/badges/coverage-branches.svg
new file mode 100644
index 0000000..74be9ef
--- /dev/null
+++ b/assets/badges/coverage-branches.svg
@@ -0,0 +1,21 @@
+
diff --git a/assets/badges/coverage-lines.svg b/assets/badges/coverage-lines.svg
new file mode 100644
index 0000000..fc05df3
--- /dev/null
+++ b/assets/badges/coverage-lines.svg
@@ -0,0 +1,21 @@
+
diff --git a/assets/badges/coverage-methods.svg b/assets/badges/coverage-methods.svg
new file mode 100644
index 0000000..e14bb74
--- /dev/null
+++ b/assets/badges/coverage-methods.svg
@@ -0,0 +1,21 @@
+
diff --git a/assets/docs/NPM_CONSUMPTION.md b/assets/docs/NPM_CONSUMPTION.md
index dcbf010..0cf3e96 100644
--- a/assets/docs/NPM_CONSUMPTION.md
+++ b/assets/docs/NPM_CONSUMPTION.md
@@ -1,15 +1,15 @@
-# Consuming @maksit/webui-* in Certs UI / Vault
+# Consuming @maks-it.com/webui-* in Certs UI / Vault
Install:
```bash
-npm install @maksit/webui-contracts @maksit/webui-core @maksit/webui-components
+npm install @maks-it.com/webui-contracts @maks-it.com/webui-core @maks-it.com/webui-components
```
Wrap the app:
```tsx
-import { WebUiProvider, Loader, Authorization } from '@maksit/webui-components'
+import { WebUiProvider, Loader, Authorization } from '@maks-it.com/webui-components'
/packages/core/src', '/packages/contracts/src'],
+ testMatch: ['**/*.test.ts'],
+ collectCoverageFrom: [
+ 'packages/core/src/**/*.ts',
+ 'packages/contracts/src/**/*.ts',
+ '!**/*.test.ts',
+ ],
+ coverageDirectory: 'coverage',
+ coverageReporters: ['json-summary', 'text'],
+ moduleNameMapper: {
+ '^(\\.{1,2}/.*)\\.js$': '$1',
+ '^@maks-it.com/webui-contracts$': '/packages/contracts/src/index.ts',
+ },
+ transform: {
+ '^.+\\.tsx?$': [
+ 'ts-jest',
+ {
+ tsconfig: '/tsconfig.jest.json',
+ },
+ ],
+ },
+}
diff --git a/src/package-lock.json b/src/package-lock.json
index bb16b00..6107c44 100644
--- a/src/package-lock.json
+++ b/src/package-lock.json
@@ -1,20 +1,467 @@
{
"name": "maksit-webui",
- "version": "0.1.0",
+ "version": "0.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "maksit-webui",
- "version": "0.1.0",
+ "version": "0.2.0",
"license": "MIT",
"workspaces": [
"packages/*"
],
+ "devDependencies": {
+ "@types/jest": "^30.0.0",
+ "jest": "^30.4.2",
+ "ts-jest": "^29.4.11"
+ },
"engines": {
"node": ">=20"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.3",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
+ "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+ "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
+ "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
+ "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
+ "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz",
+ "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/runtime": {
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
@@ -25,6 +472,72 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
@@ -467,6 +980,374 @@
"node": ">=18"
}
},
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
+ "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jest/console": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz",
+ "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "chalk": "^4.1.2",
+ "jest-message-util": "30.4.1",
+ "jest-util": "30.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/core": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz",
+ "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/console": "30.4.1",
+ "@jest/pattern": "30.4.0",
+ "@jest/reporters": "30.4.1",
+ "@jest/test-result": "30.4.1",
+ "@jest/transform": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "ansi-escapes": "^4.3.2",
+ "chalk": "^4.1.2",
+ "ci-info": "^4.2.0",
+ "exit-x": "^0.2.2",
+ "fast-json-stable-stringify": "^2.1.0",
+ "graceful-fs": "^4.2.11",
+ "jest-changed-files": "30.4.1",
+ "jest-config": "30.4.2",
+ "jest-haste-map": "30.4.1",
+ "jest-message-util": "30.4.1",
+ "jest-regex-util": "30.4.0",
+ "jest-resolve": "30.4.1",
+ "jest-resolve-dependencies": "30.4.2",
+ "jest-runner": "30.4.2",
+ "jest-runtime": "30.4.2",
+ "jest-snapshot": "30.4.1",
+ "jest-util": "30.4.1",
+ "jest-validate": "30.4.1",
+ "jest-watcher": "30.4.1",
+ "pretty-format": "30.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/diff-sequences": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz",
+ "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/environment": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz",
+ "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/fake-timers": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "jest-mock": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/expect": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz",
+ "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "expect": "30.4.1",
+ "jest-snapshot": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/expect-utils": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz",
+ "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/get-type": "30.1.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/fake-timers": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz",
+ "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "30.4.1",
+ "@sinonjs/fake-timers": "^15.4.0",
+ "@types/node": "*",
+ "jest-message-util": "30.4.1",
+ "jest-mock": "30.4.1",
+ "jest-util": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/get-type": {
+ "version": "30.1.0",
+ "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz",
+ "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/globals": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz",
+ "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "30.4.1",
+ "@jest/expect": "30.4.1",
+ "@jest/types": "30.4.1",
+ "jest-mock": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/pattern": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz",
+ "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "jest-regex-util": "30.4.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/reporters": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz",
+ "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "30.4.1",
+ "@jest/test-result": "30.4.1",
+ "@jest/transform": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "@types/node": "*",
+ "chalk": "^4.1.2",
+ "collect-v8-coverage": "^1.0.2",
+ "exit-x": "^0.2.2",
+ "glob": "^10.5.0",
+ "graceful-fs": "^4.2.11",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^6.0.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^5.0.0",
+ "istanbul-reports": "^3.1.3",
+ "jest-message-util": "30.4.1",
+ "jest-util": "30.4.1",
+ "jest-worker": "30.4.1",
+ "slash": "^3.0.0",
+ "string-length": "^4.0.2",
+ "v8-to-istanbul": "^9.0.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/schemas": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
+ "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sinclair/typebox": "^0.34.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/snapshot-utils": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz",
+ "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "30.4.1",
+ "chalk": "^4.1.2",
+ "graceful-fs": "^4.2.11",
+ "natural-compare": "^1.4.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/source-map": {
+ "version": "30.0.1",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz",
+ "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "callsites": "^3.1.0",
+ "graceful-fs": "^4.2.11"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/test-result": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz",
+ "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/console": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/istanbul-lib-coverage": "^2.0.6",
+ "collect-v8-coverage": "^1.0.2"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/test-sequencer": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz",
+ "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/test-result": "30.4.1",
+ "graceful-fs": "^4.2.11",
+ "jest-haste-map": "30.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/transform": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz",
+ "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.27.4",
+ "@jest/types": "30.4.1",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "babel-plugin-istanbul": "^7.0.1",
+ "chalk": "^4.1.2",
+ "convert-source-map": "^2.0.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "graceful-fs": "^4.2.11",
+ "jest-haste-map": "30.4.1",
+ "jest-regex-util": "30.4.0",
+ "jest-util": "30.4.1",
+ "pirates": "^4.0.7",
+ "slash": "^3.0.0",
+ "write-file-atomic": "^5.0.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz",
+ "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/pattern": "30.4.0",
+ "@jest/schemas": "30.4.1",
+ "@types/istanbul-lib-coverage": "^2.0.6",
+ "@types/istanbul-reports": "^3.0.4",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.33",
+ "chalk": "^4.1.2"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -478,6 +1359,17 @@
"@jridgewell/trace-mapping": "^0.3.24"
}
},
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@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",
@@ -506,18 +1398,50 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "node_modules/@maksit/webui-components": {
+ "node_modules/@maks-it.com/webui-components": {
"resolved": "packages/components",
"link": true
},
- "node_modules/@maksit/webui-contracts": {
+ "node_modules/@maks-it.com/webui-contracts": {
"resolved": "packages/contracts",
"link": true
},
- "node_modules/@maksit/webui-core": {
+ "node_modules/@maks-it.com/webui-core": {
"resolved": "packages/core",
"link": true
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@pkgr/core": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
+ "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/pkgr"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.60.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz",
@@ -868,6 +1792,33 @@
"win32"
]
},
+ "node_modules/@sinclair/typebox": {
+ "version": "0.34.49",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
+ "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "15.4.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz",
+ "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1"
+ }
+ },
"node_modules/@tanstack/react-table": {
"version": "8.21.3",
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
@@ -903,6 +1854,62 @@
"url": "https://github.com/sponsors/tannerlinsley"
}
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+ "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -910,6 +1917,44 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/@types/jest": {
+ "version": "30.0.0",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz",
+ "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "expect": "^30.0.0",
+ "pretty-format": "^30.0.0"
+ }
+ },
"node_modules/@types/lodash": {
"version": "4.17.24",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz",
@@ -917,6 +1962,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/node": {
+ "version": "25.9.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
+ "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": ">=7.24.0 <7.24.7"
+ }
+ },
"node_modules/@types/prop-types": {
"version": "15.7.15",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
@@ -956,6 +2011,350 @@
"@types/react": "*"
}
},
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.35",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
+ "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
+ "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz",
+ "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz",
+ "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz",
+ "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz",
+ "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz",
+ "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz",
+ "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz",
+ "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz",
+ "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz",
+ "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-loong64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz",
+ "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-loong64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz",
+ "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz",
+ "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz",
+ "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz",
+ "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz",
+ "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz",
+ "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz",
+ "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-openharmony-arm64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz",
+ "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz",
+ "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
+ "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz",
+ "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz",
+ "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/acorn": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
@@ -982,6 +2381,38 @@
"node": ">= 6.0.0"
}
},
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -989,6 +2420,43 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -1009,6 +2477,206 @@
"proxy-from-env": "^2.1.0"
}
},
+ "node_modules/babel-jest": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz",
+ "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/transform": "30.4.1",
+ "@types/babel__core": "^7.20.5",
+ "babel-plugin-istanbul": "^7.0.1",
+ "babel-preset-jest": "30.4.0",
+ "chalk": "^4.1.2",
+ "graceful-fs": "^4.2.11",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.11.0 || ^8.0.0-0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz",
+ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "workspaces": [
+ "test/babel-8"
+ ],
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-instrument": "^6.0.2",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz",
+ "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/babel__core": "^7.20.5"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/babel-preset-current-node-syntax": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz",
+ "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-import-attributes": "^7.24.7",
+ "@babel/plugin-syntax-import-meta": "^7.10.4",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0 || ^8.0.0-0"
+ }
+ },
+ "node_modules/babel-preset-jest": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz",
+ "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "babel-plugin-jest-hoist": "30.4.0",
+ "babel-preset-current-node-syntax": "^1.2.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.11.0 || ^8.0.0-beta.1"
+ }
+ },
+ "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/baseline-browser-mapping": {
+ "version": "2.10.32",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz",
+ "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bs-logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
+ "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-json-stable-stringify": "2.x"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/bundle-require": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz",
@@ -1049,6 +2717,74 @@
"node": ">= 0.4"
}
},
+ "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": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001793",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
+ "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "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": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
@@ -1065,6 +2801,107 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz",
+ "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
@@ -1075,6 +2912,44 @@
"node": ">=6"
}
},
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/collect-v8-coverage": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
+ "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "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/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1115,6 +2990,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/cookie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
@@ -1129,6 +3011,21 @@
"url": "https://opencollective.com/express"
}
},
+ "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/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -1164,6 +3061,31 @@
}
}
},
+ "node_modules/dedent": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
+ "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1174,6 +3096,16 @@
"node": ">=0.4.0"
}
},
+ "node_modules/detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@@ -1200,6 +3132,36 @@
"node": ">= 0.4"
}
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.361",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz",
+ "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emittery": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+ }
+ },
+ "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-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -1292,6 +3254,109 @@
"@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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exit-x": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz",
+ "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expect": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz",
+ "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/expect-utils": "30.4.1",
+ "@jest/get-type": "30.1.0",
+ "jest-matcher-utils": "30.4.1",
+ "jest-message-util": "30.4.1",
+ "jest-mock": "30.4.1",
+ "jest-util": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.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/fb-watchman": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bser": "2.1.1"
+ }
+ },
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -1310,6 +3375,20 @@
}
}
},
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"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",
@@ -1385,6 +3464,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "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-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -1410,6 +3509,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@@ -1424,6 +3533,37 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1437,6 +3577,55 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/handlebars": {
+ "version": "4.7.9",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
+ "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.2",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
+ "node_modules/handlebars/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -1479,6 +3668,13 @@
"node": ">= 0.4"
}
},
+ "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/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -1493,6 +3689,771 @@
"node": ">= 6"
}
},
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
+ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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-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-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "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",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "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-instrument": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
+ "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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-lib-source-maps": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
+ "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.23",
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.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/jest": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz",
+ "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@jest/core": "30.4.2",
+ "@jest/types": "30.4.1",
+ "import-local": "^3.2.0",
+ "jest-cli": "30.4.2"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-changed-files": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz",
+ "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "execa": "^5.1.1",
+ "jest-util": "30.4.1",
+ "p-limit": "^3.1.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-circus": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz",
+ "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "30.4.1",
+ "@jest/expect": "30.4.1",
+ "@jest/test-result": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "chalk": "^4.1.2",
+ "co": "^4.6.0",
+ "dedent": "^1.6.0",
+ "is-generator-fn": "^2.1.0",
+ "jest-each": "30.4.1",
+ "jest-matcher-utils": "30.4.1",
+ "jest-message-util": "30.4.1",
+ "jest-runtime": "30.4.2",
+ "jest-snapshot": "30.4.1",
+ "jest-util": "30.4.1",
+ "p-limit": "^3.1.0",
+ "pretty-format": "30.4.1",
+ "pure-rand": "^7.0.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.6"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-cli": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz",
+ "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/core": "30.4.2",
+ "@jest/test-result": "30.4.1",
+ "@jest/types": "30.4.1",
+ "chalk": "^4.1.2",
+ "exit-x": "^0.2.2",
+ "import-local": "^3.2.0",
+ "jest-config": "30.4.2",
+ "jest-util": "30.4.1",
+ "jest-validate": "30.4.1",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-config": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz",
+ "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.27.4",
+ "@jest/get-type": "30.1.0",
+ "@jest/pattern": "30.4.0",
+ "@jest/test-sequencer": "30.4.1",
+ "@jest/types": "30.4.1",
+ "babel-jest": "30.4.1",
+ "chalk": "^4.1.2",
+ "ci-info": "^4.2.0",
+ "deepmerge": "^4.3.1",
+ "glob": "^10.5.0",
+ "graceful-fs": "^4.2.11",
+ "jest-circus": "30.4.2",
+ "jest-docblock": "30.4.0",
+ "jest-environment-node": "30.4.1",
+ "jest-regex-util": "30.4.0",
+ "jest-resolve": "30.4.1",
+ "jest-runner": "30.4.2",
+ "jest-util": "30.4.1",
+ "jest-validate": "30.4.1",
+ "parse-json": "^5.2.0",
+ "pretty-format": "30.4.1",
+ "slash": "^3.0.0",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "esbuild-register": ">=3.4.0",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "esbuild-register": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-diff": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz",
+ "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/diff-sequences": "30.4.0",
+ "@jest/get-type": "30.1.0",
+ "chalk": "^4.1.2",
+ "pretty-format": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-docblock": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz",
+ "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-newline": "^3.1.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-each": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz",
+ "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/get-type": "30.1.0",
+ "@jest/types": "30.4.1",
+ "chalk": "^4.1.2",
+ "jest-util": "30.4.1",
+ "pretty-format": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-environment-node": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz",
+ "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "30.4.1",
+ "@jest/fake-timers": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "jest-mock": "30.4.1",
+ "jest-util": "30.4.1",
+ "jest-validate": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-haste-map": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz",
+ "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "anymatch": "^3.1.3",
+ "fb-watchman": "^2.0.2",
+ "graceful-fs": "^4.2.11",
+ "jest-regex-util": "30.4.0",
+ "jest-util": "30.4.1",
+ "jest-worker": "30.4.1",
+ "picomatch": "^4.0.3",
+ "walker": "^1.0.8"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.3"
+ }
+ },
+ "node_modules/jest-leak-detector": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz",
+ "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/get-type": "30.1.0",
+ "pretty-format": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz",
+ "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/get-type": "30.1.0",
+ "chalk": "^4.1.2",
+ "jest-diff": "30.4.1",
+ "pretty-format": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-message-util": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz",
+ "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@jest/types": "30.4.1",
+ "@types/stack-utils": "^2.0.3",
+ "chalk": "^4.1.2",
+ "graceful-fs": "^4.2.11",
+ "jest-util": "30.4.1",
+ "picomatch": "^4.0.3",
+ "pretty-format": "30.4.1",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.6"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-mock": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz",
+ "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "jest-util": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-pnp-resolver": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "jest-resolve": "*"
+ },
+ "peerDependenciesMeta": {
+ "jest-resolve": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-regex-util": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz",
+ "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-resolve": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz",
+ "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "graceful-fs": "^4.2.11",
+ "jest-haste-map": "30.4.1",
+ "jest-pnp-resolver": "^1.2.3",
+ "jest-util": "30.4.1",
+ "jest-validate": "30.4.1",
+ "slash": "^3.0.0",
+ "unrs-resolver": "^1.7.11"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-resolve-dependencies": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz",
+ "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jest-regex-util": "30.4.0",
+ "jest-snapshot": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-runner": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz",
+ "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/console": "30.4.1",
+ "@jest/environment": "30.4.1",
+ "@jest/test-result": "30.4.1",
+ "@jest/transform": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "chalk": "^4.1.2",
+ "emittery": "^0.13.1",
+ "exit-x": "^0.2.2",
+ "graceful-fs": "^4.2.11",
+ "jest-docblock": "30.4.0",
+ "jest-environment-node": "30.4.1",
+ "jest-haste-map": "30.4.1",
+ "jest-leak-detector": "30.4.1",
+ "jest-message-util": "30.4.1",
+ "jest-resolve": "30.4.1",
+ "jest-runtime": "30.4.2",
+ "jest-util": "30.4.1",
+ "jest-watcher": "30.4.1",
+ "jest-worker": "30.4.1",
+ "p-limit": "^3.1.0",
+ "source-map-support": "0.5.13"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-runtime": {
+ "version": "30.4.2",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz",
+ "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "30.4.1",
+ "@jest/fake-timers": "30.4.1",
+ "@jest/globals": "30.4.1",
+ "@jest/source-map": "30.0.1",
+ "@jest/test-result": "30.4.1",
+ "@jest/transform": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "chalk": "^4.1.2",
+ "cjs-module-lexer": "^2.1.0",
+ "collect-v8-coverage": "^1.0.2",
+ "glob": "^10.5.0",
+ "graceful-fs": "^4.2.11",
+ "jest-haste-map": "30.4.1",
+ "jest-message-util": "30.4.1",
+ "jest-mock": "30.4.1",
+ "jest-regex-util": "30.4.0",
+ "jest-resolve": "30.4.1",
+ "jest-snapshot": "30.4.1",
+ "jest-util": "30.4.1",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-snapshot": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz",
+ "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.27.4",
+ "@babel/generator": "^7.27.5",
+ "@babel/plugin-syntax-jsx": "^7.27.1",
+ "@babel/plugin-syntax-typescript": "^7.27.1",
+ "@babel/types": "^7.27.3",
+ "@jest/expect-utils": "30.4.1",
+ "@jest/get-type": "30.1.0",
+ "@jest/snapshot-utils": "30.4.1",
+ "@jest/transform": "30.4.1",
+ "@jest/types": "30.4.1",
+ "babel-preset-current-node-syntax": "^1.2.0",
+ "chalk": "^4.1.2",
+ "expect": "30.4.1",
+ "graceful-fs": "^4.2.11",
+ "jest-diff": "30.4.1",
+ "jest-matcher-utils": "30.4.1",
+ "jest-message-util": "30.4.1",
+ "jest-util": "30.4.1",
+ "pretty-format": "30.4.1",
+ "semver": "^7.7.2",
+ "synckit": "^0.11.8"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/semver": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
+ "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-util": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz",
+ "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "chalk": "^4.1.2",
+ "ci-info": "^4.2.0",
+ "graceful-fs": "^4.2.11",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-validate": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz",
+ "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/get-type": "30.1.0",
+ "@jest/types": "30.4.1",
+ "camelcase": "^6.3.0",
+ "chalk": "^4.1.2",
+ "leven": "^3.1.0",
+ "pretty-format": "30.4.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-validate/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jest-watcher": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz",
+ "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/test-result": "30.4.1",
+ "@jest/types": "30.4.1",
+ "@types/node": "*",
+ "ansi-escapes": "^4.3.2",
+ "chalk": "^4.1.2",
+ "emittery": "^0.13.1",
+ "jest-util": "30.4.1",
+ "string-length": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz",
+ "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@ungap/structured-clone": "^1.3.0",
+ "jest-util": "30.4.1",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.1.1"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
"node_modules/joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
@@ -1510,6 +4471,63 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
@@ -1540,12 +4558,32 @@
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -1559,10 +4597,20 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
"node_modules/lucide-react": {
- "version": "0.576.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.576.0.tgz",
- "integrity": "sha512-koNxU14BXrxUfZQ9cUaP0ES1uyPZKYDjk31FQZB6dQ/x+tXk979sVAn9ppZ/pVeJJyOxVM8j1E+8QEuSc02Vug==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.16.0.tgz",
+ "integrity": "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==",
"dev": true,
"license": "ISC",
"peerDependencies": {
@@ -1579,6 +4627,52 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
+ "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/make-dir/node_modules/semver": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
+ "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/makeerror": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tmpl": "1.0.5"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -1589,6 +4683,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -1612,6 +4713,52 @@
"node": ">= 0.6"
}
},
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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",
@@ -1644,6 +4791,76 @@
"thenify-all": "^1.0.0"
}
},
+ "node_modules/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "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/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.46",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz",
+ "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -1654,6 +4871,143 @@
"node": ">=0.10.0"
}
},
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-locate/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "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",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/path-scurry/node_modules/lru-cache": {
+ "version": "11.5.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz",
+ "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -1692,6 +5046,19 @@
"node": ">= 6"
}
},
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/pkg-types": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
@@ -1747,6 +5114,35 @@
}
}
},
+ "node_modules/pretty-format": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz",
+ "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/schemas": "30.4.1",
+ "ansi-styles": "^5.2.0",
+ "react-is-18": "npm:react-is@^18.3.1",
+ "react-is-19": "npm:react-is@^19.2.5"
+ },
+ "engines": {
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -1769,6 +5165,23 @@
"node": ">=10"
}
},
+ "node_modules/pure-rand": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz",
+ "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/dubzzz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fast-check"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/react": {
"version": "19.2.6",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
@@ -1801,6 +5214,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/react-is-18": {
+ "name": "react-is",
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/react-is-19": {
+ "name": "react-is",
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz",
+ "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
@@ -1881,6 +5310,29 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -1943,6 +5395,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
@@ -1950,6 +5412,46 @@
"dev": true,
"license": "MIT"
},
+ "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/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "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",
@@ -1960,6 +5462,117 @@
"node": ">= 12"
}
},
+ "node_modules/source-map-support": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-length/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-length/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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",
@@ -1983,6 +5596,50 @@
"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.12",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz",
+ "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pkgr/core": "^0.2.9"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/synckit"
+ }
+ },
+ "node_modules/test-exclude": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz",
+ "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^13.0.6",
+ "minimatch": "^10.2.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -2030,6 +5687,13 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "node_modules/tmpl": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -2047,6 +5711,93 @@
"dev": true,
"license": "Apache-2.0"
},
+ "node_modules/ts-jest": {
+ "version": "29.4.11",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz",
+ "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bs-logger": "^0.2.6",
+ "fast-json-stable-stringify": "^2.1.0",
+ "handlebars": "^4.7.9",
+ "json5": "^2.2.3",
+ "lodash.memoize": "^4.1.2",
+ "make-error": "^1.3.6",
+ "semver": "^7.8.0",
+ "type-fest": "^4.41.0",
+ "yargs-parser": "^21.1.1"
+ },
+ "bin": {
+ "ts-jest": "cli.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": ">=7.0.0-beta.0 <8",
+ "@jest/transform": "^29.0.0 || ^30.0.0",
+ "@jest/types": "^29.0.0 || ^30.0.0",
+ "babel-jest": "^29.0.0 || ^30.0.0",
+ "jest": "^29.0.0 || ^30.0.0",
+ "jest-util": "^29.0.0 || ^30.0.0",
+ "typescript": ">=4.3 <7"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "@jest/transform": {
+ "optional": true
+ },
+ "@jest/types": {
+ "optional": true
+ },
+ "babel-jest": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jest-util": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-jest/node_modules/semver": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
+ "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ts-jest/node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
"node_modules/tsup": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz",
@@ -2100,12 +5851,36 @@
}
}
},
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -2121,17 +5896,273 @@
"dev": true,
"license": "MIT"
},
- "node_modules/uuid": {
- "version": "13.0.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz",
- "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==",
+ "node_modules/uglify-js": {
+ "version": "3.19.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
+ "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unrs-resolver": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz",
+ "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.4"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.12.2",
+ "@unrs/resolver-binding-android-arm64": "1.12.2",
+ "@unrs/resolver-binding-darwin-arm64": "1.12.2",
+ "@unrs/resolver-binding-darwin-x64": "1.12.2",
+ "@unrs/resolver-binding-freebsd-x64": "1.12.2",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.12.2",
+ "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-loong64-musl": "1.12.2",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-x64-musl": "1.12.2",
+ "@unrs/resolver-binding-openharmony-arm64": "1.12.2",
+ "@unrs/resolver-binding-wasm32-wasi": "1.12.2",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.12.2"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
"funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
],
"license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
"bin": {
- "uuid": "dist-node/bin/uuid"
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/v8-to-istanbul": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
+ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.12",
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/walker": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "makeerror": "1.0.12"
+ }
+ },
+ "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/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/write-file-atomic": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz",
+ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/write-file-atomic/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/zod": {
@@ -2145,71 +6176,70 @@
}
},
"packages/components": {
- "name": "@maksit/webui-components",
- "version": "0.1.0",
+ "name": "@maks-it.com/webui-components",
+ "version": "0.2.0",
"dependencies": {
- "@maksit/webui-contracts": "^0.1.0",
- "@maksit/webui-core": "^0.1.0",
- "date-fns": "^4.1.0",
- "lodash": "^4.17.23",
- "uuid": "^13.0.0"
+ "@maks-it.com/webui-contracts": "^0.2.0",
+ "@maks-it.com/webui-core": "^0.2.0",
+ "date-fns": "^4.3.0",
+ "lodash": "^4.18.1"
},
"devDependencies": {
"@tanstack/react-table": "^8.21.3",
"@types/lodash": "^4.17.24",
- "@types/react": "^19.2.14",
+ "@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@types/react-virtualized": "^9.22.3",
- "lucide-react": "^0.576.0",
- "react": "^19.2.4",
- "react-dom": "^19.2.4",
- "react-router-dom": "^7.13.1",
+ "lucide-react": "^1.16.0",
+ "react": "^19.2.6",
+ "react-dom": "^19.2.6",
+ "react-router-dom": "^7.15.1",
"react-virtualized": "^9.22.6",
- "tsup": "^8.5.0",
- "typescript": "^5.9.3",
- "zod": "^4.3.6"
+ "tsup": "^8.5.1",
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
},
"peerDependencies": {
"@tanstack/react-table": "^8.0.0",
- "lucide-react": "^0.500.0",
+ "lucide-react": "^1.0.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"react-router-dom": "^7.0.0",
"react-virtualized": "^9.22.0",
- "zod": "^4.0.0"
+ "zod": "^4.4.0"
}
},
"packages/contracts": {
- "name": "@maksit/webui-contracts",
- "version": "0.1.0",
+ "name": "@maks-it.com/webui-contracts",
+ "version": "0.2.0",
"devDependencies": {
- "tsup": "^8.5.0",
- "typescript": "^5.9.3",
- "zod": "^4.3.6"
+ "tsup": "^8.5.1",
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
},
"peerDependencies": {
- "zod": "^4.0.0"
+ "zod": "^4.4.0"
}
},
"packages/core": {
- "name": "@maksit/webui-core",
- "version": "0.1.0",
+ "name": "@maks-it.com/webui-core",
+ "version": "0.2.0",
"dependencies": {
- "@maksit/webui-contracts": "^0.1.0",
- "date-fns": "^4.1.0"
+ "@maks-it.com/webui-contracts": "^0.2.0",
+ "date-fns": "^4.3.0"
},
"devDependencies": {
- "@types/react": "^19.2.14",
- "axios": "^1.13.2",
- "react": "^19.2.4",
- "tsup": "^8.5.0",
- "typescript": "^5.9.3",
- "zod": "^4.3.6"
+ "@types/react": "^19.2.15",
+ "axios": "^1.16.1",
+ "react": "^19.2.6",
+ "tsup": "^8.5.1",
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
},
"peerDependencies": {
- "axios": "^1.7.0",
+ "axios": "^1.16.0",
"react": "^18.0.0 || ^19.0.0",
- "zod": "^4.0.0"
+ "zod": "^4.4.0"
}
}
}
diff --git a/src/package.json b/src/package.json
index 28dd05b..de14fce 100644
--- a/src/package.json
+++ b/src/package.json
@@ -1,7 +1,7 @@
{
"name": "maksit-webui",
"private": true,
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Shared React UI library for MaksIT Certs UI and Vault WebUI",
"workspaces": [
"packages/*"
@@ -10,13 +10,22 @@
"node": ">=20"
},
"overrides": {
- "zod": "^4.3.6"
+ "zod": "^4.4.3",
+ "glob": "^13.0.6",
+ "test-exclude": "^8.0.0"
},
"scripts": {
- "build": "npm run build -w @maksit/webui-contracts && npm run build -w @maksit/webui-core && npm run build -w @maksit/webui-components",
+ "build": "npm run build -w @maks-it.com/webui-contracts && npm run build -w @maks-it.com/webui-core && npm run build -w @maks-it.com/webui-components",
+ "test": "jest --config jest.config.cjs",
+ "test:coverage": "jest --config jest.config.cjs --coverage",
"typecheck": "npm run typecheck --workspaces --if-present",
"clean": "npm run clean --workspaces --if-present"
},
+ "devDependencies": {
+ "@types/jest": "^30.0.0",
+ "jest": "^30.4.2",
+ "ts-jest": "^29.4.11"
+ },
"author": "MaksIT",
"license": "MIT"
}
diff --git a/src/packages/components/README.md b/src/packages/components/README.md
new file mode 100644
index 0000000..d052635
--- /dev/null
+++ b/src/packages/components/README.md
@@ -0,0 +1,66 @@
+# @maks-it.com/webui-components
+
+Shared React UI components for MaksIT WebUI apps: forms, DataTable, layout, editors, and list views.
+
+Depends on `@maks-it.com/webui-core` and `@maks-it.com/webui-contracts`. Peer dependencies must be installed in the host app.
+
+## Install
+
+```bash
+npm install @maks-it.com/webui-components @maks-it.com/webui-core @maks-it.com/webui-contracts
+npm install react react-dom react-router-dom lucide-react @tanstack/react-table react-virtualized zod
+```
+
+## Components
+
+| Export | Purpose |
+|--------|---------|
+| `DataTable`, `DataTableFilter`, `DataTableLabel` | Virtualized server-paged tables |
+| `RemoteSelectBoxComponent` | Async search select (pass `searchRoute` API path) |
+| `SecretComponent` | Secret field with optional generate action |
+| `FormContainer`, `FormHeader`, `FormContent`, `FormFooter` | Form layout shell |
+| `Layout` | App chrome / navigation wrapper |
+| `Offcanvas` | Slide-over panel |
+| `LazyLoadTable` | Incrementally loaded table |
+| `VaultStyleDataTable`, `VaultStyleListSection` | Vault-style list layouts |
+| `EntityScopesSummary` | Entity scope permissions summary |
+| `Toast`, `addToast` | Toast notifications |
+| `FieldContainer` | Label + validation wrapper for fields |
+
+## Example — DataTable
+
+```tsx
+import { DataTable, createColumns } from '@maks-it.com/webui-components'
+
+const columns = createColumns([
+ { key: 'name', header: 'Name' },
+ { key: 'createdAt', header: 'Created', label: 'date' },
+])
+
+
+```
+
+## Example — remote select
+
+```tsx
+import { RemoteSelectBoxComponent } from '@maks-it.com/webui-components'
+
+
+```
+
+## Repository
+
+[github.com/MAKS-IT-COM/maksit-webui](https://github.com/MAKS-IT-COM/maksit-webui) — `src/packages/components`
+
+## License
+
+MIT
diff --git a/src/packages/components/package.json b/src/packages/components/package.json
index 0aeaab8..ebbfc6b 100644
--- a/src/packages/components/package.json
+++ b/src/packages/components/package.json
@@ -1,6 +1,6 @@
{
- "name": "@maksit/webui-components",
- "version": "0.1.0",
+ "name": "@maks-it.com/webui-components",
+ "version": "0.2.0",
"description": "Shared React components for MaksIT WebUI apps",
"type": "module",
"main": "./dist/index.cjs",
@@ -18,7 +18,7 @@
"README.md"
],
"scripts": {
- "build": "tsup src/index.ts --format esm,cjs --dts --clean --external react --external react-dom --external react-router-dom --external lucide-react --external @tanstack/react-table --external react-virtualized",
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean --tsconfig tsconfig.build.json --external react --external react-dom --external react-router-dom --external lucide-react --external @tanstack/react-table --external react-virtualized",
"typecheck": "tsc -p tsconfig.json --noEmit",
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
"prepublishOnly": "npm run build"
@@ -33,34 +33,33 @@
"directory": "src/packages/components"
},
"dependencies": {
- "@maksit/webui-contracts": "^0.1.0",
- "@maksit/webui-core": "^0.1.0",
- "date-fns": "^4.1.0",
- "lodash": "^4.17.23",
- "uuid": "^13.0.0"
+ "@maks-it.com/webui-contracts": "^0.2.0",
+ "@maks-it.com/webui-core": "^0.2.0",
+ "date-fns": "^4.3.0",
+ "lodash": "^4.18.1"
},
"peerDependencies": {
"@tanstack/react-table": "^8.0.0",
- "lucide-react": "^0.500.0",
+ "lucide-react": "^1.0.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"react-router-dom": "^7.0.0",
"react-virtualized": "^9.22.0",
- "zod": "^4.0.0"
+ "zod": "^4.4.0"
},
"devDependencies": {
"@tanstack/react-table": "^8.21.3",
"@types/lodash": "^4.17.24",
- "@types/react": "^19.2.14",
+ "@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@types/react-virtualized": "^9.22.3",
- "lucide-react": "^0.576.0",
- "react": "^19.2.4",
- "react-dom": "^19.2.4",
- "react-router-dom": "^7.13.1",
+ "lucide-react": "^1.16.0",
+ "react": "^19.2.6",
+ "react-dom": "^19.2.6",
+ "react-router-dom": "^7.15.1",
"react-virtualized": "^9.22.6",
- "tsup": "^8.5.0",
- "typescript": "^5.9.3",
- "zod": "^4.3.6"
+ "tsup": "^8.5.1",
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
}
}
diff --git a/src/packages/components/src/components/DataTable/DataTable.tsx b/src/packages/components/src/components/DataTable/DataTable.tsx
index f6a5639..ef07043 100644
--- a/src/packages/components/src/components/DataTable/DataTable.tsx
+++ b/src/packages/components/src/components/DataTable/DataTable.tsx
@@ -1,9 +1,9 @@
import React, { useState, useMemo, useRef, useEffect } from 'react'
import { AutoSizer, MultiGrid, GridCellProps } from 'react-virtualized'
-import { mapPagedToDataTable, type DataTablePageView, type PagedResponse } from '@maksit/webui-core'
+import { mapPagedToDataTable, type DataTablePageView, type PagedResponse } from '@maks-it.com/webui-core'
import { Plus, Trash2, Edit } from 'lucide-react'
-import { debounce } from 'lodash'
+import debounce from 'lodash/debounce'
interface FilterProps {
diff --git a/src/packages/components/src/components/DataTable/DataTableFilter.tsx b/src/packages/components/src/components/DataTable/DataTableFilter.tsx
index a0d1198..33e5f58 100644
--- a/src/packages/components/src/components/DataTable/DataTableFilter.tsx
+++ b/src/packages/components/src/components/DataTable/DataTableFilter.tsx
@@ -1,5 +1,5 @@
import { useMemo, useState } from 'react'
-import { debounce } from 'lodash'
+import debounce from 'lodash/debounce'
interface FilterPropsBase {
filterId?: string
diff --git a/src/packages/components/src/components/DataTable/DataTableLabel.tsx b/src/packages/components/src/components/DataTable/DataTableLabel.tsx
index d53c41c..c85687f 100644
--- a/src/packages/components/src/components/DataTable/DataTableLabel.tsx
+++ b/src/packages/components/src/components/DataTable/DataTableLabel.tsx
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react'
-import { formatISODateString } from '@maksit/webui-core'
+import { formatISODateString } from '@maks-it.com/webui-core'
interface NormalLabelProps {
type: 'normal'
diff --git a/src/packages/components/src/components/Scopes/EntityScopesSummary.tsx b/src/packages/components/src/components/Scopes/EntityScopesSummary.tsx
index 44ec519..34abae1 100644
--- a/src/packages/components/src/components/Scopes/EntityScopesSummary.tsx
+++ b/src/packages/components/src/components/Scopes/EntityScopesSummary.tsx
@@ -1,4 +1,4 @@
-import type { SearchEntityScopeEntry } from '@maksit/webui-contracts'
+import type { SearchEntityScopeEntry } from '@maks-it.com/webui-contracts'
export interface EntityScopesSummaryProps {
entries: SearchEntityScopeEntry[]
diff --git a/src/packages/components/src/components/Toast/index.tsx b/src/packages/components/src/components/Toast/index.tsx
index 74260f3..7934fb1 100644
--- a/src/packages/components/src/components/Toast/index.tsx
+++ b/src/packages/components/src/components/Toast/index.tsx
@@ -1,5 +1,4 @@
import { useState, useEffect, FC } from 'react'
-import { v4 as uuidv4 } from 'uuid'
// Define types for a toast
interface Toast {
@@ -9,6 +8,8 @@ interface Toast {
duration?: number;
}
+const createToastId = (): string => crypto.randomUUID()
+
const Toast: FC = () => {
const [toasts, setToasts] = useState([])
@@ -17,7 +18,7 @@ const Toast: FC = () => {
const { message, type, duration } = event.detail
// Add the new toast, avoiding duplicates with same message & type
- const id = uuidv4()
+ const id = createToastId()
setToasts(prev => {
const hasDuplicate = prev.some(t => t.message === message && t.type === type)
if (hasDuplicate) return prev
diff --git a/src/packages/components/src/components/editors/FileUploadComponent.tsx b/src/packages/components/src/components/editors/FileUploadComponent.tsx
index 1ee1cb3..ebe0f8d 100644
--- a/src/packages/components/src/components/editors/FileUploadComponent.tsx
+++ b/src/packages/components/src/components/editors/FileUploadComponent.tsx
@@ -1,6 +1,6 @@
import React, { useRef, useState } from 'react'
import { ButtonComponent } from './ButtonComponent'
-import { TrashIcon } from 'lucide-react'
+import { Trash2 } from 'lucide-react'
interface FileUploadComponentProps {
label?: string
@@ -163,7 +163,7 @@ const FileUploadComponent: React.FC = ({
disabled={disabled || displayFiles.length === 0}
colspan={1}
>
-
+
{/* Select files button */}
diff --git a/src/packages/components/src/components/editors/RemoteSelectBoxComponent.tsx b/src/packages/components/src/components/editors/RemoteSelectBoxComponent.tsx
index 9f37bb4..0be9346 100644
--- a/src/packages/components/src/components/editors/RemoteSelectBoxComponent.tsx
+++ b/src/packages/components/src/components/editors/RemoteSelectBoxComponent.tsx
@@ -1,7 +1,7 @@
import { useState, useCallback, ChangeEvent, useEffect, useRef } from 'react'
-import type { PagedRequest } from '@maksit/webui-contracts'
-import type { SearchResponseBase } from '@maksit/webui-contracts'
-import { deepEqual } from '@maksit/webui-core'
+import type { PagedRequest } from '@maks-it.com/webui-contracts'
+import type { SearchResponseBase } from '@maks-it.com/webui-contracts'
+import { deepEqual } from '@maks-it.com/webui-core'
import { SelectBoxComponent } from './SelectBoxComponent'
export type RemoteSelectSearchDataSource = (
diff --git a/src/packages/components/src/components/editors/SelectBoxComponent.tsx b/src/packages/components/src/components/editors/SelectBoxComponent.tsx
index 73c1064..8a3b65a 100644
--- a/src/packages/components/src/components/editors/SelectBoxComponent.tsx
+++ b/src/packages/components/src/components/editors/SelectBoxComponent.tsx
@@ -1,4 +1,4 @@
-import { debounce } from 'lodash'
+import debounce from 'lodash/debounce'
import { CircleX } from 'lucide-react'
import { ChangeEvent, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { FieldContainer } from './FieldContainer'
diff --git a/src/packages/components/tsconfig.build.json b/src/packages/components/tsconfig.build.json
new file mode 100644
index 0000000..35bcdb2
--- /dev/null
+++ b/src/packages/components/tsconfig.build.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "ignoreDeprecations": "6.0"
+ }
+}
diff --git a/src/packages/contracts/README.md b/src/packages/contracts/README.md
new file mode 100644
index 0000000..3247510
--- /dev/null
+++ b/src/packages/contracts/README.md
@@ -0,0 +1,48 @@
+# @maks-it.com/webui-contracts
+
+Shared TypeScript contracts and Zod schemas for MaksIT WebUI apps (Certs UI, Vault WebUI, and related products).
+
+## Install
+
+```bash
+npm install @maks-it.com/webui-contracts zod
+```
+
+`zod` is a peer dependency (schemas use Zod v4).
+
+## Contents
+
+| Area | Exports |
+|------|---------|
+| Paging | `PagedRequest`, `PagedRequestSchema`, `PagedResponse` |
+| PATCH | `PatchOperation`, `PatchRequestModelBase`, `PatchRequestModelBaseSchema` |
+| API errors | `ProblemDetails` |
+| Search | `SearchResponseBase`, `SearchEntityScopeEntry` |
+| Identity | `LoginRequest` / `LoginRequestSchema`, `LoginResponse`, `RefreshTokenRequest`, `LogoutRequest`, `Claims` |
+| Misc | `TrngResponse`, `RequestModelBase`, `ResponseModelBase` |
+
+## Example
+
+```ts
+import {
+ LoginRequestSchema,
+ type PagedRequest,
+ PatchOperation,
+} from '@maks-it.com/webui-contracts'
+
+const login = LoginRequestSchema.parse({ username: 'admin', password: '***' })
+
+const page: PagedRequest = {
+ pageNumber: 1,
+ pageSize: 25,
+ filters: 'Name.Contains("cert")',
+}
+```
+
+## Repository
+
+[github.com/MAKS-IT-COM/maksit-webui](https://github.com/MAKS-IT-COM/maksit-webui) — `src/packages/contracts`
+
+## License
+
+MIT
diff --git a/src/packages/contracts/package.json b/src/packages/contracts/package.json
index 5bd15d0..4c7c712 100644
--- a/src/packages/contracts/package.json
+++ b/src/packages/contracts/package.json
@@ -1,6 +1,6 @@
{
- "name": "@maksit/webui-contracts",
- "version": "0.1.0",
+ "name": "@maks-it.com/webui-contracts",
+ "version": "0.2.0",
"description": "Shared TypeScript contracts for MaksIT WebUI apps",
"type": "module",
"main": "./dist/index.cjs",
@@ -18,7 +18,8 @@
"README.md"
],
"scripts": {
- "build": "tsup src/index.ts --format esm,cjs --dts --clean",
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean --tsconfig tsconfig.build.json",
+ "test": "jest --config ../../jest.config.cjs --testPathPatterns packages/contracts",
"typecheck": "tsc -p tsconfig.json --noEmit",
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
"prepublishOnly": "npm run build"
@@ -33,11 +34,11 @@
"directory": "src/packages/contracts"
},
"peerDependencies": {
- "zod": "^4.0.0"
+ "zod": "^4.4.0"
},
"devDependencies": {
- "tsup": "^8.5.0",
- "typescript": "^5.9.3",
- "zod": "^4.3.6"
+ "tsup": "^8.5.1",
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
}
}
diff --git a/src/packages/contracts/src/PagedRequest.ts b/src/packages/contracts/src/PagedRequest.ts
index 450b04b..62f4a5d 100644
--- a/src/packages/contracts/src/PagedRequest.ts
+++ b/src/packages/contracts/src/PagedRequest.ts
@@ -1,4 +1,4 @@
-import { boolean, number, object, record, string, type ZodType } from 'zod'
+import { boolean, intersection, number, object, record, string, type ZodType } from 'zod'
import type { RequestModelBase } from './RequestModelBase'
import { RequestModelBaseSchema } from './RequestModelBase'
@@ -11,7 +11,8 @@ export interface PagedRequest extends RequestModelBase {
isAscending?: boolean
}
-export const PagedRequestSchema: ZodType = RequestModelBaseSchema.and(
+export const PagedRequestSchema: ZodType = intersection(
+ RequestModelBaseSchema,
object({
pageSize: number().optional(),
pageNumber: number().optional(),
diff --git a/src/packages/contracts/src/PatchRequestModelBase.ts b/src/packages/contracts/src/PatchRequestModelBase.ts
index f88edee..0ebf0f5 100644
--- a/src/packages/contracts/src/PatchRequestModelBase.ts
+++ b/src/packages/contracts/src/PatchRequestModelBase.ts
@@ -1,4 +1,4 @@
-import z, { object, record, string, type ZodType } from 'zod'
+import z, { intersection, object, record, string, type ZodType } from 'zod'
import { PatchOperation } from './PatchOperation'
import { RequestModelBase, RequestModelBaseSchema } from './RequestModelBase'
@@ -8,7 +8,8 @@ export interface PatchRequestModelBase extends RequestModelBase {
[key: string]: unknown
}
-export const PatchRequestModelBaseSchema: ZodType = RequestModelBaseSchema.and(
+export const PatchRequestModelBaseSchema: ZodType = intersection(
+ RequestModelBaseSchema,
object({
operations: record(string(), z.enum(PatchOperation)).optional(),
})
diff --git a/src/packages/contracts/src/identity/login/LoginRequest.ts b/src/packages/contracts/src/identity/login/LoginRequest.ts
index 6bdfaaf..5f3a467 100644
--- a/src/packages/contracts/src/identity/login/LoginRequest.ts
+++ b/src/packages/contracts/src/identity/login/LoginRequest.ts
@@ -1,4 +1,4 @@
-import { object, RefinementCtx, string, ZodIssueCode, type ZodType } from 'zod'
+import { object, RefinementCtx, string, type ZodType } from 'zod'
export interface LoginRequest {
username: string
@@ -10,7 +10,7 @@ export interface LoginRequest {
const loginRequestSchemaRefine = (data: LoginRequest, ctx: RefinementCtx) => {
if (data.username === '') {
ctx.addIssue({
- code: ZodIssueCode.custom,
+ code: 'custom',
message: 'Username cannot be empty',
path: ['username'],
})
@@ -18,7 +18,7 @@ const loginRequestSchemaRefine = (data: LoginRequest, ctx: RefinementCtx) => {
if (data.password === '') {
ctx.addIssue({
- code: ZodIssueCode.custom,
+ code: 'custom',
message: 'Password cannot be empty',
path: ['password'],
})
@@ -26,7 +26,7 @@ const loginRequestSchemaRefine = (data: LoginRequest, ctx: RefinementCtx) => {
if (data.twoFactorCode && data.twoFactorRecoveryCode) {
ctx.addIssue({
- code: ZodIssueCode.custom,
+ code: 'custom',
message: 'Cannot have both twoFactorCode and twoFactorRecoveryCode',
path: ['twoFactorCode', 'twoFactorRecoveryCode'],
})
diff --git a/src/packages/contracts/src/schemas.test.ts b/src/packages/contracts/src/schemas.test.ts
new file mode 100644
index 0000000..2d87b77
--- /dev/null
+++ b/src/packages/contracts/src/schemas.test.ts
@@ -0,0 +1,73 @@
+import { PatchOperation } from './PatchOperation'
+import { LoginRequestSchema } from './identity/login/LoginRequest'
+import { PagedRequestSchema } from './PagedRequest'
+import { PatchRequestModelBaseSchema } from './PatchRequestModelBase'
+import { RefreshTokenRequestSchema } from './identity/login/RefreshTokenRequest'
+
+describe('LoginRequestSchema', () => {
+ it('accepts valid credentials', () => {
+ const result = LoginRequestSchema.safeParse({
+ username: 'alice',
+ password: 'secret',
+ })
+
+ expect(result.success).toBe(true)
+ })
+
+ it('rejects empty username and password', () => {
+ const result = LoginRequestSchema.safeParse({
+ username: '',
+ password: '',
+ })
+
+ expect(result.success).toBe(false)
+ if (!result.success) {
+ expect(result.error.issues.map((issue) => issue.message)).toEqual(
+ expect.arrayContaining(['Username cannot be empty', 'Password cannot be empty'])
+ )
+ }
+ })
+
+ it('rejects both two-factor fields together', () => {
+ const result = LoginRequestSchema.safeParse({
+ username: 'alice',
+ password: 'secret',
+ twoFactorCode: '123456',
+ twoFactorRecoveryCode: 'recovery',
+ })
+
+ expect(result.success).toBe(false)
+ })
+})
+
+describe('PagedRequestSchema', () => {
+ it('accepts optional paging fields', () => {
+ const result = PagedRequestSchema.safeParse({
+ pageNumber: 2,
+ pageSize: 25,
+ sortBy: 'name',
+ isAscending: true,
+ })
+
+ expect(result.success).toBe(true)
+ })
+})
+
+describe('PatchRequestModelBaseSchema', () => {
+ it('accepts patch operations keyed by field name', () => {
+ const result = PatchRequestModelBaseSchema.safeParse({
+ operations: {
+ name: PatchOperation.SetField,
+ },
+ })
+
+ expect(result.success).toBe(true)
+ })
+})
+
+describe('RefreshTokenRequestSchema', () => {
+ it('requires a non-empty refresh token', () => {
+ expect(RefreshTokenRequestSchema.safeParse({ refreshToken: 'token' }).success).toBe(true)
+ expect(RefreshTokenRequestSchema.safeParse({ refreshToken: '' }).success).toBe(false)
+ })
+})
diff --git a/src/packages/contracts/tsconfig.build.json b/src/packages/contracts/tsconfig.build.json
new file mode 100644
index 0000000..35bcdb2
--- /dev/null
+++ b/src/packages/contracts/tsconfig.build.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "ignoreDeprecations": "6.0"
+ }
+}
diff --git a/src/packages/contracts/tsconfig.json b/src/packages/contracts/tsconfig.json
index 5285d28..90d6d34 100644
--- a/src/packages/contracts/tsconfig.json
+++ b/src/packages/contracts/tsconfig.json
@@ -4,5 +4,6 @@
"rootDir": "src",
"outDir": "dist"
},
- "include": ["src"]
+ "include": ["src"],
+ "exclude": ["src/**/*.test.ts"]
}
diff --git a/src/packages/core/README.md b/src/packages/core/README.md
new file mode 100644
index 0000000..fb83459
--- /dev/null
+++ b/src/packages/core/README.md
@@ -0,0 +1,60 @@
+# @maks-it.com/webui-core
+
+Shared utilities, HTTP helpers, and React hooks for MaksIT WebUI apps.
+
+Depends on `@maks-it.com/webui-contracts`. Install peer dependencies in the host app: `react`, `axios`, `zod`.
+
+## Install
+
+```bash
+npm install @maks-it.com/webui-core @maks-it.com/webui-contracts axios react zod
+```
+
+## Highlights
+
+| Module | Exports |
+|--------|---------|
+| Deep diff | `deepDelta`, `deltaHasOperations`, collection policies |
+| Deep utils | `deepCopy`, `deepEqual`, `deepMerge`, `deepPatternMatch` |
+| Forms | `useFormState`, `validateFormState`, `applyFormFieldChange`, `createFormFieldUpdater` |
+| DataTable | `mapPagedToDataTable`, `extractPropFilter`, `DataTablePageView` |
+| ACL | `parseAclEntry`, `parseAclEntries` |
+| HTTP | `createWebUiHttpClient`, auth interceptors, Problem Details helpers |
+| Enum / flags | `enumToArr`, `flagsToString`, `hasFlag`, `toggleFlag` |
+| Identity storage | `readIdentity`, `writeIdentity`, `removeIdentity` |
+
+## Example — form state
+
+```tsx
+import { z } from 'zod'
+import { useFormState } from '@maks-it.com/webui-core'
+
+const schema = z.object({ name: z.string().min(1) })
+
+function MyForm() {
+ const { formState, errors, formIsValid, handleInputChange } = useFormState({
+ initialState: { name: '' },
+ validationSchema: schema,
+ })
+ // ...
+}
+```
+
+## Example — PATCH delta
+
+```ts
+import { deepDelta, deltaHasOperations } from '@maks-it.com/webui-core'
+
+const delta = deepDelta(formState, backupState, { arrays: { items: { identityKey: 'id' } } })
+if (deltaHasOperations(delta)) {
+ await api.patch('/resource', delta)
+}
+```
+
+## Repository
+
+[github.com/MAKS-IT-COM/maksit-webui](https://github.com/MAKS-IT-COM/maksit-webui) — `src/packages/core`
+
+## License
+
+MIT
diff --git a/src/packages/core/package.json b/src/packages/core/package.json
index 42b20f4..447370e 100644
--- a/src/packages/core/package.json
+++ b/src/packages/core/package.json
@@ -1,6 +1,6 @@
{
- "name": "@maksit/webui-core",
- "version": "0.1.0",
+ "name": "@maks-it.com/webui-core",
+ "version": "0.2.0",
"description": "Shared utilities and hooks for MaksIT WebUI apps",
"type": "module",
"main": "./dist/index.cjs",
@@ -18,7 +18,8 @@
"README.md"
],
"scripts": {
- "build": "tsup src/index.ts --format esm,cjs --dts --clean",
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean --tsconfig tsconfig.build.json",
+ "test": "jest --config ../../jest.config.cjs",
"typecheck": "tsc -p tsconfig.json --noEmit",
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
"prepublishOnly": "npm run build"
@@ -33,20 +34,20 @@
"directory": "src/packages/core"
},
"dependencies": {
- "@maksit/webui-contracts": "^0.1.0",
- "date-fns": "^4.1.0"
+ "@maks-it.com/webui-contracts": "^0.2.0",
+ "date-fns": "^4.3.0"
},
"peerDependencies": {
- "axios": "^1.7.0",
+ "axios": "^1.16.0",
"react": "^18.0.0 || ^19.0.0",
- "zod": "^4.0.0"
+ "zod": "^4.4.0"
},
"devDependencies": {
- "@types/react": "^19.2.14",
- "axios": "^1.13.2",
- "react": "^19.2.4",
- "tsup": "^8.5.0",
- "typescript": "^5.9.3",
- "zod": "^4.3.6"
+ "@types/react": "^19.2.15",
+ "axios": "^1.16.1",
+ "react": "^19.2.6",
+ "tsup": "^8.5.1",
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
}
}
diff --git a/src/packages/core/src/functions/acl/parseAclEntry.test.ts b/src/packages/core/src/functions/acl/parseAclEntry.test.ts
new file mode 100644
index 0000000..475e4a8
--- /dev/null
+++ b/src/packages/core/src/functions/acl/parseAclEntry.test.ts
@@ -0,0 +1,31 @@
+import { ScopePermission } from '../../types/ScopePermissions'
+import { parseAclEntry, parseAclEntries } from './parseAclEntry'
+
+const entityTypeMap = { O: 1, V: 2 } as const
+
+describe('parseAclEntry', () => {
+ it('parses a valid ACL entry', () => {
+ expect(parseAclEntry('O:entity-123:3', entityTypeMap)).toEqual({
+ entityType: 1,
+ entityId: 'entity-123',
+ scope: ScopePermission.Read | ScopePermission.Write,
+ })
+ })
+
+ it('returns null for malformed entries', () => {
+ expect(parseAclEntry('invalid', entityTypeMap)).toBeNull()
+ expect(parseAclEntry('X:entity:1', entityTypeMap)).toBeNull()
+ expect(parseAclEntry(null as unknown as string, entityTypeMap)).toBeNull()
+ })
+})
+
+describe('parseAclEntries', () => {
+ it('keeps only valid entries in order', () => {
+ expect(
+ parseAclEntries(['O:a:1', 'bad', 'V:b:2'], entityTypeMap)
+ ).toEqual([
+ { entityType: 1, entityId: 'a', scope: ScopePermission.Read },
+ { entityType: 2, entityId: 'b', scope: ScopePermission.Write },
+ ])
+ })
+})
diff --git a/src/packages/core/src/functions/dataTable/dataTableFilters.test.ts b/src/packages/core/src/functions/dataTable/dataTableFilters.test.ts
new file mode 100644
index 0000000..ec03d09
--- /dev/null
+++ b/src/packages/core/src/functions/dataTable/dataTableFilters.test.ts
@@ -0,0 +1,22 @@
+import { extractPropFilter } from './dataTableFilters'
+
+describe('extractPropFilter', () => {
+ it('extracts Contains filter values', () => {
+ expect(extractPropFilter('CommonName.Contains("example")', 'CommonName')).toBe('example')
+ })
+
+ it('extracts StartsWith and EndsWith filter values', () => {
+ expect(extractPropFilter('Host.StartsWith("api")', 'Host')).toBe('api')
+ expect(extractPropFilter('Host.EndsWith(".com")', 'Host')).toBe('.com')
+ })
+
+ it('is case-insensitive for property and operator names', () => {
+ expect(extractPropFilter('commonname.contains("test")', 'CommonName')).toBe('test')
+ })
+
+ it('returns undefined for empty or non-matching filters', () => {
+ expect(extractPropFilter(undefined, 'CommonName')).toBeUndefined()
+ expect(extractPropFilter(' ', 'CommonName')).toBeUndefined()
+ expect(extractPropFilter('OtherField.Contains("x")', 'CommonName')).toBeUndefined()
+ })
+})
diff --git a/src/packages/core/src/functions/dataTable/dataTablePaged.test.ts b/src/packages/core/src/functions/dataTable/dataTablePaged.test.ts
new file mode 100644
index 0000000..22773a8
--- /dev/null
+++ b/src/packages/core/src/functions/dataTable/dataTablePaged.test.ts
@@ -0,0 +1,51 @@
+import type { PagedResponse } from '@maks-it.com/webui-contracts'
+import { mapPagedToDataTable } from './dataTablePaged'
+
+describe('mapPagedToDataTable', () => {
+ it('returns an empty page for missing responses', () => {
+ expect(mapPagedToDataTable(undefined)).toEqual({
+ items: [],
+ pageNumber: 1,
+ pageSize: 0,
+ totalCount: 0,
+ totalPages: 1,
+ hasPreviousPage: false,
+ hasNextPage: false,
+ })
+ })
+
+ it('maps paged response fields with defaults', () => {
+ expect(
+ mapPagedToDataTable({
+ items: [{ id: '1' }],
+ pageNumber: 2,
+ pageSize: 25,
+ totalCount: 100,
+ totalPages: 4,
+ hasPreviousPage: true,
+ hasNextPage: true,
+ })
+ ).toEqual({
+ items: [{ id: '1' }],
+ pageNumber: 2,
+ pageSize: 25,
+ totalCount: 100,
+ totalPages: 4,
+ hasPreviousPage: true,
+ hasNextPage: true,
+ })
+ })
+
+ it('fills in defaults for partial responses', () => {
+ const partial = { items: [] } as PagedResponse
+ expect(mapPagedToDataTable(partial)).toEqual({
+ items: [],
+ pageNumber: 1,
+ pageSize: 0,
+ totalCount: 0,
+ totalPages: 1,
+ hasPreviousPage: false,
+ hasNextPage: false,
+ })
+ })
+})
diff --git a/src/packages/core/src/functions/dataTable/dataTablePaged.ts b/src/packages/core/src/functions/dataTable/dataTablePaged.ts
index 18b1139..11282f1 100644
--- a/src/packages/core/src/functions/dataTable/dataTablePaged.ts
+++ b/src/packages/core/src/functions/dataTable/dataTablePaged.ts
@@ -1,4 +1,4 @@
-import type { PagedResponse } from '@maksit/webui-contracts'
+import type { PagedResponse } from '@maks-it.com/webui-contracts'
/**
* Virtualized DataTable view model used by client paging and search helpers.
diff --git a/src/packages/core/src/functions/date/isValidDateString.test.ts b/src/packages/core/src/functions/date/isValidDateString.test.ts
new file mode 100644
index 0000000..adc88e9
--- /dev/null
+++ b/src/packages/core/src/functions/date/isValidDateString.test.ts
@@ -0,0 +1,14 @@
+import { isValidISODateString } from './isValidDateString'
+
+describe('isValidISODateString', () => {
+ it('accepts valid ISO date strings', () => {
+ expect(isValidISODateString('2024-01-15')).toBe(true)
+ expect(isValidISODateString('2024-01-15T10:30:00Z')).toBe(true)
+ })
+
+ it('rejects empty or invalid strings', () => {
+ expect(isValidISODateString('')).toBe(false)
+ expect(isValidISODateString('not-a-date')).toBe(false)
+ expect(isValidISODateString('2024-13-40')).toBe(false)
+ })
+})
diff --git a/src/packages/core/src/functions/deep/deepDelta.test.ts b/src/packages/core/src/functions/deep/deepDelta.test.ts
new file mode 100644
index 0000000..3526e7c
--- /dev/null
+++ b/src/packages/core/src/functions/deep/deepDelta.test.ts
@@ -0,0 +1,114 @@
+import { COLLECTION_ITEM_OPERATION, PatchOperation } from '@maks-it.com/webui-contracts'
+import { deepDelta, deltaHasOperations } from './deepDelta'
+
+describe('deepDelta', () => {
+ it('detects primitive field changes', () => {
+ const backup = { name: 'old', count: 1 }
+ const form = { name: 'new', count: 1 }
+
+ const delta = deepDelta(form, backup)
+
+ expect(delta.name).toBe('new')
+ expect(delta.operations?.name).toBe(PatchOperation.SetField)
+ expect(delta.count).toBeUndefined()
+ })
+
+ it('marks nullish values as RemoveField', () => {
+ const backup = { name: 'value', optional: 'present' }
+ const form = { name: 'value', optional: null }
+
+ const delta = deepDelta(form, backup)
+
+ expect(delta.optional).toBeUndefined()
+ expect(delta.operations?.optional).toBe(PatchOperation.RemoveField)
+ })
+
+ it('replaces primitive arrays when values differ', () => {
+ const backup = { tags: ['a', 'b'] }
+ const form = { tags: ['a', 'b', 'c'] }
+
+ const delta = deepDelta(form, backup)
+
+ expect(delta.tags).toEqual(['a', 'b', 'c'])
+ expect(delta.operations?.tags).toBe(PatchOperation.SetField)
+ })
+
+ it('skips unchanged primitive arrays', () => {
+ const backup = { tags: ['a', 'b'] }
+ const form = { tags: ['b', 'a'] }
+
+ const delta = deepDelta({ tags: ['a', 'b'] }, { tags: ['a', 'b'] })
+
+ expect(delta.tags).toBeUndefined()
+ expect(delta.operations?.tags).toBeUndefined()
+ })
+
+ it('diffs identifiable object arrays by id', () => {
+ const backup = {
+ items: [{ id: '1', name: 'first' }, { id: '2', name: 'second' }],
+ }
+ const form = {
+ items: [{ id: '1', name: 'updated' }, { id: '3', name: 'new' }],
+ }
+
+ const delta = deepDelta(form, backup)
+
+ expect(delta.items).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({ id: '1', name: 'updated' }),
+ expect.objectContaining({
+ id: '3',
+ name: 'new',
+ operations: { [COLLECTION_ITEM_OPERATION]: PatchOperation.AddToCollection },
+ }),
+ expect.objectContaining({
+ id: '2',
+ operations: { [COLLECTION_ITEM_OPERATION]: PatchOperation.RemoveFromCollection },
+ }),
+ ])
+ )
+ })
+
+ it('uses identityKey when items have no id', () => {
+ const backup = { hostnames: [{ hostname: 'a.example.com', enabled: true }] }
+ const form = { hostnames: [{ hostname: 'a.example.com', enabled: false }] }
+
+ const delta = deepDelta(form, backup, {
+ arrays: { hostnames: { identityKey: 'hostname', idFieldKey: 'hostname' } },
+ })
+
+ expect(delta.hostnames).toEqual([
+ expect.objectContaining({
+ hostname: 'a.example.com',
+ enabled: false,
+ operations: expect.objectContaining({ enabled: PatchOperation.SetField }),
+ }),
+ ])
+ })
+})
+
+describe('deltaHasOperations', () => {
+ it('returns false for empty delta', () => {
+ expect(deltaHasOperations({})).toBe(false)
+ })
+
+ it('returns true when top-level operations exist', () => {
+ expect(deltaHasOperations({ operations: { name: PatchOperation.SetField } })).toBe(true)
+ })
+
+ it('returns true for nested object operations', () => {
+ expect(
+ deltaHasOperations({
+ nested: { operations: { field: PatchOperation.SetField } },
+ })
+ ).toBe(true)
+ })
+
+ it('returns true for array item operations', () => {
+ expect(
+ deltaHasOperations({
+ items: [{ operations: { [COLLECTION_ITEM_OPERATION]: PatchOperation.AddToCollection } }],
+ })
+ ).toBe(true)
+ })
+})
diff --git a/src/packages/core/src/functions/deep/deepDelta.ts b/src/packages/core/src/functions/deep/deepDelta.ts
index 54d9e71..1cd2c30 100644
--- a/src/packages/core/src/functions/deep/deepDelta.ts
+++ b/src/packages/core/src/functions/deep/deepDelta.ts
@@ -1,4 +1,4 @@
-import { COLLECTION_ITEM_OPERATION, PatchOperation } from '@maksit/webui-contracts'
+import { COLLECTION_ITEM_OPERATION, PatchOperation } from '@maks-it.com/webui-contracts'
import { deepCopy } from './deepCopy'
import { deepEqual } from './deepEqual'
diff --git a/src/packages/core/src/functions/deep/deepEqual.test.ts b/src/packages/core/src/functions/deep/deepEqual.test.ts
new file mode 100644
index 0000000..b5f2845
--- /dev/null
+++ b/src/packages/core/src/functions/deep/deepEqual.test.ts
@@ -0,0 +1,39 @@
+import { deepEqual, deepEqualArrays } from './deepEqual'
+
+describe('deepEqual', () => {
+ it('returns true for identical primitives', () => {
+ expect(deepEqual(1, 1)).toBe(true)
+ expect(deepEqual('a', 'a')).toBe(true)
+ expect(deepEqual(null, null)).toBe(true)
+ })
+
+ it('returns false for different primitives', () => {
+ expect(deepEqual(1, 2)).toBe(false)
+ expect(deepEqual('a', 'b')).toBe(false)
+ expect(deepEqual(null, undefined)).toBe(false)
+ })
+
+ it('compares nested objects by value', () => {
+ expect(deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } })).toBe(true)
+ expect(deepEqual({ a: 1 }, { a: 2 })).toBe(false)
+ expect(deepEqual({ a: 1 }, { a: 1, b: 2 })).toBe(false)
+ })
+
+ it('compares arrays regardless of element order', () => {
+ expect(deepEqual([1, 2, 3], [3, 2, 1])).toBe(true)
+ expect(deepEqual([{ id: 1 }, { id: 2 }], [{ id: 2 }, { id: 1 }])).toBe(true)
+ expect(deepEqual([1, 2], [1, 2, 3])).toBe(false)
+ })
+})
+
+describe('deepEqualArrays', () => {
+ it('returns true for empty arrays', () => {
+ expect(deepEqualArrays([], [])).toBe(true)
+ })
+
+ it('matches multiset equality', () => {
+ expect(deepEqualArrays(['a', 'b'], ['b', 'a'])).toBe(true)
+ expect(deepEqualArrays([1, 1, 2], [2, 1, 1])).toBe(true)
+ expect(deepEqualArrays([1, 2], [1, 3])).toBe(false)
+ })
+})
diff --git a/src/packages/core/src/functions/enum/flags.test.ts b/src/packages/core/src/functions/enum/flags.test.ts
new file mode 100644
index 0000000..d64f147
--- /dev/null
+++ b/src/packages/core/src/functions/enum/flags.test.ts
@@ -0,0 +1,32 @@
+import { hasAnyFlag } from './hasAnyFlag'
+import { hasFlag } from './hasFlag'
+import { toggleFlag } from './toggleFlag'
+
+describe('hasFlag', () => {
+ it('returns true when all flag bits are set', () => {
+ expect(hasFlag(0b101, 0b001)).toBe(true)
+ expect(hasFlag(0b111, 0b101)).toBe(true)
+ })
+
+ it('returns false when any flag bit is missing', () => {
+ expect(hasFlag(0b100, 0b101)).toBe(false)
+ expect(hasFlag(0, 0b001)).toBe(false)
+ })
+})
+
+describe('hasAnyFlag', () => {
+ it('returns true when any overlapping bit is set', () => {
+ expect(hasAnyFlag(0b100, 0b101)).toBe(true)
+ expect(hasAnyFlag(0b010, 0b001)).toBe(false)
+ })
+})
+
+describe('toggleFlag', () => {
+ it('adds the flag when not fully set', () => {
+ expect(toggleFlag(0b100, 0b001)).toBe(0b101)
+ })
+
+ it('removes the flag when fully set', () => {
+ expect(toggleFlag(0b101, 0b001)).toBe(0b100)
+ })
+})
diff --git a/src/packages/core/src/functions/guid/isGuid.test.ts b/src/packages/core/src/functions/guid/isGuid.test.ts
new file mode 100644
index 0000000..812464c
--- /dev/null
+++ b/src/packages/core/src/functions/guid/isGuid.test.ts
@@ -0,0 +1,15 @@
+import { isGuid } from './isGuid'
+
+describe('isGuid', () => {
+ it('accepts valid GUIDs', () => {
+ expect(isGuid('550e8400-e29b-41d4-a716-446655440000')).toBe(true)
+ expect(isGuid('550E8400-E29B-41D4-A716-446655440000')).toBe(true)
+ })
+
+ it('rejects invalid GUIDs', () => {
+ expect(isGuid('')).toBe(false)
+ expect(isGuid('not-a-guid')).toBe(false)
+ expect(isGuid('550e8400-e29b-41d4-a716')).toBe(false)
+ expect(isGuid('550e8400e29b41d4a716446655440000')).toBe(false)
+ })
+})
diff --git a/src/packages/core/src/functions/headers/extractFilenameFromHeaders.test.ts b/src/packages/core/src/functions/headers/extractFilenameFromHeaders.test.ts
new file mode 100644
index 0000000..3b2def5
--- /dev/null
+++ b/src/packages/core/src/functions/headers/extractFilenameFromHeaders.test.ts
@@ -0,0 +1,31 @@
+import { extractFilenameFromHeaders } from './extractFilenameFromHeaders'
+
+describe('extractFilenameFromHeaders', () => {
+ it('returns fallback when header is missing', () => {
+ expect(extractFilenameFromHeaders({}, 'default.bin')).toBe('default.bin')
+ })
+
+ it('parses RFC 5987 encoded filenames', () => {
+ expect(
+ extractFilenameFromHeaders({
+ 'content-disposition': "attachment; filename*=UTF-8''report%20file.pdf",
+ })
+ ).toBe('report file.pdf')
+ })
+
+ it('parses quoted filenames', () => {
+ expect(
+ extractFilenameFromHeaders({
+ 'content-disposition': 'attachment; filename="archive.zip"',
+ })
+ ).toBe('archive.zip')
+ })
+
+ it('parses plain filenames', () => {
+ expect(
+ extractFilenameFromHeaders({
+ 'content-disposition': 'attachment; filename=download.bin',
+ })
+ ).toBe('download.bin')
+ })
+})
diff --git a/src/packages/core/src/functions/zod/validateFormState.test.ts b/src/packages/core/src/functions/zod/validateFormState.test.ts
new file mode 100644
index 0000000..2eb8e71
--- /dev/null
+++ b/src/packages/core/src/functions/zod/validateFormState.test.ts
@@ -0,0 +1,24 @@
+import { z } from 'zod'
+import { validateFormState } from './validateFormState'
+
+describe('validateFormState', () => {
+ const schema = z.object({
+ name: z.string().min(1, 'Name is required'),
+ age: z.number().min(0),
+ })
+
+ it('returns valid result for passing form state', () => {
+ const result = validateFormState({ name: 'Alice', age: 30 }, schema)
+
+ expect(result.formIsValid).toBe(true)
+ expect(result.errors).toEqual({ name: '', age: '' })
+ })
+
+ it('returns field errors for invalid form state', () => {
+ const result = validateFormState({ name: '', age: -1 }, schema)
+
+ expect(result.formIsValid).toBe(false)
+ expect(result.errors.name).toBe('Name is required')
+ expect(result.errors.age).toBeTruthy()
+ })
+})
diff --git a/src/packages/core/src/http/errorHandler.ts b/src/packages/core/src/http/errorHandler.ts
index 4489750..1cb3535 100644
--- a/src/packages/core/src/http/errorHandler.ts
+++ b/src/packages/core/src/http/errorHandler.ts
@@ -1,5 +1,5 @@
import type { AxiosError } from 'axios'
-import type { ProblemDetails } from '@maksit/webui-contracts'
+import type { ProblemDetails } from '@maks-it.com/webui-contracts'
import { formatProblemDetailsMessage } from './problemDetails'
/** Shows toast(s) for problem+json and 401 responses. */
diff --git a/src/packages/core/src/http/problemDetails.test.ts b/src/packages/core/src/http/problemDetails.test.ts
new file mode 100644
index 0000000..e97ccbb
--- /dev/null
+++ b/src/packages/core/src/http/problemDetails.test.ts
@@ -0,0 +1,26 @@
+import { formatProblemDetailsMessage } from './problemDetails'
+
+describe('formatProblemDetailsMessage', () => {
+ it('combines detail and field errors', () => {
+ const message = formatProblemDetailsMessage({
+ title: 'Validation failed',
+ detail: 'One or more fields are invalid.',
+ errors: {
+ name: ['Name is required'],
+ email: ['Invalid email', 'Email is too long'],
+ },
+ })
+
+ expect(message).toBe(
+ 'One or more fields are invalid. name: Name is required; email: Invalid email; email: Email is too long'
+ )
+ })
+
+ it('falls back to title when detail and errors are empty', () => {
+ expect(formatProblemDetailsMessage({ title: 'Bad Request' })).toBe('Bad Request')
+ })
+
+ it('uses a generic message when nothing else is available', () => {
+ expect(formatProblemDetailsMessage({})).toBe('Request failed')
+ })
+})
diff --git a/src/packages/core/src/http/problemDetails.ts b/src/packages/core/src/http/problemDetails.ts
index 19f8ae6..64ca814 100644
--- a/src/packages/core/src/http/problemDetails.ts
+++ b/src/packages/core/src/http/problemDetails.ts
@@ -1,4 +1,4 @@
-import type { ProblemDetails } from '@maksit/webui-contracts'
+import type { ProblemDetails } from '@maks-it.com/webui-contracts'
/** Builds a user-facing message from RFC 7807 problem details. */
export function formatProblemDetailsMessage(problem: ProblemDetails): string {
diff --git a/src/packages/core/src/localStorage/identity.ts b/src/packages/core/src/localStorage/identity.ts
index d7684cb..23dd882 100644
--- a/src/packages/core/src/localStorage/identity.ts
+++ b/src/packages/core/src/localStorage/identity.ts
@@ -1,4 +1,4 @@
-import type { LoginResponse } from '@maksit/webui-contracts'
+import type { LoginResponse } from '@maks-it.com/webui-contracts'
const readIdentity = () => {
const json = localStorage.getItem('identity')
diff --git a/src/packages/core/src/types/index.ts b/src/packages/core/src/types/index.ts
index 81612f3..c298191 100644
--- a/src/packages/core/src/types/index.ts
+++ b/src/packages/core/src/types/index.ts
@@ -3,5 +3,5 @@ export type {
FormValidationResult,
FormValidationSchema,
} from './FormValidationSchema'
-export type { PagedResponse } from '@maksit/webui-contracts'
+export type { PagedResponse } from '@maks-it.com/webui-contracts'
export { ScopePermission } from './ScopePermissions'
diff --git a/src/packages/core/tsconfig.build.json b/src/packages/core/tsconfig.build.json
new file mode 100644
index 0000000..35bcdb2
--- /dev/null
+++ b/src/packages/core/tsconfig.build.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "ignoreDeprecations": "6.0"
+ }
+}
diff --git a/src/packages/core/tsconfig.json b/src/packages/core/tsconfig.json
index 5285d28..90d6d34 100644
--- a/src/packages/core/tsconfig.json
+++ b/src/packages/core/tsconfig.json
@@ -4,5 +4,6 @@
"rootDir": "src",
"outDir": "dist"
},
- "include": ["src"]
+ "include": ["src"],
+ "exclude": ["src/**/*.test.ts"]
}
diff --git a/src/tsconfig.jest.json b/src/tsconfig.jest.json
new file mode 100644
index 0000000..64e8225
--- /dev/null
+++ b/src/tsconfig.jest.json
@@ -0,0 +1,11 @@
+{
+ "extends": "./tsconfig.base.json",
+ "compilerOptions": {
+ "noEmit": true,
+ "types": ["jest", "node"]
+ },
+ "include": [
+ "packages/core/src/**/*.ts",
+ "packages/contracts/src/**/*.ts"
+ ]
+}
diff --git a/utils/Generate-CoverageBadges/Generate-CoverageBadges.ps1 b/utils/Generate-CoverageBadges/Generate-CoverageBadges.ps1
index b862100..dff68c1 100644
--- a/utils/Generate-CoverageBadges/Generate-CoverageBadges.ps1
+++ b/utils/Generate-CoverageBadges/Generate-CoverageBadges.ps1
@@ -3,263 +3,20 @@
<#
.SYNOPSIS
- Generates SVG coverage badges for README.
+ Legacy entry point — forwards to the Run-Tests plugin engine.
.DESCRIPTION
- This script runs unit tests via TestRunner.psm1, then generates shields.io-style
- SVG badges for line, branch, and method coverage.
-
- Configuration is stored in scriptsettings.json:
- - openReport : Generate and open full HTML report (true/false)
- - paths.testProjects : Array of relative paths to test projects (preferred)
- - paths.testProject : Single test project path (legacy; use testProjects)
- - paths.badgesDir : Relative path to badges output directory
- - badges : Array of badges to generate (name, label, metric)
- - colorThresholds : Coverage percentages for badge colors
-
- Badge colors based on coverage:
- - brightgreen (>=80%), green (>=60%), yellowgreen (>=40%)
- - yellow (>=20%), orange (>=10%), red (<10%)
- If openReport is true, ReportGenerator is required:
- dotnet tool install -g dotnet-reportgenerator-globaltool
-
-.EXAMPLE
- pwsh -File .\Generate-CoverageBadges.ps1
- Runs tests and generates coverage badges (and optionally HTML report if configured).
-
-.OUTPUTS
- SVG badge files in the configured badges directory.
-
-.NOTES
- Author: MaksIT
- Requires: .NET SDK, Coverlet (included in test project)
+ Generate-CoverageBadges.ps1 is kept for backward compatibility.
+ Configure plugins in src/Run-Tests/scriptsettings.json.
#>
$ErrorActionPreference = "Stop"
-# Get the directory of the current script (for loading settings and relative paths)
-$ScriptDir = $PSScriptRoot
-$UtilsDir = Split-Path $ScriptDir -Parent
-
-#region Import Modules
-
-# Import TestRunner module (executes tests and collects coverage metrics)
-$testRunnerModulePath = Join-Path $UtilsDir "TestRunner.psm1"
-if (-not (Test-Path $testRunnerModulePath)) {
- Write-Error "TestRunner module not found at: $testRunnerModulePath"
- exit 1
-}
-Import-Module $testRunnerModulePath -Force
-
-# Import shared ScriptConfig module (settings + command validation helpers)
-$scriptConfigModulePath = Join-Path $UtilsDir "ScriptConfig.psm1"
-if (-not (Test-Path $scriptConfigModulePath)) {
- Write-Error "ScriptConfig module not found at: $scriptConfigModulePath"
- exit 1
-}
-Import-Module $scriptConfigModulePath -Force
-
-# Import shared Logging module (timestamped/aligned output)
-$loggingModulePath = Join-Path $UtilsDir "Logging.psm1"
-if (-not (Test-Path $loggingModulePath)) {
- Write-Error "Logging module not found at: $loggingModulePath"
- exit 1
-}
-Import-Module $loggingModulePath -Force
-
-#endregion
-
-#region Load Settings
-
-$Settings = Get-ScriptSettings -ScriptDir $ScriptDir
-
-$thresholds = $Settings.colorThresholds
-
-#endregion
-
-#region Configuration
-
-# Runtime options from settings
-$OpenReport = if ($null -ne $Settings.openReport) { [bool]$Settings.openReport } else { $false }
-
-# Resolve configured paths to absolute paths (one or more test projects)
-$testProjectPaths = [System.Collections.Generic.List[string]]::new()
-$pathsNode = $Settings.paths
-if ($pathsNode.PSObject.Properties.Name -contains 'testProjects' -and $pathsNode.testProjects) {
- foreach ($rel in @($pathsNode.testProjects)) {
- if ([string]::IsNullOrWhiteSpace([string]$rel)) { continue }
- $testProjectPaths.Add([System.IO.Path]::GetFullPath((Join-Path $ScriptDir $rel.Trim())))
- }
-}
-if ($testProjectPaths.Count -eq 0 -and $pathsNode.testProject) {
- $testProjectPaths.Add([System.IO.Path]::GetFullPath((Join-Path $ScriptDir $pathsNode.testProject)))
-}
-if ($testProjectPaths.Count -eq 0) {
- Write-Error "Configure paths.testProjects (array of relative paths) or paths.testProject (single path) in scriptsettings.json."
+$runTestsScript = Join-Path (Split-Path $PSScriptRoot -Parent) "Run-Tests\Run-Tests.ps1"
+if (-not (Test-Path $runTestsScript -PathType Leaf)) {
+ Write-Error "Run-Tests engine not found at: $runTestsScript"
exit 1
}
-$BadgesDir = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir $Settings.paths.badgesDir))
-
-# Ensure badges directory exists
-if (-not (Test-Path $BadgesDir)) {
- New-Item -ItemType Directory -Path $BadgesDir | Out-Null
-}
-
-#endregion
-
-#region Helpers
-
-# Maps a coverage percentage to a shields.io color using configured thresholds.
-function Get-BadgeColor {
- param([double]$percentage)
-
- if ($percentage -ge $thresholds.brightgreen) { return "brightgreen" }
- if ($percentage -ge $thresholds.green) { return "green" }
- if ($percentage -ge $thresholds.yellowgreen) { return "yellowgreen" }
- if ($percentage -ge $thresholds.yellow) { return "yellow" }
- if ($percentage -ge $thresholds.orange) { return "orange" }
- return "red"
-}
-
-# Builds a shields.io-like SVG badge string for one metric.
-function New-Badge {
- param(
- [string]$label,
- [string]$value,
- [string]$color
- )
-
- # Calculate widths (approximate character width of 6.5px for the font)
- $labelWidth = [math]::Max(($label.Length * 6.5) + 10, 50)
- $valueWidth = [math]::Max(($value.Length * 6.5) + 10, 40)
- $totalWidth = $labelWidth + $valueWidth
- $labelX = $labelWidth / 2
- $valueX = $labelWidth + ($valueWidth / 2)
-
- $colorMap = @{
- "brightgreen" = "#4c1"
- "green" = "#97ca00"
- "yellowgreen" = "#a4a61d"
- "yellow" = "#dfb317"
- "orange" = "#fe7d37"
- "red" = "#e05d44"
- }
- $hexColor = $colorMap[$color]
- if (-not $hexColor) { $hexColor = "#9f9f9f" }
-
- return @"
-
-"@
-}
-
-#endregion
-
-#region Main
-
-#region Test And Coverage
-
-$invokeCoverageParams = @{
- TestProjectPath = @($testProjectPaths)
- KeepResults = $OpenReport
-}
-# Keep single-project results next to that project; for several projects use a shared folder under this script.
-if ($testProjectPaths.Count -gt 1) {
- $invokeCoverageParams.ResultsDirectory = Join-Path $ScriptDir "TestResults"
-}
-$coverage = Invoke-TestsWithCoverage @invokeCoverageParams
-if (-not $coverage.Success) {
- Write-Error "Tests failed: $($coverage.Error)"
- exit 1
-}
-
-Write-Log -Level "OK" -Message "Tests passed!"
-
-$metrics = @{
- "line" = $coverage.LineRate
- "branch" = $coverage.BranchRate
- "method" = $coverage.MethodRate
-}
-
-#endregion
-
-#region Generate Badges
-
-Write-LogStep -Message "Generating coverage badges..."
-
-foreach ($badge in $Settings.badges) {
- $metricValue = $metrics[$badge.metric]
- $color = Get-BadgeColor $metricValue
- $svg = New-Badge -label $badge.label -value "$metricValue%" -color $color
- $path = Join-Path $BadgesDir $badge.name
- $svg | Out-File -FilePath $path -Encoding utf8NoBOM
- Write-Log -Level "OK" -Message "$($badge.name): $($badge.label) = $metricValue%"
-}
-
-#endregion
-
-#region Summary
-
-Write-Log -Level "INFO" -Message "Coverage Summary:"
-Write-Log -Level "INFO" -Message "Line Coverage: $($coverage.LineRate)%"
-Write-Log -Level "INFO" -Message "Branch Coverage: $($coverage.BranchRate)%"
-Write-Log -Level "INFO" -Message "Method Coverage: $($coverage.MethodRate)% ($($coverage.CoveredMethods) of $($coverage.TotalMethods) methods)"
-Write-Log -Level "OK" -Message "Badges generated in: $BadgesDir"
-Write-Log -Level "STEP" -Message "Commit the badges/ folder to update README."
-
-#endregion
-
-#region Optional Html Report
-
-if ($OpenReport -and $coverage.CoverageFile) {
- Write-LogStep -Message "Generating HTML report..."
- Assert-Command reportgenerator
-
- # Cobertura file(s): single path, or semicolon-separated list from merged runs.
- $firstCobertura = if ($coverage.CoverageFiles -and $coverage.CoverageFiles.Count -gt 0) {
- $coverage.CoverageFiles[0]
- }
- else {
- ($coverage.CoverageFile -split ';')[0].Trim()
- }
- $ResultsDir = Split-Path (Split-Path $firstCobertura -Parent) -Parent
- $ReportDir = Join-Path $ResultsDir "report"
-
- $reportGenArgs = @(
- "-reports:$($coverage.CoverageFile)"
- "-targetdir:$ReportDir"
- "-reporttypes:Html"
- )
- & reportgenerator @reportGenArgs
-
- $IndexFile = Join-Path $ReportDir "index.html"
- if (Test-Path $IndexFile) {
- Start-Process $IndexFile
- }
-
- Write-Log -Level "INFO" -Message "TestResults kept for HTML report viewing."
-}
-
-#endregion
-
-#endregion
+& pwsh -NoProfile -ExecutionPolicy Bypass -File $runTestsScript
+exit $LASTEXITCODE
diff --git a/utils/Generate-CoverageBadges/scriptsettings.json b/utils/Generate-CoverageBadges/scriptsettings.json
index c48c539..a3169f5 100644
--- a/utils/Generate-CoverageBadges/scriptsettings.json
+++ b/utils/Generate-CoverageBadges/scriptsettings.json
@@ -1,47 +1,6 @@
{
"$schema": "https://json-schema.org/draft-07/schema",
"title": "Generate Coverage Badges Script Settings",
- "description": "Configuration for Generate-CoverageBadges.ps1 script",
- "openReport": false,
- "paths": {
- "testProjects": [
- "..\\..\\src\\MaksIT.Core.Tests"
- ],
- "badgesDir": "..\\..\\assets\\badges"
- },
- "badges": [
- {
- "name": "coverage-lines.svg",
- "label": "Line Coverage",
- "metric": "line"
- },
- {
- "name": "coverage-branches.svg",
- "label": "Branch Coverage",
- "metric": "branch"
- },
- {
- "name": "coverage-methods.svg",
- "label": "Method Coverage",
- "metric": "method"
- }
- ],
- "colorThresholds": {
- "brightgreen": 80,
- "green": 60,
- "yellowgreen": 40,
- "yellow": 20,
- "orange": 10,
- "red": 0
- },
- "_comments": {
- "openReport": "If true, generate and open full HTML coverage report (requires reportgenerator tool).",
- "paths": {
- "testProjects": "Array of relative paths (from this folder) to test project directories or .csproj files. All are run; badge metrics aggregate Cobertura line/branch/method stats.",
- "testProject": "Optional legacy single path if testProjects is omitted.",
- "badgesDir": "Relative path where SVG coverage badges are written."
- },
- "badges": "List of output badges. Each entry maps a metric key (line|branch|method) to filename and label.",
- "colorThresholds": "Coverage percentage thresholds used to pick badge colors."
- }
+ "description": "Legacy settings file. Use utils/Run-Tests/scriptsettings.json instead.",
+ "_forwardTo": "..\\Run-Tests\\scriptsettings.json"
}
diff --git a/utils/Release-Package/CorePlugins/DockerPush.psm1 b/utils/Release-Package/CorePlugins/DockerPush.psm1
index e4371c2..ca7f44a 100644
--- a/utils/Release-Package/CorePlugins/DockerPush.psm1
+++ b/utils/Release-Package/CorePlugins/DockerPush.psm1
@@ -149,13 +149,21 @@ function Invoke-Plugin {
throw "Each images[] entry must define 'service' and 'dockerfile'."
}
+ $service = [string]$img.service
$dockerfileRel = [string]$img.dockerfile
- $dockerfilePath = [System.IO.Path]::GetFullPath((Join-Path $contextPath $dockerfileRel))
+
+ $imgContextPath = $contextPath
+ if ($img.PSObject.Properties.Name -contains 'contextPath' -and -not [string]::IsNullOrWhiteSpace([string]$img.contextPath)) {
+ $imgContextPath = [System.IO.Path]::GetFullPath((Join-Path $scriptDir ([string]$img.contextPath)))
+ if (-not (Test-Path $imgContextPath -PathType Container)) {
+ throw "Docker context directory not found for image '$service': $imgContextPath"
+ }
+ }
+
+ $dockerfilePath = [System.IO.Path]::GetFullPath((Join-Path $imgContextPath $dockerfileRel))
if (-not (Test-Path $dockerfilePath -PathType Leaf)) {
throw "Dockerfile not found: $dockerfilePath"
}
-
- $service = [string]$img.service
$baseName = "$registryUrl/$($pluginSettings.projectName)/$service"
$versionEnvFiles = @()
@@ -165,7 +173,7 @@ function Invoke-Plugin {
continue
}
- $envFilePath = [System.IO.Path]::GetFullPath((Join-Path $contextPath ([string]$relativeEnvFile)))
+ $envFilePath = [System.IO.Path]::GetFullPath((Join-Path $imgContextPath ([string]$relativeEnvFile)))
if (-not (Test-Path -LiteralPath $envFilePath -PathType Leaf)) {
throw "Configured versionEnvFiles entry not found: $envFilePath"
}
@@ -187,7 +195,7 @@ function Invoke-Plugin {
$primaryRef = "${baseName}:$($imageTags[0])"
Write-Log -Level "STEP" -Message "Building $primaryRef ..."
- docker build -t $primaryRef -f $dockerfilePath $contextPath
+ docker build -t $primaryRef -f $dockerfilePath $imgContextPath
if ($LASTEXITCODE -ne 0) {
throw "Docker build failed for $primaryRef"
}
diff --git a/utils/Release-Package/CorePlugins/GitHub.psm1 b/utils/Release-Package/CorePlugins/GitHub.psm1
index ddbc7a6..ef0f227 100644
--- a/utils/Release-Package/CorePlugins/GitHub.psm1
+++ b/utils/Release-Package/CorePlugins/GitHub.psm1
@@ -46,6 +46,32 @@ function Get-GitHubRepositoryInternal {
throw "Could not parse GitHub repo from source: $repoSource. Configure plugins[].repository with 'owner/repo' or a GitHub URL."
}
+function Get-ChangelogVersionHeaderPatternInternal {
+ # Keep a Changelog: ## [1.0.0] - 2026-05-24, bare ## 1.0.0 - 2026-05-24, or legacy ## v1.0.0
+ return '(?m)^##\s+(?:\[(\d+\.\d+\.\d+)\]|v(\d+\.\d+\.\d+)|(\d+\.\d+\.\d+)(?:\s*-\s*\d{4}-\d{2}-\d{2})?\s*$)'
+}
+
+function Get-LatestChangelogVersionInternal {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$ReleaseNotesContent
+ )
+
+ $match = [regex]::Match($ReleaseNotesContent, (Get-ChangelogVersionHeaderPatternInternal))
+ if (-not $match.Success) {
+ return $null
+ }
+
+ foreach ($groupIndex in 1..3) {
+ $version = $match.Groups[$groupIndex].Value
+ if (-not [string]::IsNullOrEmpty($version)) {
+ return $version
+ }
+ }
+
+ return $null
+}
+
function Get-ReleaseNotesInternal {
param(
[Parameter(Mandatory = $true)]
@@ -61,11 +87,11 @@ function Get-ReleaseNotesInternal {
}
$releaseNotesContent = Get-Content $ReleaseNotesFile -Raw
- if ($releaseNotesContent -notmatch '##\s+v(\d+\.\d+\.\d+)') {
+ $releaseNotesVersion = Get-LatestChangelogVersionInternal -ReleaseNotesContent $releaseNotesContent
+ if ([string]::IsNullOrWhiteSpace($releaseNotesVersion)) {
throw "No version entry found in the configured release notes source."
}
- $releaseNotesVersion = $Matches[1]
if ($releaseNotesVersion -ne $Version) {
throw "Project version ($Version) does not match the latest release notes version ($releaseNotesVersion)."
}
@@ -73,7 +99,9 @@ function Get-ReleaseNotesInternal {
Write-Log -Level "OK" -Message " Release notes version matches: v$releaseNotesVersion"
Write-Log -Level "STEP" -Message "Extracting release notes..."
- $pattern = "(?ms)^##\s+v$([regex]::Escape($Version))\b.*?(?=^##\s+v\d+\.\d+\.\d+|\Z)"
+ $escapedVersion = [regex]::Escape($Version)
+ $nextHeaderPattern = '(?m)^##\s+(?:\[\d+\.\d+\.\d+\]|v\d+\.\d+\.\d+|\d+\.\d+\.\d+(?:\s*-\s*\d{4}-\d{2}-\d{2})?\s*$)'
+ $pattern = "(?ms)^##\s+(?:\[$escapedVersion\]|v$escapedVersion|$escapedVersion(?:\s*-\s*\d{4}-\d{2}-\d{2})?).*?(?=$nextHeaderPattern|\Z)"
$match = [regex]::Match($releaseNotesContent, $pattern)
if (-not $match.Success) {
@@ -133,8 +161,17 @@ function Invoke-Plugin {
}
}
+ $requireReleaseAssets = $true
+ if ($null -ne $pluginSettings.requireReleaseAssets) {
+ $requireReleaseAssets = [bool]$pluginSettings.requireReleaseAssets
+ }
+
+ if ($releaseAssetPaths.Count -eq 0 -and $requireReleaseAssets) {
+ throw "GitHub release requires at least one prepared release asset (set requireReleaseAssets: false for notes-only npm releases)."
+ }
+
if ($releaseAssetPaths.Count -eq 0) {
- throw "GitHub release requires at least one prepared release asset."
+ Write-Log -Level "INFO" -Message " Notes-only GitHub release (requireReleaseAssets: false)."
}
$repo = Get-GitHubRepositoryInternal -ConfiguredRepository $configuredRepository
diff --git a/utils/Release-Package/CorePlugins/NpmBuild.psm1 b/utils/Release-Package/CorePlugins/NpmBuild.psm1
index 5307d8f..aeec2d0 100644
--- a/utils/Release-Package/CorePlugins/NpmBuild.psm1
+++ b/utils/Release-Package/CorePlugins/NpmBuild.psm1
@@ -35,7 +35,8 @@ function Invoke-Plugin {
$workspaceRoot = $null
if ($pluginSettings.workspaceRoot) {
- $workspaceRoot = (Resolve-RelativePaths -Value $pluginSettings.workspaceRoot -BasePath $shared.scriptDir)[0]
+ $workspaceRoots = @(Resolve-RelativePaths -Value $pluginSettings.workspaceRoot -BasePath $shared.scriptDir)
+ $workspaceRoot = $workspaceRoots[0]
}
elseif ($shared.PSObject.Properties['npmWorkspaceRoot'] -and -not [string]::IsNullOrWhiteSpace([string]$shared.npmWorkspaceRoot)) {
$workspaceRoot = [string]$shared.npmWorkspaceRoot
diff --git a/utils/Release-Package/CorePlugins/NpmPublish.psm1 b/utils/Release-Package/CorePlugins/NpmPublish.psm1
index 7980830..1e996b4 100644
--- a/utils/Release-Package/CorePlugins/NpmPublish.psm1
+++ b/utils/Release-Package/CorePlugins/NpmPublish.psm1
@@ -45,7 +45,8 @@ function Invoke-Plugin {
$workspaceRoot = $null
if ($pluginSettings.workspaceRoot) {
- $workspaceRoot = (Resolve-RelativePaths -Value $pluginSettings.workspaceRoot -BasePath $shared.scriptDir)[0]
+ $workspaceRoots = @(Resolve-RelativePaths -Value $pluginSettings.workspaceRoot -BasePath $shared.scriptDir)
+ $workspaceRoot = $workspaceRoots[0]
}
elseif ($shared.PSObject.Properties['npmWorkspaceRoot'] -and -not [string]::IsNullOrWhiteSpace([string]$shared.npmWorkspaceRoot)) {
$workspaceRoot = [string]$shared.npmWorkspaceRoot
diff --git a/utils/Release-Package/CorePlugins/NpmReleaseVersion.psm1 b/utils/Release-Package/CorePlugins/NpmReleaseVersion.psm1
index 00808e5..2045209 100644
--- a/utils/Release-Package/CorePlugins/NpmReleaseVersion.psm1
+++ b/utils/Release-Package/CorePlugins/NpmReleaseVersion.psm1
@@ -68,12 +68,11 @@ function Invoke-Plugin {
$pluginSettings = $Settings
$shared = $Settings.context
- $packageJsonPath = if ($pluginSettings.packageJsonPath) {
- (Resolve-RelativePaths -Value $pluginSettings.packageJsonPath -BasePath $shared.scriptDir)[0]
- }
- else {
+ $packageJsonPaths = @(Resolve-RelativePaths -Value $pluginSettings.packageJsonPath -BasePath $shared.scriptDir)
+ if ($packageJsonPaths.Count -eq 0) {
throw "NpmReleaseVersion plugin requires 'packageJsonPath' in scriptsettings.json."
}
+ $packageJsonPath = $packageJsonPaths[0]
$version = Get-PackageJsonVersionInternal -PackageJsonPath $packageJsonPath
$syncWorkspaceVersions = $false
diff --git a/utils/Release-Package/EngineSupport.psm1 b/utils/Release-Package/EngineSupport.psm1
index 230a615..048192a 100644
--- a/utils/Release-Package/EngineSupport.psm1
+++ b/utils/Release-Package/EngineSupport.psm1
@@ -22,7 +22,7 @@ if (-not (Get-Command Get-PluginStageLabel -ErrorAction SilentlyContinue) -or -n
}
}
-if (-not (Get-Command Resolve-DotNetReleaseVersion -ErrorAction SilentlyContinue)) {
+if (-not (Get-Command Resolve-ReleaseVersion -ErrorAction SilentlyContinue)) {
$releaseContextModulePath = Join-Path $PSScriptRoot "ReleaseContext.psm1"
if (Test-Path $releaseContextModulePath -PathType Leaf) {
Import-Module $releaseContextModulePath -Force
@@ -79,7 +79,9 @@ function New-EngineContext {
[psobject]$Settings
)
- $version = (Resolve-DotNetReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir).version
+ $resolvedVersion = Resolve-ReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
+ $version = $resolvedVersion.version
+ $versionSource = $resolvedVersion.source
$artifactsDirectory = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir '..\\..\\release'))
$currentBranch = Get-CurrentBranch
@@ -113,7 +115,7 @@ function New-EngineContext {
Assert-WorkingTreeClean
$tag = "v$version"
- Write-Log -Level "INFO" -Message " Release tag default from DotNetReleaseVersion: $tag (ReleasePublishGuard may replace from git when publish is allowed)."
+ Write-Log -Level "INFO" -Message " Release tag default from ${versionSource}: $tag (ReleasePublishGuard may replace from git when publish is allowed)."
return [pscustomobject]@{
scriptDir = $ScriptDir
diff --git a/utils/Release-Package/README.md b/utils/Release-Package/README.md
index 947a8b6..c684d48 100644
--- a/utils/Release-Package/README.md
+++ b/utils/Release-Package/README.md
@@ -10,14 +10,14 @@ Canonical source: this folder in **maksit-repoutils**. Product repositories refr
|------|------|
| `Release-Package.ps1` | Loads settings, builds `New-EngineContext`, runs plugins in order. |
| `PluginSupport.psm1` | Plugin discovery, `Invoke-ConfiguredPlugin`; publish plugins honor `skipPublishPlugins` from `ReleasePublishGuard` (no per-plugin `branches` for GitHub/NuGet/Docker/Helm). |
-| `ReleaseContext.psm1` | Resolves semver via `Resolve-DotNetReleaseVersion` from the `DotNetReleaseVersion` plugin `projectFiles` (first `.csproj` ``). |
-| `EngineSupport.psm1` | Warn-only dirty-tree preflight; default `context.tag` = `v{version}` from DotNetReleaseVersion; `Initialize-ReleaseStageContext` sets `releaseDir` only. |
+| `ReleaseContext.psm1` | Resolves semver via `Resolve-ReleaseVersion` from `DotNetReleaseVersion.projectFiles` (first `.csproj` ``) or `NpmReleaseVersion.packageJsonPath`. |
+| `EngineSupport.psm1` | Warn-only dirty-tree preflight; default `context.tag` = `v{version}` from the configured version plugin; `Initialize-ReleaseStageContext` sets `releaseDir` only. |
## Plugins
`CorePlugins/` — e.g. `DotNetReleaseVersion`, `NpmReleaseVersion`, `NpmBuild`, `NpmPublish`, `DockerPush`, `HelmPush`, `ReleasePublishGuard`. Optional `CustomPlugins/`.
-`DotNetPack` and `QualityGate` (when used) can declare their own `projectFiles`; semver still comes only from `DotNetReleaseVersion.projectFiles`.
+`DotNetPack` and `QualityGate` (when used) can declare their own `projectFiles`; semver still comes from the configured version plugin (`DotNetReleaseVersion` or `NpmReleaseVersion`).
## `ReleasePublishGuard`
@@ -38,7 +38,7 @@ For TypeScript monorepos published to npmjs:
## Helm charts in git
-Commit `Chart.yaml` with placeholder `version` and `appVersion` (for example `0.0.0`) so `helm lint` stays valid. `HelmPush` temporarily replaces both with the **bare** release semver from `context.version` (`DotNetReleaseVersion`, e.g. `3.3.4` without a `v` prefix) before packaging and OCI push; if `version` were missing, it would fall back to stripping `v`/`V` from `context.tag`. Then it restores `Chart.yaml`. `DockerPush` tags images with the **bare** semver from `context.version` (e.g. `3.3.4`), also pushes `vX.Y.Z` and `shared.tag` when they differ, and optional `latest` — not from `Chart.yaml`; optionally use per-image `versionEnvFiles` to temporarily set `VITE_APP_VERSION={shared.version}` in frontend `.env` files during docker build, then restore originals.
+Commit `Chart.yaml` with placeholder `version` and `appVersion` (for example `0.0.0`) so `helm lint` stays valid. `HelmPush` temporarily replaces both with the **bare** release semver from `context.version` (`DotNetReleaseVersion`, e.g. `3.3.4` without a `v` prefix) before packaging and OCI push; if `version` were missing, it would fall back to stripping `v`/`V` from `context.tag`. Then it restores `Chart.yaml`. `DockerPush` tags images with the **bare** semver from `context.version` (e.g. `3.3.4`), also pushes `vX.Y.Z` and `shared.tag` when they differ, and optional `latest` — not from `Chart.yaml`; optionally use per-image `versionEnvFiles` to temporarily set `VITE_APP_VERSION={shared.version}` in frontend `.env` files during docker build, then restore originals. Each image may override the plugin `contextPath` with its own `contextPath` (paths relative to Release-Package); `dockerfile` and `versionEnvFiles` resolve against that per-image context.
Sample chart: repository `charts/my-service/` (matches the sample `chartPath` in `scriptsettings.json`). Product repos often use `src/helm/` instead.
diff --git a/utils/Release-Package/ReleaseContext.psm1 b/utils/Release-Package/ReleaseContext.psm1
index f6f7a33..c92fb06 100644
--- a/utils/Release-Package/ReleaseContext.psm1
+++ b/utils/Release-Package/ReleaseContext.psm1
@@ -6,9 +6,9 @@
Helpers to resolve release semver from plugin configuration.
.DESCRIPTION
- Used by New-EngineContext and the DotNetReleaseVersion plugin:
- - Source: DotNetReleaseVersion plugin -> projectFiles
- - Version from first path in projectFiles (SDK-style .csproj )
+ Used by New-EngineContext and version plugins:
+ - DotNetReleaseVersion plugin -> projectFiles (.csproj )
+ - NpmReleaseVersion plugin -> packageJsonPath (package.json version)
#>
if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
@@ -136,10 +136,90 @@ function Resolve-DotNetReleaseVersion {
return [pscustomobject]@{
version = $version
+ source = 'DotNetReleaseVersion'
}
}
-Export-ModuleMember -Function Get-CsprojPropertyValue, Get-CsprojVersions, Resolve-RelativePaths, Resolve-DotNetReleaseVersion
+function Resolve-NpmReleaseVersion {
+ param(
+ [Parameter(Mandatory = $true)]
+ [object[]]$Plugins,
+
+ [Parameter(Mandatory = $true)]
+ [string]$ScriptDir
+ )
+
+ $releaseVersionPlugin = @($Plugins | Where-Object { $_.name -eq 'NpmReleaseVersion' } | Select-Object -First 1)
+ if ($releaseVersionPlugin.Count -eq 0 -or $null -eq $releaseVersionPlugin[0]) {
+ Write-Error "Configure an NpmReleaseVersion plugin in scriptsettings.json with packageJsonPath."
+ exit 1
+ }
+
+ $releaseVersionSettings = $releaseVersionPlugin[0]
+ $packageJsonPaths = @(Resolve-RelativePaths -Value $releaseVersionSettings.packageJsonPath -BasePath $ScriptDir)
+
+ if ($packageJsonPaths.Count -eq 0) {
+ Write-Error "Configure release version via NpmReleaseVersion.packageJsonPath."
+ exit 1
+ }
+
+ $packageJsonPath = $packageJsonPaths[0]
+ if (-not (Test-Path $packageJsonPath -PathType Leaf)) {
+ Write-Error "NpmReleaseVersion: package.json not found at: $packageJsonPath"
+ exit 1
+ }
+
+ Write-Log -Level "INFO" -Message "Reading version from npm package.json (packageJsonPath)..."
+ $json = Get-Content -Path $packageJsonPath -Raw -Encoding UTF8 | ConvertFrom-Json
+ $version = [string]$json.version
+ if ([string]::IsNullOrWhiteSpace($version)) {
+ Write-Error "NpmReleaseVersion: 'version' is missing in '$packageJsonPath'."
+ exit 1
+ }
+
+ if ($version -notmatch '^\d+\.\d+\.\d+') {
+ Write-Error "NpmReleaseVersion: version '$version' in '$packageJsonPath' is not a valid semver."
+ exit 1
+ }
+
+ Write-Log -Level "OK" -Message " $([System.IO.Path]::GetFileName($packageJsonPath)): $version"
+
+ return [pscustomobject]@{
+ version = $version
+ source = 'NpmReleaseVersion'
+ }
+}
+
+function Resolve-ReleaseVersion {
+ param(
+ [Parameter(Mandatory = $true)]
+ [object[]]$Plugins,
+
+ [Parameter(Mandatory = $true)]
+ [string]$ScriptDir
+ )
+
+ $dotnetPlugin = @($Plugins | Where-Object { $_.name -eq 'DotNetReleaseVersion' -and $_.enabled -ne $false })
+ $npmPlugin = @($Plugins | Where-Object { $_.name -eq 'NpmReleaseVersion' -and $_.enabled -ne $false })
+
+ if ($dotnetPlugin.Count -gt 0 -and $npmPlugin.Count -gt 0) {
+ Write-Error "Configure only one release version plugin: DotNetReleaseVersion or NpmReleaseVersion, not both."
+ exit 1
+ }
+
+ if ($dotnetPlugin.Count -gt 0) {
+ return Resolve-DotNetReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
+ }
+
+ if ($npmPlugin.Count -gt 0) {
+ return Resolve-NpmReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
+ }
+
+ Write-Error "Configure a DotNetReleaseVersion plugin (projectFiles) or NpmReleaseVersion plugin (packageJsonPath) in scriptsettings.json."
+ exit 1
+}
+
+Export-ModuleMember -Function Get-CsprojPropertyValue, Get-CsprojVersions, Resolve-RelativePaths, Resolve-DotNetReleaseVersion, Resolve-NpmReleaseVersion, Resolve-ReleaseVersion
diff --git a/utils/Release-Package/scriptsettings.json b/utils/Release-Package/scriptsettings.json
index 6114251..51c26a7 100644
--- a/utils/Release-Package/scriptsettings.json
+++ b/utils/Release-Package/scriptsettings.json
@@ -39,7 +39,8 @@
"githubToken": "GITHUB_MAKS_IT_COM",
"repository": "https://github.com/MAKS-IT-COM/maksit-webui",
"releaseNotesFile": "..\\..\\CHANGELOG.md",
- "releaseTitlePattern": "Release {version}"
+ "releaseTitlePattern": "Release {version}",
+ "requireReleaseAssets": false
},
{
"name": "NpmPublish",
@@ -50,9 +51,9 @@
"access": "public",
"workspaceRoot": "..\\..\\src",
"publishOrder": [
- "@maksit/webui-contracts",
- "@maksit/webui-core",
- "@maksit/webui-components"
+ "@maks-it.com/webui-contracts",
+ "@maks-it.com/webui-core",
+ "@maks-it.com/webui-components"
]
}
],
diff --git a/utils/Run-Tests/CorePlugins/CoverageBadges.psm1 b/utils/Run-Tests/CorePlugins/CoverageBadges.psm1
new file mode 100644
index 0000000..398c295
--- /dev/null
+++ b/utils/Run-Tests/CorePlugins/CoverageBadges.psm1
@@ -0,0 +1,177 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+<#
+.SYNOPSIS
+ Coverage badge plugin for the Run-Tests engine.
+
+.DESCRIPTION
+ Reads line/branch/method coverage from shared engine context and writes SVG badges.
+#>
+
+if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
+ $pluginSupportModulePath = Join-Path (Split-Path $PSScriptRoot -Parent) "PluginSupport.psm1"
+ if (Test-Path $pluginSupportModulePath -PathType Leaf) {
+ Import-Module $pluginSupportModulePath -Force -Global -ErrorAction Stop
+ }
+}
+
+function Get-BadgeColorInternal {
+ param(
+ [double]$percentage,
+ [psobject]$thresholds
+ )
+
+ if ($percentage -ge $thresholds.brightgreen) { return 'brightgreen' }
+ if ($percentage -ge $thresholds.green) { return 'green' }
+ if ($percentage -ge $thresholds.yellowgreen) { return 'yellowgreen' }
+ if ($percentage -ge $thresholds.yellow) { return 'yellow' }
+ if ($percentage -ge $thresholds.orange) { return 'orange' }
+ return 'red'
+}
+
+function New-BadgeSvgInternal {
+ param(
+ [string]$label,
+ [string]$value,
+ [string]$color
+ )
+
+ $labelWidth = [math]::Max(($label.Length * 6.5) + 10, 50)
+ $valueWidth = [math]::Max(($value.Length * 6.5) + 10, 40)
+ $totalWidth = $labelWidth + $valueWidth
+ $labelX = $labelWidth / 2
+ $valueX = $labelWidth + ($valueWidth / 2)
+
+ $colorMap = @{
+ brightgreen = '#4c1'
+ green = '#97ca00'
+ yellowgreen = '#a4a61d'
+ yellow = '#dfb317'
+ orange = '#fe7d37'
+ red = '#e05d44'
+ }
+ $hexColor = $colorMap[$color]
+ if (-not $hexColor) { $hexColor = '#9f9f9f' }
+
+ return @"
+
+"@
+}
+
+function Get-CoverageMetricsFromSharedContext {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Shared
+ )
+
+ $line = $null
+ $branch = $null
+ $method = $null
+
+ if ($Shared.PSObject.Properties.Name -contains 'coverageLineRate') {
+ $line = [double]$Shared.coverageLineRate
+ }
+ if ($Shared.PSObject.Properties.Name -contains 'coverageBranchRate') {
+ $branch = [double]$Shared.coverageBranchRate
+ }
+ if ($Shared.PSObject.Properties.Name -contains 'coverageMethodRate') {
+ $method = [double]$Shared.coverageMethodRate
+ }
+
+ if ($null -eq $line -and $Shared.PSObject.Properties.Name -contains 'testResult' -and $null -ne $Shared.testResult) {
+ $line = [double]$Shared.testResult.LineRate
+ $branch = [double]$Shared.testResult.BranchRate
+ $method = [double]$Shared.testResult.MethodRate
+ }
+
+ if ($null -eq $line) {
+ throw 'CoverageBadges requires coverage metrics on shared context. Run NpmJestTest or DotNetTest first.'
+ }
+
+ return @{
+ line = $line
+ branch = $branch
+ method = $method
+ }
+}
+
+function Invoke-Plugin {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Settings
+ )
+
+ Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
+ Import-PluginDependency -ModuleName "ReleaseContext" -RequiredCommand "Resolve-RelativePaths"
+
+ $pluginSettings = $Settings
+ $sharedSettings = $Settings.context
+ $scriptDir = $sharedSettings.scriptDir
+ $metrics = Get-CoverageMetricsFromSharedContext -Shared $sharedSettings
+
+ $badgesDir = $sharedSettings.badgesDir
+ if ($pluginSettings.badgesDir) {
+ $badgesDirs = @(Resolve-RelativePaths -Value $pluginSettings.badgesDir -BasePath $scriptDir)
+ $badgesDir = $badgesDirs[0]
+ }
+ if ([string]::IsNullOrWhiteSpace([string]$badgesDir)) {
+ throw "CoverageBadges requires badgesDir in plugin settings or paths.badgesDir in scriptsettings.json."
+ }
+
+ if (-not (Test-Path $badgesDir)) {
+ New-Item -ItemType Directory -Path $badgesDir | Out-Null
+ }
+
+ $thresholds = $pluginSettings.colorThresholds
+ if ($null -eq $thresholds) {
+ $thresholds = [pscustomobject]@{
+ brightgreen = 80
+ green = 60
+ yellowgreen = 40
+ yellow = 20
+ orange = 10
+ red = 0
+ }
+ }
+
+ Write-Log -Level "STEP" -Message "Generating coverage badges..."
+
+ foreach ($badge in @($pluginSettings.badges)) {
+ $metricValue = $metrics[[string]$badge.metric]
+ if ($null -eq $metricValue) {
+ throw "Unknown or missing coverage metric '$($badge.metric)' for badge '$($badge.name)'."
+ }
+
+ $color = Get-BadgeColorInternal -percentage $metricValue -thresholds $thresholds
+ $svg = New-BadgeSvgInternal -label $badge.label -value "$metricValue%" -color $color
+ $path = Join-Path $badgesDir $badge.name
+ $svg | Out-File -FilePath $path -Encoding utf8NoBOM
+ Write-Log -Level "OK" -Message "$($badge.name): $($badge.label) = $metricValue%"
+ }
+
+ Write-Log -Level "OK" -Message "Badges generated in: $badgesDir"
+ Write-Log -Level "STEP" -Message "Commit the badges folder to update README."
+}
+
+Export-ModuleMember -Function Invoke-Plugin
diff --git a/utils/Run-Tests/CorePlugins/DotNetTest.psm1 b/utils/Run-Tests/CorePlugins/DotNetTest.psm1
new file mode 100644
index 0000000..dba703e
--- /dev/null
+++ b/utils/Run-Tests/CorePlugins/DotNetTest.psm1
@@ -0,0 +1,98 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+<#
+.SYNOPSIS
+ .NET test plugin for executing automated tests.
+
+.DESCRIPTION
+ Resolves one or more .NET test projects (`project` or `projects`), runs tests once
+ via TestRunner, then publishes metrics on the shared engine context for any later
+ plugin: `qualityLineCoverage`, `testResult`, `coverageLineRate` / `coverageBranchRate` / `coverageMethodRate`,
+ method counts, `testResultsDirectory`, `coverageCoberturaPaths`. Quality gates read
+ those keys generically (not tied to this plugin by name). Cobertura files are removed
+ after parsing unless TestRunner gains KeepResults.
+#>
+
+if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
+ $pluginSupportModulePath = Join-Path (Split-Path $PSScriptRoot -Parent) "PluginSupport.psm1"
+ if (Test-Path $pluginSupportModulePath -PathType Leaf) {
+ # Same fallback pattern as the other plugins: use the existing shared module if it is already loaded.
+ Import-Module $pluginSupportModulePath -Force -Global -ErrorAction Stop
+ }
+}
+
+function Invoke-Plugin {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Settings
+ )
+
+ Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
+ Import-PluginDependency -ModuleName "TestRunner" -RequiredCommand "Invoke-TestsWithCoverage"
+
+ $pluginSettings = $Settings
+ $sharedSettings = $Settings.context
+ $testResultsDirSetting = $pluginSettings.resultsDir
+ $scriptDir = $sharedSettings.scriptDir
+
+ $testProjectPaths = [System.Collections.Generic.List[string]]::new()
+ if ($pluginSettings.PSObject.Properties.Name -contains 'projects' -and $pluginSettings.projects) {
+ foreach ($rel in @($pluginSettings.projects)) {
+ if ([string]::IsNullOrWhiteSpace([string]$rel)) { continue }
+ $testProjectPaths.Add([System.IO.Path]::GetFullPath((Join-Path $scriptDir $rel.Trim())))
+ }
+ }
+ if ($testProjectPaths.Count -eq 0 -and $pluginSettings.project) {
+ $testProjectPaths.Add([System.IO.Path]::GetFullPath((Join-Path $scriptDir $pluginSettings.project)))
+ }
+ if ($testProjectPaths.Count -eq 0) {
+ throw "DotNetTest plugin requires 'project' or 'projects' in scriptsettings.json."
+ }
+
+ $testResultsDir = $null
+ if (-not [string]::IsNullOrWhiteSpace($testResultsDirSetting)) {
+ $testResultsDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $testResultsDirSetting))
+ }
+ elseif ($testProjectPaths.Count -gt 1) {
+ $testResultsDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir "TestResults"))
+ }
+
+ Write-Log -Level "STEP" -Message "Running tests..."
+
+ # Build a splatted hashtable so optional arguments can be added without duplicating the call site.
+ $invokeTestParams = @{
+ TestProjectPath = @($testProjectPaths)
+ Silent = $true
+ }
+ if ($testResultsDir) {
+ $invokeTestParams.ResultsDirectory = $testResultsDir
+ }
+
+ $testResult = Invoke-TestsWithCoverage @invokeTestParams
+
+ if (-not $testResult.Success) {
+ throw "Tests failed. $($testResult.Error)"
+ }
+
+ $sharedSettings | Add-Member -NotePropertyName testResult -NotePropertyValue $testResult -Force
+ $sharedSettings | Add-Member -NotePropertyName qualityLineCoverage -NotePropertyValue $testResult.LineRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageLineRate -NotePropertyValue $testResult.LineRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageBranchRate -NotePropertyValue $testResult.BranchRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageMethodRate -NotePropertyValue $testResult.MethodRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageTotalMethods -NotePropertyValue $testResult.TotalMethods -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageCoveredMethods -NotePropertyValue $testResult.CoveredMethods -Force
+ if (($testResult.PSObject.Properties.Name -contains 'ResultsDirectory') -and $testResult.ResultsDirectory) {
+ $sharedSettings | Add-Member -NotePropertyName testResultsDirectory -NotePropertyValue $testResult.ResultsDirectory -Force
+ }
+ if ($testResult.CoverageFiles) {
+ $sharedSettings | Add-Member -NotePropertyName coverageCoberturaPaths -NotePropertyValue @($testResult.CoverageFiles) -Force
+ }
+
+ Write-Log -Level "OK" -Message " All tests passed!"
+ Write-Log -Level "INFO" -Message " Line Coverage: $($testResult.LineRate)%"
+ Write-Log -Level "INFO" -Message " Branch Coverage: $($testResult.BranchRate)%"
+ Write-Log -Level "INFO" -Message " Method Coverage: $($testResult.MethodRate)%"
+}
+
+Export-ModuleMember -Function Invoke-Plugin
diff --git a/utils/Run-Tests/CorePlugins/NpmJestTest.psm1 b/utils/Run-Tests/CorePlugins/NpmJestTest.psm1
new file mode 100644
index 0000000..3566e56
--- /dev/null
+++ b/utils/Run-Tests/CorePlugins/NpmJestTest.psm1
@@ -0,0 +1,82 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+<#
+.SYNOPSIS
+ npm/Jest test plugin for the Run-Tests engine.
+
+.DESCRIPTION
+ Runs Jest with coverage via TestRunner.Invoke-NpmJestTestsWithCoverage and publishes
+ normalized metrics on the shared engine context for downstream plugins.
+#>
+
+if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
+ $pluginSupportModulePath = Join-Path (Split-Path $PSScriptRoot -Parent) "PluginSupport.psm1"
+ if (Test-Path $pluginSupportModulePath -PathType Leaf) {
+ Import-Module $pluginSupportModulePath -Force -Global -ErrorAction Stop
+ }
+}
+
+function Invoke-Plugin {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Settings
+ )
+
+ Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
+ Import-PluginDependency -ModuleName "TestRunner" -RequiredCommand "Invoke-NpmJestTestsWithCoverage"
+ Import-PluginDependency -ModuleName "ScriptConfig" -RequiredCommand "Assert-Command"
+ Import-PluginDependency -ModuleName "ReleaseContext" -RequiredCommand "Resolve-RelativePaths"
+
+ $pluginSettings = $Settings
+ $sharedSettings = $Settings.context
+ $scriptDir = $sharedSettings.scriptDir
+
+ Assert-Command npm
+
+ if (-not $pluginSettings.workspaceRoot) {
+ throw "NpmJestTest plugin requires 'workspaceRoot' in scriptsettings.json."
+ }
+
+ $workspaceRoots = @(Resolve-RelativePaths -Value $pluginSettings.workspaceRoot -BasePath $scriptDir)
+ $workspaceRoot = $workspaceRoots[0]
+
+ $testScript = 'test'
+ if (-not [string]::IsNullOrWhiteSpace([string]$pluginSettings.testScript)) {
+ $testScript = [string]$pluginSettings.testScript
+ }
+
+ $coverageDirectory = 'coverage'
+ if (-not [string]::IsNullOrWhiteSpace([string]$pluginSettings.coverageDirectory)) {
+ $coverageDirectory = [string]$pluginSettings.coverageDirectory
+ }
+
+ $testResult = Invoke-NpmJestTestsWithCoverage -WorkspaceRoot $workspaceRoot -TestScript $testScript -CoverageDirectory $coverageDirectory
+
+ if (-not $testResult.Success) {
+ throw "Tests failed. $($testResult.Error)"
+ }
+
+ $sharedSettings | Add-Member -NotePropertyName npmWorkspaceRoot -NotePropertyValue $workspaceRoot -Force
+ $sharedSettings | Add-Member -NotePropertyName testResult -NotePropertyValue $testResult -Force
+ $sharedSettings | Add-Member -NotePropertyName qualityLineCoverage -NotePropertyValue $testResult.LineRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageLineRate -NotePropertyValue $testResult.LineRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageBranchRate -NotePropertyValue $testResult.BranchRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageMethodRate -NotePropertyValue $testResult.MethodRate -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageTotalMethods -NotePropertyValue $testResult.TotalMethods -Force
+ $sharedSettings | Add-Member -NotePropertyName coverageCoveredMethods -NotePropertyValue $testResult.CoveredMethods -Force
+
+ if (($testResult.PSObject.Properties.Name -contains 'ResultsDirectory') -and $testResult.ResultsDirectory) {
+ $sharedSettings | Add-Member -NotePropertyName testResultsDirectory -NotePropertyValue $testResult.ResultsDirectory -Force
+ }
+ if (($testResult.PSObject.Properties.Name -contains 'CoverageSummaryFile') -and $testResult.CoverageSummaryFile) {
+ $sharedSettings | Add-Member -NotePropertyName coverageSummaryFile -NotePropertyValue $testResult.CoverageSummaryFile -Force
+ }
+
+ Write-Log -Level "OK" -Message " All tests passed!"
+ Write-Log -Level "INFO" -Message " Line Coverage: $($testResult.LineRate)%"
+ Write-Log -Level "INFO" -Message " Branch Coverage: $($testResult.BranchRate)%"
+ Write-Log -Level "INFO" -Message " Method Coverage: $($testResult.MethodRate)%"
+}
+
+Export-ModuleMember -Function Invoke-Plugin
diff --git a/utils/Run-Tests/CorePlugins/QualityGate.psm1 b/utils/Run-Tests/CorePlugins/QualityGate.psm1
new file mode 100644
index 0000000..830c08b
--- /dev/null
+++ b/utils/Run-Tests/CorePlugins/QualityGate.psm1
@@ -0,0 +1,184 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+<#
+.SYNOPSIS
+ Quality gate plugin (coverage threshold + optional .NET vulnerability scan).
+
+.DESCRIPTION
+ Does not run tests or collect coverage. It reads whatever prior plugins left on the
+ shared engine context (same object passed to every plugin as .context).
+
+ Line coverage for threshold checks is resolved in order (first present wins):
+ - qualityLineCoverage (generic; any plugin may set this)
+ - coverageLineRate (conventional flat metric)
+ - testResult.LineRate (object from a test plugin; property name is conventional)
+
+ Configure coverageThreshold > 0 to require one of those inputs. With coverageThreshold 0
+ and scanVulnerabilities false, the plugin is a no-op.
+
+ When scanVulnerabilities is true, runs dotnet list package --vulnerable on projectFiles.
+
+ Use stageLabel "qualityGate" in scriptsettings.json; plugin module: CorePlugins/QualityGate.psm1 (`"name": "QualityGate"`).
+#>
+
+if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
+ $pluginSupportModulePath = Join-Path (Split-Path $PSScriptRoot -Parent) "PluginSupport.psm1"
+ if (Test-Path $pluginSupportModulePath -PathType Leaf) {
+ Import-Module $pluginSupportModulePath -Force -Global -ErrorAction Stop
+ }
+}
+
+function Test-VulnerablePackagesInternal {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string[]]$ProjectFiles
+ )
+
+ $findings = @()
+
+ foreach ($projectPath in $ProjectFiles) {
+ Write-Log -Level "STEP" -Message "Checking vulnerable packages: $([System.IO.Path]::GetFileName($projectPath))"
+
+ $output = & dotnet list $projectPath package --vulnerable --include-transitive 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ throw "dotnet list package --vulnerable failed for $projectPath."
+ }
+
+ $outputText = ($output | Out-String)
+ if ($outputText -match "(?im)\bhas the following vulnerable packages\b" -or $outputText -match "(?im)^\s*>\s+[A-Za-z0-9_.-]+\s") {
+ $findings += [pscustomobject]@{
+ Project = $projectPath
+ Output = $outputText.Trim()
+ }
+ }
+ }
+
+ return $findings
+}
+
+function Get-LineCoveragePercentFromSharedContext {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Shared
+ )
+
+ foreach ($prop in @('qualityLineCoverage', 'coverageLineRate')) {
+ if ($Shared.PSObject.Properties.Name -contains $prop) {
+ $raw = $Shared.$prop
+ if ($null -eq $raw) { continue }
+ $asString = [string]$raw
+ if ([string]::IsNullOrWhiteSpace($asString)) { continue }
+ return [double]$asString
+ }
+ }
+
+ if ($Shared.PSObject.Properties.Name -contains 'testResult' -and $null -ne $Shared.testResult) {
+ $tr = $Shared.testResult
+ if ($tr.PSObject.Properties.Name -contains 'LineRate') {
+ return [double]$tr.LineRate
+ }
+ }
+
+ return $null
+}
+
+function Invoke-Plugin {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Settings
+ )
+
+ Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
+ Import-PluginDependency -ModuleName "ScriptConfig" -RequiredCommand "Assert-Command"
+ Import-PluginDependency -ModuleName "ReleaseContext" -RequiredCommand "Resolve-RelativePaths"
+
+ $pluginSettings = $Settings
+ $sharedSettings = $Settings.context
+ $scriptDir = $sharedSettings.scriptDir
+ $coverageThresholdSetting = $pluginSettings.coverageThreshold
+ $failOnVulnerabilitiesSetting = $pluginSettings.failOnVulnerabilities
+ $scanVulnerabilities = $true
+ if ($null -ne $pluginSettings.scanVulnerabilities) {
+ $scanVulnerabilities = [bool]$pluginSettings.scanVulnerabilities
+ }
+
+ if ($pluginSettings.PSObject.Properties['projectFiles'] -and $null -ne $pluginSettings.projectFiles) {
+ $projectFiles = @(Resolve-RelativePaths -Value $pluginSettings.projectFiles -BasePath $scriptDir)
+ }
+ elseif ($sharedSettings.PSObject.Properties['projectFiles'] -and $null -ne $sharedSettings.projectFiles) {
+ $projectFiles = @($sharedSettings.projectFiles)
+ }
+ else {
+ $projectFiles = @()
+ }
+
+ $coverageThreshold = 0
+ if ($null -ne $coverageThresholdSetting) {
+ $coverageThreshold = [double]$coverageThresholdSetting
+ }
+
+ $needCoverageCheck = $coverageThreshold -gt 0
+ if (-not $needCoverageCheck -and -not $scanVulnerabilities) {
+ Write-Log -Level "INFO" -Message " Quality gate: no checks enabled (coverageThreshold 0, scanVulnerabilities false)."
+ return
+ }
+
+ $lineRate = $null
+ if ($needCoverageCheck) {
+ $lineRate = Get-LineCoveragePercentFromSharedContext -Shared $sharedSettings
+ if ($null -eq $lineRate) {
+ throw "coverageThreshold is $coverageThreshold but shared context has no line coverage. Set one of: qualityLineCoverage, coverageLineRate, or testResult.LineRate (from an earlier plugin)."
+ }
+
+ Write-Log -Level "STEP" -Message "Checking line coverage threshold against shared context..."
+ if ($lineRate -lt $coverageThreshold) {
+ throw "Line coverage $lineRate% is below the configured threshold of $coverageThreshold%."
+ }
+
+ Write-Log -Level "OK" -Message " Coverage threshold met: $lineRate% >= $coverageThreshold%"
+ }
+ else {
+ Write-Log -Level "INFO" -Message " Coverage threshold check not required (coverageThreshold is 0)."
+ }
+
+ if (-not $scanVulnerabilities) {
+ Write-Log -Level "INFO" -Message " Vulnerability scan skipped (scanVulnerabilities is false)."
+ return
+ }
+
+ Assert-Command dotnet
+
+ $failOnVulnerabilities = $true
+ if ($null -ne $failOnVulnerabilitiesSetting) {
+ $failOnVulnerabilities = [bool]$failOnVulnerabilitiesSetting
+ }
+
+ if ($projectFiles.Count -eq 0) {
+ throw "QualityGate requires projectFiles when scanVulnerabilities is true."
+ }
+
+ $vulnerabilities = Test-VulnerablePackagesInternal -ProjectFiles $projectFiles
+
+ if ($vulnerabilities.Count -eq 0) {
+ Write-Log -Level "OK" -Message " No vulnerable packages detected."
+ return
+ }
+
+ foreach ($finding in $vulnerabilities) {
+ Write-Log -Level "WARN" -Message " Vulnerable packages detected in $([System.IO.Path]::GetFileName($finding.Project))"
+ $finding.Output -split "`r?`n" | ForEach-Object {
+ if (-not [string]::IsNullOrWhiteSpace($_)) {
+ Write-Log -Level "WARN" -Message " $_"
+ }
+ }
+ }
+
+ if ($failOnVulnerabilities) {
+ throw "Vulnerable packages were detected and failOnVulnerabilities is enabled."
+ }
+
+ Write-Log -Level "WARN" -Message "Vulnerable packages detected, but failOnVulnerabilities is disabled."
+}
+
+Export-ModuleMember -Function Invoke-Plugin
diff --git a/utils/Run-Tests/CustomPlugins/.gitkeep b/utils/Run-Tests/CustomPlugins/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/utils/Run-Tests/EngineSupport.psm1 b/utils/Run-Tests/EngineSupport.psm1
new file mode 100644
index 0000000..7233e17
--- /dev/null
+++ b/utils/Run-Tests/EngineSupport.psm1
@@ -0,0 +1,35 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
+ $loggingModulePath = Join-Path (Split-Path $PSScriptRoot -Parent) "Logging.psm1"
+ if (Test-Path $loggingModulePath -PathType Leaf) {
+ Import-Module $loggingModulePath -Force
+ }
+}
+
+function New-EngineContext {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$ScriptDir,
+
+ [Parameter(Mandatory = $true)]
+ [string]$UtilsDir,
+
+ [Parameter(Mandatory = $false)]
+ [psobject]$Settings
+ )
+
+ $badgesDir = $null
+ if ($Settings -and $Settings.PSObject.Properties['paths'] -and $Settings.paths.badgesDir) {
+ $badgesDir = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir ([string]$Settings.paths.badgesDir)))
+ }
+
+ return [pscustomobject]@{
+ scriptDir = $ScriptDir
+ utilsDir = $UtilsDir
+ badgesDir = $badgesDir
+ }
+}
+
+Export-ModuleMember -Function New-EngineContext
diff --git a/utils/Run-Tests/PluginSupport.psm1 b/utils/Run-Tests/PluginSupport.psm1
new file mode 100644
index 0000000..e2bb0c3
--- /dev/null
+++ b/utils/Run-Tests/PluginSupport.psm1
@@ -0,0 +1,376 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
+ $loggingModulePath = Join-Path (Split-Path $PSScriptRoot -Parent) "Logging.psm1"
+ if (Test-Path $loggingModulePath -PathType Leaf) {
+ Import-Module $loggingModulePath -Force
+ }
+}
+
+function Import-PluginDependency {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$ModuleName,
+
+ [Parameter(Mandatory = $true)]
+ [string]$RequiredCommand
+ )
+
+ if (Get-Command $RequiredCommand -ErrorAction SilentlyContinue) {
+ return
+ }
+
+ $moduleRoot = Split-Path $PSScriptRoot -Parent
+ $modulePath = Join-Path $moduleRoot "$ModuleName.psm1"
+ if (Test-Path $modulePath -PathType Leaf) {
+ # Import into the global session so the calling plugin can see the exported commands.
+ # Importing only into this module's scope would make the dependency invisible to the plugin.
+ Import-Module $modulePath -Force -Global -ErrorAction Stop
+ }
+
+ if (-not (Get-Command $RequiredCommand -ErrorAction SilentlyContinue)) {
+ throw "Required command '$RequiredCommand' is still unavailable after importing module '$ModuleName'."
+ }
+}
+
+function Get-ConfiguredPlugins {
+ param(
+ [Parameter(Mandatory = $true)]
+ [psobject]$Settings
+ )
+
+ if (-not $Settings.PSObject.Properties['plugins'] -or $null -eq $Settings.plugins) {
+ return @()
+ }
+
+ # JSON can deserialize a single plugin as one object or multiple plugins as an array.
+ # Always return an array so the engine can loop without special-case logic.
+ if ($Settings.plugins -is [System.Collections.IEnumerable] -and -not ($Settings.plugins -is [string])) {
+ return @($Settings.plugins)
+ }
+
+ return @($Settings.plugins)
+}
+
+function Get-PluginStageLabel {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin
+ )
+
+ if (-not $Plugin.PSObject.Properties['stageLabel'] -or [string]::IsNullOrWhiteSpace([string]$Plugin.stageLabel)) {
+ return 'release'
+ }
+
+ return [string]$Plugin.stageLabel
+}
+
+function Get-PluginBranches {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin
+ )
+
+ if (-not $Plugin.PSObject.Properties['branches'] -or $null -eq $Plugin.branches) {
+ return @()
+ }
+
+ # Strings are also IEnumerable in PowerShell, so exclude them or we would split into characters.
+ if ($Plugin.branches -is [System.Collections.IEnumerable] -and -not ($Plugin.branches -is [string])) {
+ return @($Plugin.branches | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
+ }
+
+ if ([string]::IsNullOrWhiteSpace([string]$Plugin.branches)) {
+ return @()
+ }
+
+ return @([string]$Plugin.branches)
+}
+
+function Test-PluginAllowedOnBranch {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin,
+
+ [Parameter(Mandatory = $true)]
+ [string]$CurrentBranch
+ )
+
+ $allowedBranches = Get-PluginBranches -Plugin $Plugin
+ if ($allowedBranches.Count -eq 0) {
+ return $true
+ }
+
+ if ($allowedBranches -contains '*') {
+ return $true
+ }
+
+ return $allowedBranches -contains $CurrentBranch
+}
+
+function Test-IsPublishPlugin {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin
+ )
+
+ if ($null -eq $Plugin -or [string]::IsNullOrWhiteSpace([string]$Plugin.name)) {
+ return $false
+ }
+
+ return @('GitHub', 'DotNetNuGet', 'DockerPush', 'HelmPush', 'NpmPublish') -contains ([string]$Plugin.name)
+}
+
+function Get-PluginSettingValue {
+ param(
+ [Parameter(Mandatory = $true)]
+ [object[]]$Plugins,
+
+ [Parameter(Mandatory = $true)]
+ [string]$PropertyName
+ )
+
+ foreach ($plugin in $Plugins) {
+ if ($null -eq $plugin -or [string]::IsNullOrWhiteSpace($plugin.name)) {
+ continue
+ }
+
+ if (-not $plugin.PSObject.Properties[$PropertyName]) {
+ continue
+ }
+
+ $value = $plugin.$PropertyName
+ if ($null -eq $value) {
+ continue
+ }
+
+ if ($value -is [string] -and [string]::IsNullOrWhiteSpace($value)) {
+ continue
+ }
+
+ return $value
+ }
+
+ return $null
+}
+
+function Get-PluginPathListSetting {
+ param(
+ [Parameter(Mandatory = $true)]
+ [object[]]$Plugins,
+
+ [Parameter(Mandatory = $true)]
+ [string]$PropertyName,
+
+ [Parameter(Mandatory = $true)]
+ [string]$BasePath
+ )
+
+ $rawPaths = @()
+ $value = Get-PluginSettingValue -Plugins $Plugins -PropertyName $PropertyName
+
+ if ($null -eq $value) {
+ return @()
+ }
+
+ # Same rule as above: treat a string as one path, not a char-by-char sequence.
+ if ($value -is [System.Collections.IEnumerable] -and -not ($value -is [string])) {
+ $rawPaths += $value
+ }
+ else {
+ $rawPaths += $value
+ }
+
+ $resolvedPaths = @()
+ foreach ($path in $rawPaths) {
+ if ([string]::IsNullOrWhiteSpace([string]$path)) {
+ continue
+ }
+
+ $resolvedPaths += [System.IO.Path]::GetFullPath((Join-Path $BasePath ([string]$path)))
+ }
+
+ # Wrap again to stop PowerShell from unrolling a single-item array into a bare string.
+ return @($resolvedPaths)
+}
+
+function Get-PluginPathSetting {
+ param(
+ [Parameter(Mandatory = $true)]
+ [object[]]$Plugins,
+
+ [Parameter(Mandatory = $true)]
+ [string]$PropertyName,
+
+ [Parameter(Mandatory = $true)]
+ [string]$BasePath
+ )
+
+ $value = Get-PluginSettingValue -Plugins $Plugins -PropertyName $PropertyName
+ if ($null -eq $value -or [string]::IsNullOrWhiteSpace([string]$value)) {
+ return $null
+ }
+
+ return [System.IO.Path]::GetFullPath((Join-Path $BasePath ([string]$value)))
+}
+
+function Get-ArchiveNamePattern {
+ param(
+ [Parameter(Mandatory = $true)]
+ [object[]]$Plugins,
+
+ [Parameter(Mandatory = $true)]
+ [string]$CurrentBranch
+ )
+
+ foreach ($plugin in $Plugins) {
+ if ($null -eq $plugin -or [string]::IsNullOrWhiteSpace($plugin.name)) {
+ continue
+ }
+
+ if (-not $plugin.enabled) {
+ continue
+ }
+
+ if (-not (Test-PluginAllowedOnBranch -Plugin $plugin -CurrentBranch $CurrentBranch)) {
+ continue
+ }
+
+ if ($plugin.PSObject.Properties['zipNamePattern'] -and -not [string]::IsNullOrWhiteSpace([string]$plugin.zipNamePattern)) {
+ return [string]$plugin.zipNamePattern
+ }
+ }
+
+ return "release-{version}.zip"
+}
+
+function Resolve-PluginModulePath {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin,
+
+ [Parameter(Mandatory = $true)]
+ [string]$PluginsDirectory
+ )
+
+ $pluginFileName = "{0}.psm1" -f $Plugin.name
+ $candidatePaths = @(
+ (Join-Path $PluginsDirectory $pluginFileName),
+ (Join-Path (Join-Path (Split-Path $PluginsDirectory -Parent) "CustomPlugins") $pluginFileName)
+ )
+
+ foreach ($candidatePath in $candidatePaths) {
+ if (Test-Path $candidatePath -PathType Leaf) {
+ return $candidatePath
+ }
+ }
+
+ return $candidatePaths[0]
+}
+
+function Test-PluginRunnable {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin,
+
+ [Parameter(Mandatory = $true)]
+ [psobject]$SharedSettings,
+
+ [Parameter(Mandatory = $true)]
+ [string]$PluginsDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [bool]$WriteLogs = $true
+ )
+
+ if ($null -eq $Plugin -or [string]::IsNullOrWhiteSpace($Plugin.name)) {
+ if ($WriteLogs) {
+ Write-Log -Level "WARN" -Message "Skipping plugin entry with no name."
+ }
+ return $false
+ }
+
+ if (-not $Plugin.enabled) {
+ if ($WriteLogs) {
+ Write-Log -Level "WARN" -Message "Skipping plugin '$($Plugin.name)' (disabled)."
+ }
+ return $false
+ }
+
+ $pluginModulePath = Resolve-PluginModulePath -Plugin $Plugin -PluginsDirectory $PluginsDirectory
+ if (-not (Test-Path $pluginModulePath -PathType Leaf)) {
+ if ($WriteLogs) {
+ Write-Log -Level "ERROR" -Message "Plugin module not found: $pluginModulePath"
+ }
+ return $false
+ }
+
+ return $true
+}
+
+function New-PluginInvocationSettings {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin,
+
+ [Parameter(Mandatory = $true)]
+ [psobject]$SharedSettings
+ )
+
+ $properties = @{}
+ foreach ($property in $Plugin.PSObject.Properties) {
+ $properties[$property.Name] = $property.Value
+ }
+
+ # Plugins receive their own config plus shared runtime context.
+ $properties['context'] = $SharedSettings
+ return [pscustomobject]$properties
+}
+
+function Invoke-ConfiguredPlugin {
+ param(
+ [Parameter(Mandatory = $true)]
+ $Plugin,
+
+ [Parameter(Mandatory = $true)]
+ [psobject]$SharedSettings,
+
+ [Parameter(Mandatory = $true)]
+ [string]$PluginsDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [bool]$ContinueOnError = $true
+ )
+
+ if (-not (Test-PluginRunnable -Plugin $Plugin -SharedSettings $SharedSettings -PluginsDirectory $PluginsDirectory -WriteLogs:$true)) {
+ return
+ }
+
+ if ((Test-IsPublishPlugin -Plugin $Plugin) -and ($SharedSettings.PSObject.Properties.Name -contains 'skipPublishPlugins') -and $SharedSettings.skipPublishPlugins) {
+ Write-Log -Level "INFO" -Message "Skipping plugin '$($Plugin.name)' (ReleasePublishGuard suppressed publish)."
+ return
+ }
+
+ $pluginModulePath = Resolve-PluginModulePath -Plugin $Plugin -PluginsDirectory $PluginsDirectory
+ Write-Log -Level "STEP" -Message "Running plugin '$($Plugin.name)'..."
+
+ try {
+ $moduleInfo = Import-Module $pluginModulePath -Force -PassThru -ErrorAction Stop
+ # Resolve Invoke-Plugin from the imported module explicitly so we call the plugin we just loaded,
+ # not some command with the same name from another module already in session.
+ $invokeCommand = Get-Command -Name "Invoke-Plugin" -Module $moduleInfo.Name -ErrorAction Stop
+ $pluginSettings = New-PluginInvocationSettings -Plugin $Plugin -SharedSettings $SharedSettings
+
+ & $invokeCommand -Settings $pluginSettings
+ Write-Log -Level "OK" -Message " Plugin '$($Plugin.name)' completed."
+ }
+ catch {
+ Write-Log -Level "ERROR" -Message " Plugin '$($Plugin.name)' failed: $($_.Exception.Message)"
+ if (-not $ContinueOnError) {
+ exit 1
+ }
+ }
+}
+
+Export-ModuleMember -Function Import-PluginDependency, Get-ConfiguredPlugins, Get-PluginStageLabel, Get-PluginBranches, Test-IsPublishPlugin, Get-PluginSettingValue, Get-PluginPathListSetting, Get-PluginPathSetting, Get-ArchiveNamePattern, Resolve-PluginModulePath, Test-PluginRunnable, New-PluginInvocationSettings, Invoke-ConfiguredPlugin
diff --git a/utils/Run-Tests/README.md b/utils/Run-Tests/README.md
new file mode 100644
index 0000000..db4c2a6
--- /dev/null
+++ b/utils/Run-Tests/README.md
@@ -0,0 +1,58 @@
+# Run Tests
+
+Plugin-driven test engine (same pattern as `src/Release-Package`).
+
+## Run
+
+```powershell
+pwsh -File .\src\Run-Tests\Run-Tests.ps1
+```
+
+Or:
+
+```bat
+src\Run-Tests\Run-Tests.bat
+```
+
+## Core plugins
+
+| Plugin | Role |
+|--------|------|
+| `DotNetTest` | `dotnet test` + Coverlet Cobertura (`.NET` repos) |
+| `NpmJestTest` | `npm test -- --coverage` + Jest `coverage-summary.json` |
+| `QualityGate` | Optional line-coverage threshold from shared context |
+| `CoverageBadges` | SVG badges for README (`assets/badges/`) |
+
+Configure plugin order and settings in `scriptsettings.json`.
+
+## Shared context
+
+Test plugins publish metrics for downstream plugins:
+
+- `qualityLineCoverage`, `coverageLineRate`, `coverageBranchRate`, `coverageMethodRate`
+- `testResult` (full result object from `TestRunner`)
+
+`QualityGate` and `CoverageBadges` read these keys; they do not re-run tests.
+
+## npm/Jest example
+
+Replace `DotNetTest` with:
+
+```json
+{
+ "name": "NpmJestTest",
+ "stageLabel": "test",
+ "enabled": true,
+ "workspaceRoot": "..\\..\\src",
+ "testScript": "test",
+ "coverageDirectory": "coverage"
+}
+```
+
+## Legacy entry point
+
+`src/Generate-CoverageBadges/Generate-CoverageBadges.ps1` forwards to this engine.
+
+## Custom plugins
+
+Add `CustomPlugins/YourPlugin.psm1` with `Invoke-Plugin`, then register it in `scriptsettings.json`.
diff --git a/utils/Run-Tests/Run-Tests.bat b/utils/Run-Tests/Run-Tests.bat
new file mode 100644
index 0000000..345b419
--- /dev/null
+++ b/utils/Run-Tests/Run-Tests.bat
@@ -0,0 +1,3 @@
+@echo off
+pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0Run-Tests.ps1"
+pause
diff --git a/utils/Run-Tests/Run-Tests.ps1 b/utils/Run-Tests/Run-Tests.ps1
new file mode 100644
index 0000000..79613b5
--- /dev/null
+++ b/utils/Run-Tests/Run-Tests.ps1
@@ -0,0 +1,76 @@
+#requires -Version 7.0
+#requires -PSEdition Core
+
+<#
+.SYNOPSIS
+ Plugin-driven test and coverage engine.
+
+.DESCRIPTION
+ Loads scriptsettings.json, builds shared execution context, and runs configured
+ plugins in order. Each plugin implements Invoke-Plugin and receives its own
+ settings plus shared context on Settings.context.
+
+.USAGE
+ pwsh -File .\Run-Tests.ps1
+#>
+
+$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
+$utilsDir = Split-Path $scriptDir -Parent
+
+$scriptConfigModulePath = Join-Path $utilsDir "ScriptConfig.psm1"
+if (-not (Test-Path $scriptConfigModulePath)) {
+ Write-Error "ScriptConfig module not found at: $scriptConfigModulePath"
+ exit 1
+}
+Import-Module $scriptConfigModulePath -Force
+
+$loggingModulePath = Join-Path $utilsDir "Logging.psm1"
+if (-not (Test-Path $loggingModulePath)) {
+ Write-Error "Logging module not found at: $loggingModulePath"
+ exit 1
+}
+Import-Module $loggingModulePath -Force
+
+$pluginSupportModulePath = Join-Path $scriptDir "PluginSupport.psm1"
+if (-not (Test-Path $pluginSupportModulePath)) {
+ Write-Error "PluginSupport module not found at: $pluginSupportModulePath"
+ exit 1
+}
+Import-Module $pluginSupportModulePath -Force
+
+$engineSupportModulePath = Join-Path $scriptDir "EngineSupport.psm1"
+if (-not (Test-Path $engineSupportModulePath)) {
+ Write-Error "EngineSupport module not found at: $engineSupportModulePath"
+ exit 1
+}
+Import-Module $engineSupportModulePath -Force
+
+$releaseContextModulePath = Join-Path $utilsDir "Release-Package\ReleaseContext.psm1"
+if (-not (Test-Path $releaseContextModulePath)) {
+ Write-Error "ReleaseContext module not found at: $releaseContextModulePath"
+ exit 1
+}
+Import-Module $releaseContextModulePath -Force
+
+$settings = Get-ScriptSettings -ScriptDir $scriptDir
+$configuredPlugins = Get-ConfiguredPlugins -Settings $settings
+$pluginsDir = Join-Path $scriptDir "CorePlugins"
+
+Write-Log -Level "STEP" -Message "=================================================="
+Write-Log -Level "STEP" -Message "TEST ENGINE"
+Write-Log -Level "STEP" -Message "=================================================="
+
+$engineContext = New-EngineContext -ScriptDir $scriptDir -UtilsDir $utilsDir -Settings $settings
+
+if ($configuredPlugins.Count -eq 0) {
+ Write-Log -Level "WARN" -Message "No plugins configured in scriptsettings.json."
+ exit 0
+}
+
+foreach ($plugin in $configuredPlugins) {
+ Invoke-ConfiguredPlugin -Plugin $plugin -SharedSettings $engineContext -PluginsDirectory $pluginsDir -ContinueOnError:$false
+}
+
+Write-Log -Level "OK" -Message "=================================================="
+Write-Log -Level "OK" -Message "TEST RUN COMPLETE"
+Write-Log -Level "OK" -Message "=================================================="
diff --git a/utils/Run-Tests/scriptsettings.json b/utils/Run-Tests/scriptsettings.json
new file mode 100644
index 0000000..34158dc
--- /dev/null
+++ b/utils/Run-Tests/scriptsettings.json
@@ -0,0 +1,63 @@
+{
+ "$schema": "https://json-schema.org/draft-07/schema",
+ "title": "Run Tests Script Settings",
+ "description": "maksit-webui: plugin-driven Jest tests and coverage badges.",
+ "paths": {
+ "badgesDir": "..\\..\\assets\\badges"
+ },
+ "plugins": [
+ {
+ "name": "NpmJestTest",
+ "stageLabel": "test",
+ "enabled": true,
+ "workspaceRoot": "..\\..\\src",
+ "testScript": "test",
+ "coverageDirectory": "coverage"
+ },
+ {
+ "name": "QualityGate",
+ "stageLabel": "qualityGate",
+ "enabled": true,
+ "coverageThreshold": 0,
+ "scanVulnerabilities": false
+ },
+ {
+ "name": "CoverageBadges",
+ "stageLabel": "report",
+ "enabled": true,
+ "badgesDir": "..\\..\\assets\\badges",
+ "badges": [
+ {
+ "name": "coverage-lines.svg",
+ "label": "Line Coverage",
+ "metric": "line"
+ },
+ {
+ "name": "coverage-branches.svg",
+ "label": "Branch Coverage",
+ "metric": "branch"
+ },
+ {
+ "name": "coverage-methods.svg",
+ "label": "Method Coverage",
+ "metric": "method"
+ }
+ ],
+ "colorThresholds": {
+ "brightgreen": 80,
+ "green": 60,
+ "yellowgreen": 40,
+ "yellow": 20,
+ "orange": 10,
+ "red": 0
+ }
+ }
+ ],
+ "_comments": {
+ "plugins": {
+ "NpmJestTest": "Runs npm test with Jest coverage in workspaceRoot. Publishes coverage metrics on shared context.",
+ "QualityGate": "Reads shared context metrics; set coverageThreshold > 0 to enforce minimum line coverage.",
+ "CoverageBadges": "Writes SVG badges from shared context metrics into badgesDir."
+ }
+ }
+}
diff --git a/utils/TestRunner.psm1 b/utils/TestRunner.psm1
index 6a8616b..395acf4 100644
--- a/utils/TestRunner.psm1
+++ b/utils/TestRunner.psm1
@@ -283,4 +283,110 @@ function Invoke-TestsWithCoverage {
}
}
-Export-ModuleMember -Function Invoke-TestsWithCoverage
+function Invoke-NpmJestTestsWithCoverage {
+ <#
+ .SYNOPSIS
+ Runs npm/Jest tests with coverage and returns normalized metrics.
+
+ .PARAMETER WorkspaceRoot
+ npm workspace root (folder containing package.json and jest.config).
+
+ .PARAMETER TestScript
+ npm script name to run (default: test). Coverage flags are appended via `--`.
+
+ .PARAMETER CoverageDirectory
+ Relative path under WorkspaceRoot where Jest writes coverage output.
+
+ .PARAMETER Silent
+ Suppress console output from npm.
+
+ .OUTPUTS
+ Same metric shape as Invoke-TestsWithCoverage, plus CoverageSummaryFile when available.
+ #>
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$WorkspaceRoot,
+
+ [string]$TestScript = 'test',
+
+ [string]$CoverageDirectory = 'coverage',
+
+ [switch]$Silent
+ )
+
+ $ErrorActionPreference = 'Stop'
+ $workspaceFull = [System.IO.Path]::GetFullPath($WorkspaceRoot)
+ if (-not (Test-Path (Join-Path $workspaceFull 'package.json') -PathType Leaf)) {
+ return [PSCustomObject]@{
+ Success = $false
+ Error = "package.json not found in workspace root: $workspaceFull"
+ }
+ }
+
+ if (-not $Silent) {
+ Write-TestRunnerLogInternal -Level 'STEP' -Message 'Running npm/Jest tests with coverage...'
+ Write-TestRunnerLogInternal -Level 'INFO' -Message "Workspace: $workspaceFull"
+ }
+
+ Push-Location $workspaceFull
+ try {
+ $npmArgs = @('run', $TestScript, '--', '--coverage', '--coverageReporters=json-summary', '--coverageReporters=text')
+ if ($Silent) {
+ $null = & npm @npmArgs 2>&1
+ }
+ else {
+ & npm @npmArgs
+ }
+
+ if ($LASTEXITCODE -ne 0) {
+ return [PSCustomObject]@{
+ Success = $false
+ Error = "npm run $TestScript failed with exit code $LASTEXITCODE"
+ }
+ }
+ }
+ finally {
+ Pop-Location
+ }
+
+ $summaryPath = Join-Path $workspaceFull (Join-Path $CoverageDirectory 'coverage-summary.json')
+ if (-not (Test-Path $summaryPath -PathType Leaf)) {
+ return [PSCustomObject]@{
+ Success = $false
+ Error = "Jest coverage summary not found at: $summaryPath"
+ }
+ }
+
+ $summaryJson = Get-Content -LiteralPath $summaryPath -Raw -Encoding UTF8 | ConvertFrom-Json
+ $total = $summaryJson.total
+ if ($null -eq $total) {
+ return [PSCustomObject]@{
+ Success = $false
+ Error = "Jest coverage summary is missing 'total' metrics in: $summaryPath"
+ }
+ }
+
+ $lineRate = [math]::Round([double]$total.lines.pct, 1)
+ $branchRate = [math]::Round([double]$total.branches.pct, 1)
+ $methodRate = [math]::Round([double]$total.functions.pct, 1)
+ $totalMethods = [int]$total.functions.total
+ $coveredMethods = [int]$total.functions.covered
+ $resultsDirectory = [System.IO.Path]::GetFullPath((Join-Path $workspaceFull $CoverageDirectory))
+
+ if (-not $Silent) {
+ Write-TestRunnerLogInternal -Level 'OK' -Message "Coverage summary: $summaryPath"
+ }
+
+ return [PSCustomObject]@{
+ Success = $true
+ LineRate = $lineRate
+ BranchRate = $branchRate
+ MethodRate = $methodRate
+ TotalMethods = $totalMethods
+ CoveredMethods = $coveredMethods
+ CoverageSummaryFile = $summaryPath
+ ResultsDirectory = $resultsDirectory
+ }
+}
+
+Export-ModuleMember -Function Invoke-TestsWithCoverage, Invoke-NpmJestTestsWithCoverage
diff --git a/utils/Update-RepoUtils/scriptsettings.json b/utils/Update-RepoUtils/scriptsettings.json
index 568a597..ed0142c 100644
--- a/utils/Update-RepoUtils/scriptsettings.json
+++ b/utils/Update-RepoUtils/scriptsettings.json
@@ -9,7 +9,8 @@
"preserveFileName": "scriptsettings.json",
"cloneDepth": 1,
"skippedRelativeDirectories": [
- "Release-Package/CustomPlugins"
+ "Release-Package/CustomPlugins",
+ "Run-Tests/CustomPlugins"
]
}
}