diff --git a/.config/prettierignore b/.config/prettierignore index 4d106a8c..67d30674 100644 --- a/.config/prettierignore +++ b/.config/prettierignore @@ -1,10 +1,11 @@ .config -.github -.vscode -docs -es6 -js-legacy -node_modules -screenshots -LICENSE -package-lock.json +../.github +../.vscode +../docs +../es6 +../js-legacy +../node_modules +../screenshots +../LICENSE +../package-lock.json +../types/generated diff --git a/.config/rollup.config.js b/.config/rollup.config.js index 599a93f3..fa1d3471 100644 --- a/.config/rollup.config.js +++ b/.config/rollup.config.js @@ -25,6 +25,7 @@ export default [ { input: 'src/plugins/jsmind.draggable-node.js', output: { + name: 'jsMindDraggableNode', file: 'es6/jsmind.draggable-node.js', format: 'umd', banner: '/**\n* @license BSD-3-Clause\n* @copyright 2014-2025 hizzgdev@163.com\n*\n* Project Home:\n* https://github.com/hizzgdev/jsmind/\n*/', @@ -32,6 +33,7 @@ export default [ globals: { jsmind: 'jsMind', }, + exports: 'named', }, external: ['jsmind'], plugins: [ @@ -48,6 +50,7 @@ export default [ { input: 'src/plugins/jsmind.screenshot.js', output: { + name: 'jsMindScreenshot', file: 'es6/jsmind.screenshot.js', format: 'umd', banner: '/**\n* @license BSD-3-Clause\n* @copyright 2014-2025 hizzgdev@163.com\n*\n* Project Home:\n* https://github.com/hizzgdev/jsmind/\n*/', @@ -56,6 +59,7 @@ export default [ 'jsmind': 'jsMind', 'dom-to-image': 'domtoimage', }, + exports: 'named', }, external: ['jsmind', 'dom-to-image'], plugins: [ diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 6b9a1ff7..302a3b63 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 18.x - - run: npm ci + - run: npm ci && npm run gen:dts:check - run: npm test publish-npm: @@ -28,7 +28,7 @@ jobs: node-version: 18.x registry-url: https://registry.npmjs.org/ - run: npm ci - - run: npm run build + - run: npm run gen:dts && npm run build - run: npm publish env: NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/.gitignore b/.gitignore index 9036ae2b..6d9c8121 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ es6/*.js es6/*.js.map +types/generated/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..a2b1dd32 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +## Changelog / 变更日志 + +### 2025-08-10 + +- Added + + - Central typings entry `types/index.d.ts` and regenerated declaration files under `types/generated/` for core and plugins (dom, format, graph, layout_provider, mind, node, option, plugin, shortcut_provider, util, view_provider, draggable-node, screenshot). + - Declaration build config `tsconfig.decls.json` and npm scripts: `gen:dts`, `gen:dts:check`. + - TypeScript typings validation: `tests/unit/typescript.types.test.js` with fixture `tests/fixtures/typescript-test.ts` using TypeScript Compiler API. + +- Changed + + - Updated `package.json` to expose `types` via `types/index.d.ts` and added scripts for generating/checking declarations. + - Enriched JSDoc and in-source documentation across core modules and plugins to improve generated typings (data_provider, dom, format, graph, layout_provider, mind, node, option, plugin, util, view_provider, draggable-node, screenshot). + - Updated `tsconfig.json` strictness and paths; CI updated to run `npm run gen:dts` before build and include `gen:dts:check` in install pipeline. + - Dev dependencies refreshed, adding `typescript`, `tsd-jsdoc`, and updating various `@types/*`, jest, jsdoc toolchain packages. + +- Removed + + - Legacy type files under `types/` (`jsmind.d.ts`, `jsmind.draggable-node.d.ts`, `jsmind.screenshot.d.ts`) in favor of `types/generated/`. + +- Notes + + - Running `npm test` now validates both runtime tests and TypeScript definitions. Consumers can rely on the aggregated public typings from the package root; plugin typings are exposed under `plugins/` paths. + +### 2025-08-09 + +- Added + + - TypeScript support: publish `.d.ts` for core and plugins (`es6/jsmind.d.ts`, `es6/jsmind.draggable-node.d.ts`, `es6/jsmind.screenshot.d.ts`). + - Type checking in CI: added Jest test `tests/unit/typescript.types.test.js` using TypeScript Compiler API to verify typings with `tsconfig.json` (noEmit). + +- Changed + + - Updated root `package.json` to expose `types` and proper `exports` fields for typings. + +- Notes + + - The TS example `example/typescript-test.ts` is included in `tsconfig.json#includes` for compile-time verification. + - Running `npm test` will now validate TypeScript definitions. diff --git a/package-lock.json b/package-lock.json index 2e87ac1b..03092af0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,9 @@ "jest-environment-jsdom": "^28.1.0", "prettier": "2.6.2", "rollup": "2.79.2", - "rollup-plugin-cleanup": "^3.2.1" + "rollup-plugin-cleanup": "^3.2.1", + "tsd-jsdoc": "^2.5.0", + "typescript": "^5.9.2" } }, "node_modules/@ampproject/remapping": { @@ -1075,6 +1077,34 @@ "@types/tough-cookie": "*" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "22.7.5", "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.7.5.tgz", @@ -1373,6 +1403,14 @@ "node": ">= 0.8" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1519,6 +1557,20 @@ } ] }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmmirror.com/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", @@ -1851,6 +1903,17 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", @@ -3251,6 +3314,62 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmmirror.com/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsdom": { "version": "19.0.0", "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-19.0.0.tgz", @@ -3327,6 +3446,17 @@ "node": ">=6" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", @@ -3351,6 +3481,17 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", @@ -3423,6 +3564,58 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmmirror.com/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peer": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3433,6 +3626,14 @@ "node": ">= 0.4" } }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3923,6 +4124,17 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz", @@ -4322,6 +4534,13 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", + "dev": true, + "peer": true + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/terminal-link/-/terminal-link-2.1.1.tgz", @@ -4434,6 +4653,33 @@ "node": ">=12" } }, + "node_modules/tsd-jsdoc": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/tsd-jsdoc/-/tsd-jsdoc-2.5.0.tgz", + "integrity": "sha512-80fcJLAiUeerg4xPftp+iEEKWUjJjHk9AvcHwJqA8Zw0R4oASdu3kT/plE/Zj19QUTz8KupyOX25zStlNJjS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "typescript": "^3.2.1" + }, + "peerDependencies": { + "jsdoc": "^3.6.3" + } + }, + "node_modules/tsd-jsdoc/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmmirror.com/type-detect/-/type-detect-4.0.8.tgz", @@ -4455,6 +4701,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmmirror.com/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz", @@ -4709,6 +4985,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index b3df0bcb..c90885a6 100644 --- a/package.json +++ b/package.json @@ -3,24 +3,49 @@ "version": "0.8.7", "description": "jsMind is a pure javascript library for mindmap, it base on html5 canvas. jsMind was released under BSD license, you can embed it in any project, if only you observe the license.", "main": "es6/jsmind.js", + "types": "types/generated/types-index.d.ts", "exports": { - ".": "./es6/jsmind.js", - "./draggable-node": "./es6/jsmind.draggable-node.js", - "./screenshot": "./es6/jsmind.screenshot.js", + ".": { + "import": "./es6/jsmind.js", + "require": "./es6/jsmind.js", + "types": "./types/generated/types-index.d.ts" + }, + "./draggable-node": { + "import": "./es6/jsmind.draggable-node.js", + "require": "./es6/jsmind.draggable-node.js", + "types": "./types/generated/plugins/jsmind.draggable-node.d.ts" + }, + "./screenshot": { + "import": "./es6/jsmind.screenshot.js", + "require": "./es6/jsmind.screenshot.js", + "types": "./types/generated/plugins/jsmind.screenshot.d.ts" + }, "./style/jsmind.css": "./style/jsmind.css" }, "directories": { "doc": "docs", "example": "example" }, + "files": [ + "es6", + "style", + "types", + "LICENSE", + "README.md" + ], "scripts": { "server": "http-server", "build": "rollup -c .config/rollup.config.js", "test": "NODE_OPTIONS=--experimental-vm-modules jest", "test-legacy": "jest tests/legacy", "test-es6": "NODE_OPTIONS=--experimental-vm-modules jest tests/unit", + "gen:dts": "tsc -p tsconfig.decls.json", + "gen:dts:check": "npm run gen:dts && test -d types/generated && ls types/generated >/dev/null", "format": "prettier --config .config/prettierrc.json --ignore-path .config/prettierignore --write .", - "format-check": "prettier --config .config/prettierrc.json --ignore-path .config/prettierignore --check ." + "format-check": "prettier --config .config/prettierrc.json --ignore-path .config/prettierignore --check .", + "pretest": "npm run gen:dts", + "prepare": "npm run gen:dts", + "prepublishOnly": "npm run build && npm run gen:dts" }, "repository": { "type": "git", @@ -45,13 +70,15 @@ } ], "devDependencies": { + "@rollup/plugin-terser": "^0.4.4", "http-server": "^14.1.1", "jest": "^28.1.0", "jest-environment-jsdom": "^28.1.0", "prettier": "2.6.2", "rollup": "2.79.2", "rollup-plugin-cleanup": "^3.2.1", - "@rollup/plugin-terser": "^0.4.4" + "tsd-jsdoc": "^2.5.0", + "typescript": "^5.9.2" }, "jest": { "verbose": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..d0048cb9 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4321 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@rollup/plugin-terser': + specifier: ^0.4.4 + version: 0.4.4(rollup@2.79.2) + http-server: + specifier: ^14.1.1 + version: 14.1.1 + jest: + specifier: ^28.1.0 + version: 28.1.3 + jest-environment-jsdom: + specifier: ^28.1.0 + version: 28.1.3 + prettier: + specifier: 2.6.2 + version: 2.6.2 + rollup: + specifier: 2.79.2 + version: 2.79.2 + rollup-plugin-cleanup: + specifier: ^3.2.1 + version: 3.2.1(rollup@2.79.2) + tsd-jsdoc: + specifier: ^2.5.0 + version: 2.5.0(jsdoc@3.6.11) + typescript: + specifier: ^5.9.2 + version: 5.9.2 + +packages: + /@ampproject/remapping@2.3.0: + resolution: + { + integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==, + } + engines: { node: '>=6.0.0' } + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + dev: true + + /@babel/code-frame@7.27.1: + resolution: + { + integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + dev: true + + /@babel/compat-data@7.28.0: + resolution: + { + integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==, + } + engines: { node: '>=6.9.0' } + dev: true + + /@babel/core@7.28.0: + resolution: + { + integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helpers': 7.28.2 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.28.0: + resolution: + { + integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + dev: true + + /@babel/helper-compilation-targets@7.27.2: + resolution: + { + integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.2 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-globals@7.28.0: + resolution: + { + integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, + } + engines: { node: '>=6.9.0' } + dev: true + + /@babel/helper-module-imports@7.27.1: + resolution: + { + integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-plugin-utils@7.27.1: + resolution: + { + integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==, + } + engines: { node: '>=6.9.0' } + dev: true + + /@babel/helper-string-parser@7.27.1: + resolution: + { + integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, + } + engines: { node: '>=6.9.0' } + dev: true + + /@babel/helper-validator-identifier@7.27.1: + resolution: + { + integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==, + } + engines: { node: '>=6.9.0' } + dev: true + + /@babel/helper-validator-option@7.27.1: + resolution: + { + integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, + } + engines: { node: '>=6.9.0' } + dev: true + + /@babel/helpers@7.28.2: + resolution: + { + integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + dev: true + + /@babel/parser@7.28.0: + resolution: + { + integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==, + } + engines: { node: '>=6.0.0' } + hasBin: true + dependencies: + '@babel/types': 7.28.2 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0): + resolution: + { + integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0): + resolution: + { + integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.0): + resolution: + { + integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0): + resolution: + { + integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0): + resolution: + { + integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0): + resolution: + { + integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0): + resolution: + { + integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.0): + resolution: + { + integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0): + resolution: + { + integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0): + resolution: + { + integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + dev: true + + /@babel/template@7.27.2: + resolution: + { + integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + dev: true + + /@babel/traverse@7.28.0: + resolution: + { + integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.28.2: + resolution: + { + integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==, + } + engines: { node: '>=6.9.0' } + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: + { + integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, + } + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: + { + integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==, + } + engines: { node: '>=8' } + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: + { + integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==, + } + engines: { node: '>=8' } + dev: true + + /@jest/console@28.1.3: + resolution: + { + integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + chalk: 4.1.2 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + slash: 3.0.0 + dev: true + + /@jest/core@28.1.3: + resolution: + { + integrity: sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 28.1.3 + '@jest/reporters': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 28.1.3 + jest-config: 28.1.3(@types/node@24.2.1) + jest-haste-map: 28.1.3 + jest-message-util: 28.1.3 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-resolve-dependencies: 28.1.3 + jest-runner: 28.1.3 + jest-runtime: 28.1.3 + jest-snapshot: 28.1.3 + jest-util: 28.1.3 + jest-validate: 28.1.3 + jest-watcher: 28.1.3 + micromatch: 4.0.8 + pretty-format: 28.1.3 + rimraf: 3.0.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + + /@jest/environment@28.1.3: + resolution: + { + integrity: sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/fake-timers': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + jest-mock: 28.1.3 + dev: true + + /@jest/expect-utils@28.1.3: + resolution: + { + integrity: sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + jest-get-type: 28.0.2 + dev: true + + /@jest/expect@28.1.3: + resolution: + { + integrity: sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + expect: 28.1.3 + jest-snapshot: 28.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@28.1.3: + resolution: + { + integrity: sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + '@sinonjs/fake-timers': 9.1.2 + '@types/node': 24.2.1 + jest-message-util: 28.1.3 + jest-mock: 28.1.3 + jest-util: 28.1.3 + dev: true + + /@jest/globals@28.1.3: + resolution: + { + integrity: sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/environment': 28.1.3 + '@jest/expect': 28.1.3 + '@jest/types': 28.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@28.1.3: + resolution: + { + integrity: sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@jridgewell/trace-mapping': 0.3.29 + '@types/node': 24.2.1 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + jest-worker: 28.1.3 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + terminal-link: 2.1.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@28.1.3: + resolution: + { + integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@sinclair/typebox': 0.24.51 + dev: true + + /@jest/source-map@28.1.2: + resolution: + { + integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@28.1.3: + resolution: + { + integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/console': 28.1.3 + '@jest/types': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@28.1.3: + resolution: + { + integrity: sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/test-result': 28.1.3 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + slash: 3.0.0 + dev: true + + /@jest/transform@28.1.3: + resolution: + { + integrity: sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@babel/core': 7.28.0 + '@jest/types': 28.1.3 + '@jridgewell/trace-mapping': 0.3.29 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.9.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + jest-regex-util: 28.0.2 + jest-util: 28.1.3 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@28.1.3: + resolution: + { + integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/schemas': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.2.1 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.12: + resolution: + { + integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==, + } + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + dev: true + + /@jridgewell/resolve-uri@3.1.2: + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: '>=6.0.0' } + dev: true + + /@jridgewell/source-map@0.3.10: + resolution: + { + integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==, + } + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + dev: true + + /@jridgewell/sourcemap-codec@1.5.4: + resolution: + { + integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==, + } + dev: true + + /@jridgewell/trace-mapping@0.3.29: + resolution: + { + integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==, + } + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + dev: true + + /@rollup/plugin-terser@0.4.4(rollup@2.79.2): + resolution: + { + integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==, + } + engines: { node: '>=14.0.0' } + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + rollup: 2.79.2 + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.43.1 + dev: true + + /@sinclair/typebox@0.24.51: + resolution: + { + integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==, + } + dev: true + + /@sinonjs/commons@1.8.6: + resolution: + { + integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==, + } + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@9.1.2: + resolution: + { + integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==, + } + dependencies: + '@sinonjs/commons': 1.8.6 + dev: true + + /@tootallnate/once@2.0.0: + resolution: + { + integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==, + } + engines: { node: '>= 10' } + dev: true + + /@types/babel__core@7.20.5: + resolution: + { + integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, + } + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + dev: true + + /@types/babel__generator@7.27.0: + resolution: + { + integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, + } + dependencies: + '@babel/types': 7.28.2 + dev: true + + /@types/babel__template@7.4.4: + resolution: + { + integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, + } + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + dev: true + + /@types/babel__traverse@7.28.0: + resolution: + { + integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, + } + dependencies: + '@babel/types': 7.28.2 + dev: true + + /@types/graceful-fs@4.1.9: + resolution: + { + integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==, + } + dependencies: + '@types/node': 24.2.1 + dev: true + + /@types/istanbul-lib-coverage@2.0.6: + resolution: + { + integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==, + } + dev: true + + /@types/istanbul-lib-report@3.0.3: + resolution: + { + integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==, + } + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + dev: true + + /@types/istanbul-reports@3.0.4: + resolution: + { + integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==, + } + dependencies: + '@types/istanbul-lib-report': 3.0.3 + dev: true + + /@types/jsdom@16.2.15: + resolution: + { + integrity: sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==, + } + dependencies: + '@types/node': 24.2.1 + '@types/parse5': 6.0.3 + '@types/tough-cookie': 4.0.5 + dev: true + + /@types/linkify-it@5.0.0: + resolution: + { + integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==, + } + dev: true + + /@types/markdown-it@12.2.3: + resolution: + { + integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==, + } + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + dev: true + + /@types/mdurl@2.0.0: + resolution: + { + integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==, + } + dev: true + + /@types/node@24.2.1: + resolution: + { + integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==, + } + dependencies: + undici-types: 7.10.0 + dev: true + + /@types/parse5@6.0.3: + resolution: + { + integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==, + } + dev: true + + /@types/prettier@2.7.3: + resolution: + { + integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==, + } + dev: true + + /@types/stack-utils@2.0.3: + resolution: + { + integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==, + } + dev: true + + /@types/tough-cookie@4.0.5: + resolution: + { + integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==, + } + dev: true + + /@types/yargs-parser@21.0.3: + resolution: + { + integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==, + } + dev: true + + /@types/yargs@17.0.33: + resolution: + { + integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==, + } + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + + /abab@2.0.6: + resolution: + { + integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==, + } + deprecated: Use your platform's native atob() and btoa() methods instead + dev: true + + /acorn-globals@6.0.0: + resolution: + { + integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==, + } + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + dev: true + + /acorn-walk@7.2.0: + resolution: + { + integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==, + } + engines: { node: '>=0.4.0' } + dev: true + + /acorn@7.4.1: + resolution: + { + integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==, + } + engines: { node: '>=0.4.0' } + hasBin: true + dev: true + + /acorn@8.15.0: + resolution: + { + integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, + } + engines: { node: '>=0.4.0' } + hasBin: true + dev: true + + /agent-base@6.0.2: + resolution: + { + integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==, + } + engines: { node: '>= 6.0.0' } + dependencies: + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + dev: true + + /ansi-escapes@4.3.2: + resolution: + { + integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==, + } + engines: { node: '>=8' } + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: '>=8' } + dev: true + + /ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: '>=8' } + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, + } + engines: { node: '>=10' } + dev: true + + /anymatch@3.1.3: + resolution: + { + integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, + } + engines: { node: '>= 8' } + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse@1.0.10: + resolution: + { + integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==, + } + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } + dev: true + + /async@3.2.6: + resolution: + { + integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==, + } + dev: true + + /asynckit@0.4.0: + resolution: + { + integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, + } + dev: true + + /babel-jest@28.1.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.28.0 + '@jest/transform': 28.1.3 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 28.1.3(@babel/core@7.28.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: + { + integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==, + } + engines: { node: '>=8' } + dependencies: + '@babel/helper-plugin-utils': 7.27.1 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@28.1.3: + resolution: + { + integrity: sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + dev: true + + /babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.0): + resolution: + { + integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==, + } + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.0) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) + dev: true + + /babel-preset-jest@28.1.3(@babel/core@7.28.0): + resolution: + { + integrity: sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.28.0 + babel-plugin-jest-hoist: 28.1.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.0) + dev: true + + /balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + dev: true + + /basic-auth@2.0.1: + resolution: + { + integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==, + } + engines: { node: '>= 0.8' } + dependencies: + safe-buffer: 5.1.2 + dev: true + + /bluebird@3.7.2: + resolution: + { + integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==, + } + dev: true + + /brace-expansion@1.1.12: + resolution: + { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, + } + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: '>=8' } + dependencies: + fill-range: 7.1.1 + dev: true + + /browser-process-hrtime@1.0.0: + resolution: + { + integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==, + } + dev: true + + /browserslist@4.25.2: + resolution: + { + integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==, + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + dependencies: + caniuse-lite: 1.0.30001733 + electron-to-chromium: 1.5.199 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.2) + dev: true + + /bser@2.1.1: + resolution: + { + integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==, + } + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: + { + integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, + } + dev: true + + /call-bind-apply-helpers@1.0.2: + resolution: + { + integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, + } + engines: { node: '>= 0.4' } + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + dev: true + + /call-bound@1.0.4: + resolution: + { + integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, + } + engines: { node: '>= 0.4' } + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + dev: true + + /callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: '>=6' } + dev: true + + /camelcase@5.3.1: + resolution: + { + integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, + } + engines: { node: '>=6' } + dev: true + + /camelcase@6.3.0: + resolution: + { + integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, + } + engines: { node: '>=10' } + dev: true + + /caniuse-lite@1.0.30001733: + resolution: + { + integrity: sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==, + } + dev: true + + /catharsis@0.9.0: + resolution: + { + integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==, + } + engines: { node: '>= 10' } + dependencies: + lodash: 4.17.21 + dev: true + + /chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: '>=10' } + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /char-regex@1.0.2: + resolution: + { + integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==, + } + engines: { node: '>=10' } + dev: true + + /ci-info@3.9.0: + resolution: + { + integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==, + } + engines: { node: '>=8' } + dev: true + + /cjs-module-lexer@1.4.3: + resolution: + { + integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==, + } + dev: true + + /cliui@8.0.1: + resolution: + { + integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, + } + engines: { node: '>=12' } + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /co@4.6.0: + resolution: + { + integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==, + } + engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } + dev: true + + /collect-v8-coverage@1.0.2: + resolution: + { + integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==, + } + dev: true + + /color-convert@2.0.1: + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: '>=7.0.0' } + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } + dev: true + + /combined-stream@1.0.8: + resolution: + { + integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, + } + engines: { node: '>= 0.8' } + dependencies: + delayed-stream: 1.0.0 + dev: true + + /commander@2.20.3: + resolution: + { + integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, + } + dev: true + + /concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } + dev: true + + /convert-source-map@1.9.0: + resolution: + { + integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==, + } + dev: true + + /convert-source-map@2.0.0: + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } + dev: true + + /corser@2.0.1: + resolution: + { + integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==, + } + engines: { node: '>= 0.4.0' } + dev: true + + /cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: '>= 8' } + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /cssom@0.3.8: + resolution: + { + integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==, + } + dev: true + + /cssom@0.5.0: + resolution: + { + integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==, + } + dev: true + + /cssstyle@2.3.0: + resolution: + { + integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==, + } + engines: { node: '>=8' } + dependencies: + cssom: 0.3.8 + dev: true + + /data-urls@3.0.2: + resolution: + { + integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==, + } + engines: { node: '>=12' } + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + dev: true + + /debug@4.4.1: + resolution: + { + integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==, + } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /decimal.js@10.6.0: + resolution: + { + integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, + } + dev: true + + /dedent@0.7.0: + resolution: + { + integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==, + } + dev: true + + /deepmerge@4.3.1: + resolution: + { + integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, + } + engines: { node: '>=0.10.0' } + dev: true + + /delayed-stream@1.0.0: + resolution: + { + integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, + } + engines: { node: '>=0.4.0' } + dev: true + + /detect-newline@3.1.0: + resolution: + { + integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, + } + engines: { node: '>=8' } + dev: true + + /diff-sequences@28.1.1: + resolution: + { + integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dev: true + + /domexception@4.0.0: + resolution: + { + integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==, + } + engines: { node: '>=12' } + deprecated: Use your platform's native DOMException instead + dependencies: + webidl-conversions: 7.0.0 + dev: true + + /dunder-proto@1.0.1: + resolution: + { + integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, + } + engines: { node: '>= 0.4' } + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + dev: true + + /electron-to-chromium@1.5.199: + resolution: + { + integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==, + } + dev: true + + /emittery@0.10.2: + resolution: + { + integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==, + } + engines: { node: '>=12' } + dev: true + + /emoji-regex@8.0.0: + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, + } + dev: true + + /entities@2.1.0: + resolution: + { + integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==, + } + dev: true + + /error-ex@1.3.2: + resolution: + { + integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==, + } + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-define-property@1.0.1: + resolution: + { + integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, + } + engines: { node: '>= 0.4' } + dev: true + + /es-errors@1.3.0: + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, + } + engines: { node: '>= 0.4' } + dev: true + + /es-object-atoms@1.1.1: + resolution: + { + integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, + } + engines: { node: '>= 0.4' } + dependencies: + es-errors: 1.3.0 + dev: true + + /es-set-tostringtag@2.1.0: + resolution: + { + integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, + } + engines: { node: '>= 0.4' } + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: true + + /escalade@3.2.0: + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: '>=6' } + dev: true + + /escape-string-regexp@2.0.0: + resolution: + { + integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==, + } + engines: { node: '>=8' } + dev: true + + /escodegen@2.1.0: + resolution: + { + integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==, + } + engines: { node: '>=6.0' } + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + + /esprima@4.0.1: + resolution: + { + integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, + } + engines: { node: '>=4' } + hasBin: true + dev: true + + /estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: '>=4.0' } + dev: true + + /estree-walker@0.6.1: + resolution: + { + integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==, + } + dev: true + + /esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: '>=0.10.0' } + dev: true + + /eventemitter3@4.0.7: + resolution: + { + integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, + } + dev: true + + /execa@5.1.1: + resolution: + { + integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, + } + engines: { node: '>=10' } + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /exit@0.1.2: + resolution: + { + integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==, + } + engines: { node: '>= 0.8.0' } + dev: true + + /expect@28.1.3: + resolution: + { + integrity: sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/expect-utils': 28.1.3 + jest-get-type: 28.0.2 + jest-matcher-utils: 28.1.3 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + dev: true + + /fb-watchman@2.0.2: + resolution: + { + integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==, + } + dependencies: + bser: 2.1.1 + dev: true + + /fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: '>=8' } + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@4.1.0: + resolution: + { + integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, + } + engines: { node: '>=8' } + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /follow-redirects@1.15.11: + resolution: + { + integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==, + } + engines: { node: '>=4.0' } + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: true + + /form-data@4.0.4: + resolution: + { + integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==, + } + engines: { node: '>= 6' } + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + dev: true + + /fs.realpath@1.0.0: + resolution: + { + integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, + } + dev: true + + /fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } + dev: true + + /gensync@1.0.0-beta.2: + resolution: + { + integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, + } + engines: { node: '>=6.9.0' } + dev: true + + /get-caller-file@2.0.5: + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, + } + engines: { node: 6.* || 8.* || >= 10.* } + dev: true + + /get-intrinsic@1.3.0: + resolution: + { + integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, + } + engines: { node: '>= 0.4' } + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + dev: true + + /get-package-type@0.1.0: + resolution: + { + integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==, + } + engines: { node: '>=8.0.0' } + dev: true + + /get-proto@1.0.1: + resolution: + { + integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, + } + engines: { node: '>= 0.4' } + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + dev: true + + /get-stream@6.0.1: + resolution: + { + integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==, + } + engines: { node: '>=10' } + dev: true + + /glob@7.2.3: + resolution: + { + integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, + } + deprecated: Glob versions prior to v9 are no longer supported + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /gopd@1.2.0: + resolution: + { + integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, + } + engines: { node: '>= 0.4' } + dev: true + + /graceful-fs@4.2.11: + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, + } + dev: true + + /has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: '>=8' } + dev: true + + /has-symbols@1.1.0: + resolution: + { + integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, + } + engines: { node: '>= 0.4' } + dev: true + + /has-tostringtag@1.0.2: + resolution: + { + integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, + } + engines: { node: '>= 0.4' } + dependencies: + has-symbols: 1.1.0 + dev: true + + /hasown@2.0.2: + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, + } + engines: { node: '>= 0.4' } + dependencies: + function-bind: 1.1.2 + dev: true + + /he@1.2.0: + resolution: + { + integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, + } + hasBin: true + dev: true + + /html-encoding-sniffer@3.0.0: + resolution: + { + integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==, + } + engines: { node: '>=12' } + dependencies: + whatwg-encoding: 2.0.0 + dev: true + + /html-escaper@2.0.2: + resolution: + { + integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==, + } + dev: true + + /http-proxy-agent@5.0.0: + resolution: + { + integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==, + } + engines: { node: '>= 6' } + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + dev: true + + /http-proxy@1.18.1: + resolution: + { + integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==, + } + engines: { node: '>=8.0.0' } + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.11 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + dev: true + + /http-server@14.1.1: + resolution: + { + integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==, + } + engines: { node: '>=12' } + hasBin: true + dependencies: + basic-auth: 2.0.1 + chalk: 4.1.2 + corser: 2.0.1 + he: 1.2.0 + html-encoding-sniffer: 3.0.0 + http-proxy: 1.18.1 + mime: 1.6.0 + minimist: 1.2.8 + opener: 1.5.2 + portfinder: 1.0.37 + secure-compare: 3.0.1 + union: 0.5.0 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + - supports-color + dev: true + + /https-proxy-agent@5.0.1: + resolution: + { + integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==, + } + engines: { node: '>= 6' } + dependencies: + agent-base: 6.0.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + dev: true + + /human-signals@2.1.0: + resolution: + { + integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==, + } + engines: { node: '>=10.17.0' } + dev: true + + /iconv-lite@0.6.3: + resolution: + { + integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, + } + engines: { node: '>=0.10.0' } + dependencies: + safer-buffer: 2.1.2 + dev: true + + /import-local@3.2.0: + resolution: + { + integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==, + } + engines: { node: '>=8' } + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: '>=0.8.19' } + dev: true + + /inflight@1.0.6: + resolution: + { + integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, + } + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, + } + dev: true + + /is-arrayish@0.2.1: + resolution: + { + integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, + } + dev: true + + /is-core-module@2.16.1: + resolution: + { + integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, + } + engines: { node: '>= 0.4' } + dependencies: + hasown: 2.0.2 + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, + } + engines: { node: '>=8' } + dev: true + + /is-generator-fn@2.1.0: + resolution: + { + integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==, + } + engines: { node: '>=6' } + dev: true + + /is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: '>=0.12.0' } + dev: true + + /is-potential-custom-element-name@1.0.1: + resolution: + { + integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, + } + dev: true + + /is-stream@2.0.1: + resolution: + { + integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, + } + engines: { node: '>=8' } + dev: true + + /isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + dev: true + + /istanbul-lib-coverage@3.2.2: + resolution: + { + integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==, + } + engines: { node: '>=8' } + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: + { + integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==, + } + engines: { node: '>=8' } + dependencies: + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: + { + integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==, + } + engines: { node: '>=10' } + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: + { + integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==, + } + engines: { node: '>=10' } + dependencies: + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.7: + resolution: + { + integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==, + } + engines: { node: '>=8' } + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /jest-changed-files@28.1.3: + resolution: + { + integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + dev: true + + /jest-circus@28.1.3: + resolution: + { + integrity: sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/environment': 28.1.3 + '@jest/expect': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + is-generator-fn: 2.1.0 + jest-each: 28.1.3 + jest-matcher-utils: 28.1.3 + jest-message-util: 28.1.3 + jest-runtime: 28.1.3 + jest-snapshot: 28.1.3 + jest-util: 28.1.3 + p-limit: 3.1.0 + pretty-format: 28.1.3 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-cli@28.1.3: + resolution: + { + integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.2.0 + jest-config: 28.1.3(@types/node@24.2.1) + jest-util: 28.1.3 + jest-validate: 28.1.3 + prompts: 2.4.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /jest-config@28.1.3(@types/node@24.2.1): + resolution: + { + integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.28.0 + '@jest/test-sequencer': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + babel-jest: 28.1.3(@babel/core@7.28.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 28.1.3 + jest-environment-node: 28.1.3 + jest-get-type: 28.0.2 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-runner: 28.1.3 + jest-util: 28.1.3 + jest-validate: 28.1.3 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 28.1.3 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-diff@28.1.3: + resolution: + { + integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + chalk: 4.1.2 + diff-sequences: 28.1.1 + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + dev: true + + /jest-docblock@28.1.1: + resolution: + { + integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@28.1.3: + resolution: + { + integrity: sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + chalk: 4.1.2 + jest-get-type: 28.0.2 + jest-util: 28.1.3 + pretty-format: 28.1.3 + dev: true + + /jest-environment-jsdom@28.1.3: + resolution: + { + integrity: sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/environment': 28.1.3 + '@jest/fake-timers': 28.1.3 + '@jest/types': 28.1.3 + '@types/jsdom': 16.2.15 + '@types/node': 24.2.1 + jest-mock: 28.1.3 + jest-util: 28.1.3 + jsdom: 19.0.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-environment-node@28.1.3: + resolution: + { + integrity: sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/environment': 28.1.3 + '@jest/fake-timers': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + jest-mock: 28.1.3 + jest-util: 28.1.3 + dev: true + + /jest-get-type@28.0.2: + resolution: + { + integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dev: true + + /jest-haste-map@28.1.3: + resolution: + { + integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 24.2.1 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 28.0.2 + jest-util: 28.1.3 + jest-worker: 28.1.3 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@28.1.3: + resolution: + { + integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + dev: true + + /jest-matcher-utils@28.1.3: + resolution: + { + integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + chalk: 4.1.2 + jest-diff: 28.1.3 + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + dev: true + + /jest-message-util@28.1.3: + resolution: + { + integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 28.1.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 28.1.3 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@28.1.3: + resolution: + { + integrity: sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@28.1.3): + resolution: + { + integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==, + } + engines: { node: '>=6' } + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 28.1.3 + dev: true + + /jest-regex-util@28.0.2: + resolution: + { + integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dev: true + + /jest-resolve-dependencies@28.1.3: + resolution: + { + integrity: sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + jest-regex-util: 28.0.2 + jest-snapshot: 28.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@28.1.3: + resolution: + { + integrity: sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + jest-pnp-resolver: 1.2.3(jest-resolve@28.1.3) + jest-util: 28.1.3 + jest-validate: 28.1.3 + resolve: 1.22.10 + resolve.exports: 1.1.1 + slash: 3.0.0 + dev: true + + /jest-runner@28.1.3: + resolution: + { + integrity: sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/console': 28.1.3 + '@jest/environment': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + chalk: 4.1.2 + emittery: 0.10.2 + graceful-fs: 4.2.11 + jest-docblock: 28.1.1 + jest-environment-node: 28.1.3 + jest-haste-map: 28.1.3 + jest-leak-detector: 28.1.3 + jest-message-util: 28.1.3 + jest-resolve: 28.1.3 + jest-runtime: 28.1.3 + jest-util: 28.1.3 + jest-watcher: 28.1.3 + jest-worker: 28.1.3 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@28.1.3: + resolution: + { + integrity: sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/environment': 28.1.3 + '@jest/fake-timers': 28.1.3 + '@jest/globals': 28.1.3 + '@jest/source-map': 28.1.2 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.2 + execa: 5.1.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + jest-message-util: 28.1.3 + jest-mock: 28.1.3 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-snapshot: 28.1.3 + jest-util: 28.1.3 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@28.1.3: + resolution: + { + integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@babel/core': 7.28.0 + '@babel/generator': 7.28.0 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + '@jest/expect-utils': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@types/babel__traverse': 7.28.0 + '@types/prettier': 2.7.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.0) + chalk: 4.1.2 + expect: 28.1.3 + graceful-fs: 4.2.11 + jest-diff: 28.1.3 + jest-get-type: 28.0.2 + jest-haste-map: 28.1.3 + jest-matcher-utils: 28.1.3 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + natural-compare: 1.4.0 + pretty-format: 28.1.3 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@28.1.3: + resolution: + { + integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@28.1.3: + resolution: + { + integrity: sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/types': 28.1.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 28.0.2 + leven: 3.1.0 + pretty-format: 28.1.3 + dev: true + + /jest-watcher@28.1.3: + resolution: + { + integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.2.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.10.2 + jest-util: 28.1.3 + string-length: 4.0.2 + dev: true + + /jest-worker@28.1.3: + resolution: + { + integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@types/node': 24.2.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@28.1.3: + resolution: + { + integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 28.1.3 + '@jest/types': 28.1.3 + import-local: 3.2.0 + jest-cli: 28.1.3 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /js-cleanup@1.2.0: + resolution: + { + integrity: sha512-JeDD0yiiSt80fXzAVa/crrS0JDPQljyBG/RpOtaSbyDq03VHa9szJWMaWOYU/bcTn412uMN2MxApXq8v79cUiQ==, + } + engines: { node: ^10.14.2 || >=12.0.0 } + dependencies: + magic-string: 0.25.9 + perf-regexes: 1.0.1 + skip-regex: 1.0.2 + dev: true + + /js-tokens@4.0.0: + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } + dev: true + + /js-yaml@3.14.1: + resolution: + { + integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==, + } + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js2xmlparser@4.0.2: + resolution: + { + integrity: sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==, + } + dependencies: + xmlcreate: 2.0.4 + dev: true + + /jsdoc@3.6.11: + resolution: + { + integrity: sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==, + } + engines: { node: '>=12.0.0' } + hasBin: true + dependencies: + '@babel/parser': 7.28.0 + '@types/markdown-it': 12.2.3 + bluebird: 3.7.2 + catharsis: 0.9.0 + escape-string-regexp: 2.0.0 + js2xmlparser: 4.0.2 + klaw: 3.0.0 + markdown-it: 12.3.2 + markdown-it-anchor: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2) + marked: 4.3.0 + mkdirp: 1.0.4 + requizzle: 0.2.4 + strip-json-comments: 3.1.1 + taffydb: 2.6.2 + underscore: 1.13.7 + dev: true + + /jsdom@19.0.0: + resolution: + { + integrity: sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==, + } + engines: { node: '>=12' } + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.15.0 + acorn-globals: 6.0.0 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.4 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.21 + parse5: 6.0.1 + saxes: 5.0.1 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 3.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 10.0.0 + ws: 8.18.3 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /jsesc@3.1.0: + resolution: + { + integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, + } + engines: { node: '>=6' } + hasBin: true + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: + { + integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, + } + dev: true + + /json5@2.2.3: + resolution: + { + integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, + } + engines: { node: '>=6' } + hasBin: true + dev: true + + /klaw@3.0.0: + resolution: + { + integrity: sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==, + } + dependencies: + graceful-fs: 4.2.11 + dev: true + + /kleur@3.0.3: + resolution: + { + integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==, + } + engines: { node: '>=6' } + dev: true + + /leven@3.1.0: + resolution: + { + integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==, + } + engines: { node: '>=6' } + dev: true + + /lines-and-columns@1.2.4: + resolution: + { + integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, + } + dev: true + + /linkify-it@3.0.3: + resolution: + { + integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==, + } + dependencies: + uc.micro: 1.0.6 + dev: true + + /locate-path@5.0.0: + resolution: + { + integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, + } + engines: { node: '>=8' } + dependencies: + p-locate: 4.1.0 + dev: true + + /lodash@4.17.21: + resolution: + { + integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, + } + dev: true + + /lru-cache@5.1.1: + resolution: + { + integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, + } + dependencies: + yallist: 3.1.1 + dev: true + + /magic-string@0.25.9: + resolution: + { + integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==, + } + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /make-dir@4.0.0: + resolution: + { + integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==, + } + engines: { node: '>=10' } + dependencies: + semver: 7.7.2 + dev: true + + /makeerror@1.0.12: + resolution: + { + integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, + } + dependencies: + tmpl: 1.0.5 + dev: true + + /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2): + resolution: + { + integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==, + } + peerDependencies: + '@types/markdown-it': '*' + markdown-it: '*' + dependencies: + '@types/markdown-it': 12.2.3 + markdown-it: 12.3.2 + dev: true + + /markdown-it@12.3.2: + resolution: + { + integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==, + } + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 2.1.0 + linkify-it: 3.0.3 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: true + + /marked@4.3.0: + resolution: + { + integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==, + } + engines: { node: '>= 12' } + hasBin: true + dev: true + + /math-intrinsics@1.1.0: + resolution: + { + integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, + } + engines: { node: '>= 0.4' } + dev: true + + /mdurl@1.0.1: + resolution: + { + integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==, + } + dev: true + + /merge-stream@2.0.0: + resolution: + { + integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, + } + dev: true + + /micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: '>=8.6' } + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: + { + integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, + } + engines: { node: '>= 0.6' } + dev: true + + /mime-types@2.1.35: + resolution: + { + integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, + } + engines: { node: '>= 0.6' } + dependencies: + mime-db: 1.52.0 + dev: true + + /mime@1.6.0: + resolution: + { + integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==, + } + engines: { node: '>=4' } + hasBin: true + dev: true + + /mimic-fn@2.1.0: + resolution: + { + integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==, + } + engines: { node: '>=6' } + dev: true + + /minimatch@3.1.2: + resolution: + { + integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, + } + dependencies: + brace-expansion: 1.1.12 + dev: true + + /minimist@1.2.8: + resolution: + { + integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, + } + dev: true + + /mkdirp@1.0.4: + resolution: + { + integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==, + } + engines: { node: '>=10' } + hasBin: true + dev: true + + /ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + dev: true + + /natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + dev: true + + /node-int64@0.4.0: + resolution: + { + integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==, + } + dev: true + + /node-releases@2.0.19: + resolution: + { + integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==, + } + dev: true + + /normalize-path@3.0.0: + resolution: + { + integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, + } + engines: { node: '>=0.10.0' } + dev: true + + /npm-run-path@4.0.1: + resolution: + { + integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==, + } + engines: { node: '>=8' } + dependencies: + path-key: 3.1.1 + dev: true + + /nwsapi@2.2.21: + resolution: + { + integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==, + } + dev: true + + /object-inspect@1.13.4: + resolution: + { + integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, + } + engines: { node: '>= 0.4' } + dev: true + + /once@1.4.0: + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, + } + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: + { + integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==, + } + engines: { node: '>=6' } + dependencies: + mimic-fn: 2.1.0 + dev: true + + /opener@1.5.2: + resolution: + { + integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==, + } + hasBin: true + dev: true + + /p-limit@2.3.0: + resolution: + { + integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, + } + engines: { node: '>=6' } + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: '>=10' } + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: + { + integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, + } + engines: { node: '>=8' } + dependencies: + p-limit: 2.3.0 + dev: true + + /p-try@2.2.0: + resolution: + { + integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, + } + engines: { node: '>=6' } + dev: true + + /parse-json@5.2.0: + resolution: + { + integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, + } + engines: { node: '>=8' } + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parse5@6.0.1: + resolution: + { + integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==, + } + dev: true + + /path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: '>=8' } + dev: true + + /path-is-absolute@1.0.1: + resolution: + { + integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, + } + engines: { node: '>=0.10.0' } + dev: true + + /path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: '>=8' } + dev: true + + /path-parse@1.0.7: + resolution: + { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, + } + dev: true + + /perf-regexes@1.0.1: + resolution: + { + integrity: sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng==, + } + engines: { node: '>=6.14' } + dev: true + + /picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } + dev: true + + /picomatch@2.3.1: + resolution: + { + integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, + } + engines: { node: '>=8.6' } + dev: true + + /pirates@4.0.7: + resolution: + { + integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, + } + engines: { node: '>= 6' } + dev: true + + /pkg-dir@4.2.0: + resolution: + { + integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==, + } + engines: { node: '>=8' } + dependencies: + find-up: 4.1.0 + dev: true + + /portfinder@1.0.37: + resolution: + { + integrity: sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==, + } + engines: { node: '>= 10.12' } + dependencies: + async: 3.2.6 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + dev: true + + /prettier@2.6.2: + resolution: + { + integrity: sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==, + } + engines: { node: '>=10.13.0' } + hasBin: true + dev: true + + /pretty-format@28.1.3: + resolution: + { + integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==, + } + engines: { node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0 } + dependencies: + '@jest/schemas': 28.1.3 + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 18.3.1 + dev: true + + /prompts@2.4.2: + resolution: + { + integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==, + } + engines: { node: '>= 6' } + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /psl@1.15.0: + resolution: + { + integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==, + } + dependencies: + punycode: 2.3.1 + dev: true + + /punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: '>=6' } + dev: true + + /qs@6.14.0: + resolution: + { + integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==, + } + engines: { node: '>=0.6' } + dependencies: + side-channel: 1.1.0 + dev: true + + /querystringify@2.2.0: + resolution: + { + integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==, + } + dev: true + + /randombytes@2.1.0: + resolution: + { + integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==, + } + dependencies: + safe-buffer: 5.2.1 + dev: true + + /react-is@18.3.1: + resolution: + { + integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==, + } + dev: true + + /require-directory@2.1.1: + resolution: + { + integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, + } + engines: { node: '>=0.10.0' } + dev: true + + /requires-port@1.0.0: + resolution: + { + integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==, + } + dev: true + + /requizzle@0.2.4: + resolution: + { + integrity: sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==, + } + dependencies: + lodash: 4.17.21 + dev: true + + /resolve-cwd@3.0.0: + resolution: + { + integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==, + } + engines: { node: '>=8' } + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@5.0.0: + resolution: + { + integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, + } + engines: { node: '>=8' } + dev: true + + /resolve.exports@1.1.1: + resolution: + { + integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==, + } + engines: { node: '>=10' } + dev: true + + /resolve@1.22.10: + resolution: + { + integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==, + } + engines: { node: '>= 0.4' } + hasBin: true + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /rimraf@3.0.2: + resolution: + { + integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==, + } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup-plugin-cleanup@3.2.1(rollup@2.79.2): + resolution: + { + integrity: sha512-zuv8EhoO3TpnrU8MX8W7YxSbO4gmOR0ny06Lm3nkFfq0IVKdBUtHwhVzY1OAJyNCIAdLiyPnOrU0KnO0Fri1GQ==, + } + engines: { node: ^10.14.2 || >=12.0.0 } + peerDependencies: + rollup: '>=2.0' + dependencies: + js-cleanup: 1.2.0 + rollup: 2.79.2 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-pluginutils@2.8.2: + resolution: + { + integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==, + } + dependencies: + estree-walker: 0.6.1 + dev: true + + /rollup@2.79.2: + resolution: + { + integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==, + } + engines: { node: '>=10.0.0' } + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /safe-buffer@5.1.2: + resolution: + { + integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, + } + dev: true + + /safe-buffer@5.2.1: + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } + dev: true + + /safer-buffer@2.1.2: + resolution: + { + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, + } + dev: true + + /saxes@5.0.1: + resolution: + { + integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==, + } + engines: { node: '>=10' } + dependencies: + xmlchars: 2.2.0 + dev: true + + /secure-compare@3.0.1: + resolution: + { + integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==, + } + dev: true + + /semver@6.3.1: + resolution: + { + integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, + } + hasBin: true + dev: true + + /semver@7.7.2: + resolution: + { + integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==, + } + engines: { node: '>=10' } + hasBin: true + dev: true + + /serialize-javascript@6.0.2: + resolution: + { + integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==, + } + dependencies: + randombytes: 2.1.0 + dev: true + + /shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: '>=8' } + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: '>=8' } + dev: true + + /side-channel-list@1.0.0: + resolution: + { + integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==, + } + engines: { node: '>= 0.4' } + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + dev: true + + /side-channel-map@1.0.1: + resolution: + { + integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, + } + engines: { node: '>= 0.4' } + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + dev: true + + /side-channel-weakmap@1.0.2: + resolution: + { + integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, + } + engines: { node: '>= 0.4' } + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + dev: true + + /side-channel@1.1.0: + resolution: + { + integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==, + } + engines: { node: '>= 0.4' } + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + dev: true + + /signal-exit@3.0.7: + resolution: + { + integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, + } + dev: true + + /sisteransi@1.0.5: + resolution: + { + integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==, + } + dev: true + + /skip-regex@1.0.2: + resolution: + { + integrity: sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA==, + } + engines: { node: '>=4.2' } + dev: true + + /slash@3.0.0: + resolution: + { + integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, + } + engines: { node: '>=8' } + dev: true + + /smob@1.5.0: + resolution: + { + integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==, + } + dev: true + + /source-map-support@0.5.13: + resolution: + { + integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==, + } + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map-support@0.5.21: + resolution: + { + integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, + } + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: + { + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, + } + engines: { node: '>=0.10.0' } + dev: true + + /sourcemap-codec@1.4.8: + resolution: + { + integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==, + } + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + + /sprintf-js@1.0.3: + resolution: + { + integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==, + } + dev: true + + /stack-utils@2.0.6: + resolution: + { + integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==, + } + engines: { node: '>=10' } + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /string-length@4.0.2: + resolution: + { + integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==, + } + engines: { node: '>=10' } + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@4.2.3: + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, + } + engines: { node: '>=8' } + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /strip-ansi@6.0.1: + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, + } + engines: { node: '>=8' } + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@4.0.0: + resolution: + { + integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==, + } + engines: { node: '>=8' } + dev: true + + /strip-final-newline@2.0.0: + resolution: + { + integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==, + } + engines: { node: '>=6' } + dev: true + + /strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, + } + engines: { node: '>=8' } + dev: true + + /supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: '>=8' } + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color@8.1.1: + resolution: + { + integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==, + } + engines: { node: '>=10' } + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-hyperlinks@2.3.0: + resolution: + { + integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==, + } + engines: { node: '>=8' } + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: + { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, + } + engines: { node: '>= 0.4' } + dev: true + + /symbol-tree@3.2.4: + resolution: + { + integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, + } + dev: true + + /taffydb@2.6.2: + resolution: + { + integrity: sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==, + } + dev: true + + /terminal-link@2.1.1: + resolution: + { + integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==, + } + engines: { node: '>=8' } + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + dev: true + + /terser@5.43.1: + resolution: + { + integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==, + } + engines: { node: '>=10' } + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.10 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /test-exclude@6.0.0: + resolution: + { + integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==, + } + engines: { node: '>=8' } + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /tmpl@1.0.5: + resolution: + { + integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==, + } + dev: true + + /to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: '>=8.0' } + dependencies: + is-number: 7.0.0 + dev: true + + /tough-cookie@4.1.4: + resolution: + { + integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==, + } + engines: { node: '>=6' } + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + + /tr46@3.0.0: + resolution: + { + integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==, + } + engines: { node: '>=12' } + dependencies: + punycode: 2.3.1 + dev: true + + /tsd-jsdoc@2.5.0(jsdoc@3.6.11): + resolution: + { + integrity: sha512-80fcJLAiUeerg4xPftp+iEEKWUjJjHk9AvcHwJqA8Zw0R4oASdu3kT/plE/Zj19QUTz8KupyOX25zStlNJjS9g==, + } + peerDependencies: + jsdoc: ^3.6.3 + dependencies: + jsdoc: 3.6.11 + typescript: 3.9.10 + dev: true + + /type-detect@4.0.8: + resolution: + { + integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==, + } + engines: { node: '>=4' } + dev: true + + /type-fest@0.21.3: + resolution: + { + integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==, + } + engines: { node: '>=10' } + dev: true + + /typescript@3.9.10: + resolution: + { + integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==, + } + engines: { node: '>=4.2.0' } + hasBin: true + dev: true + + /typescript@5.9.2: + resolution: + { + integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==, + } + engines: { node: '>=14.17' } + hasBin: true + dev: true + + /uc.micro@1.0.6: + resolution: + { + integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==, + } + dev: true + + /underscore@1.13.7: + resolution: + { + integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==, + } + dev: true + + /undici-types@7.10.0: + resolution: + { + integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==, + } + dev: true + + /union@0.5.0: + resolution: + { + integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==, + } + engines: { node: '>= 0.8.0' } + dependencies: + qs: 6.14.0 + dev: true + + /universalify@0.2.0: + resolution: + { + integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==, + } + engines: { node: '>= 4.0.0' } + dev: true + + /update-browserslist-db@1.1.3(browserslist@4.25.2): + resolution: + { + integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==, + } + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.25.2 + escalade: 3.2.0 + picocolors: 1.1.1 + dev: true + + /url-join@4.0.1: + resolution: + { + integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==, + } + dev: true + + /url-parse@1.5.10: + resolution: + { + integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==, + } + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + + /v8-to-istanbul@9.3.0: + resolution: + { + integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==, + } + engines: { node: '>=10.12.0' } + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + dev: true + + /w3c-hr-time@1.0.2: + resolution: + { + integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==, + } + deprecated: Use your platform's native performance.now() and performance.timeOrigin. + dependencies: + browser-process-hrtime: 1.0.0 + dev: true + + /w3c-xmlserializer@3.0.0: + resolution: + { + integrity: sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==, + } + engines: { node: '>=12' } + dependencies: + xml-name-validator: 4.0.0 + dev: true + + /walker@1.0.8: + resolution: + { + integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, + } + dependencies: + makeerror: 1.0.12 + dev: true + + /webidl-conversions@7.0.0: + resolution: + { + integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, + } + engines: { node: '>=12' } + dev: true + + /whatwg-encoding@2.0.0: + resolution: + { + integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==, + } + engines: { node: '>=12' } + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype@3.0.0: + resolution: + { + integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==, + } + engines: { node: '>=12' } + dev: true + + /whatwg-url@10.0.0: + resolution: + { + integrity: sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==, + } + engines: { node: '>=12' } + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + + /whatwg-url@11.0.0: + resolution: + { + integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==, + } + engines: { node: '>=12' } + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + + /which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: '>= 8' } + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, + } + engines: { node: '>=10' } + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, + } + dev: true + + /write-file-atomic@4.0.2: + resolution: + { + integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==, + } + engines: { node: ^12.13.0 || ^14.15.0 || >=16.0.0 } + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /ws@8.18.3: + resolution: + { + integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==, + } + engines: { node: '>=10.0.0' } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /xml-name-validator@4.0.0: + resolution: + { + integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==, + } + engines: { node: '>=12' } + dev: true + + /xmlchars@2.2.0: + resolution: + { + integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, + } + dev: true + + /xmlcreate@2.0.4: + resolution: + { + integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==, + } + dev: true + + /y18n@5.0.8: + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, + } + engines: { node: '>=10' } + dev: true + + /yallist@3.1.1: + resolution: + { + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, + } + dev: true + + /yargs-parser@21.1.1: + resolution: + { + integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, + } + engines: { node: '>=12' } + dev: true + + /yargs@17.7.2: + resolution: + { + integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, + } + engines: { node: '>=12' } + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: '>=10' } + dev: true diff --git a/src/jsmind.common.js b/src/jsmind.common.js index e52468c2..895b7e9d 100644 --- a/src/jsmind.common.js +++ b/src/jsmind.common.js @@ -6,7 +6,15 @@ * https://github.com/hizzgdev/jsmind/ */ +/** + * Library version string. + * @type {string} + */ export const __version__ = '0.8.7'; +/** + * Library author. + * @type {string} + */ export const __author__ = 'hizzgdev@163.com'; if (typeof String.prototype.startsWith != 'function') { @@ -15,6 +23,11 @@ if (typeof String.prototype.startsWith != 'function') { }; } +/** + * Direction constants and parser. + * @typedef {{left:number,center:number,right:number,of:(dir:(string|number))=>number|undefined}} DirectionType + */ +/** @type {DirectionType} */ export const Direction = { left: -1, center: 0, @@ -37,12 +50,19 @@ export const Direction = { } }, }; +/** @enum {number} */ export const EventType = { show: 1, resize: 2, edit: 3, select: 4 }; +/** @enum {number} */ export const Key = { meta: 1 << 13, ctrl: 1 << 12, alt: 1 << 11, shift: 1 << 10 }; +/** @enum {number} */ export const LogLevel = { debug: 1, info: 2, warn: 3, error: 4, disable: 9 }; // an noop function define var _noop = function () {}; +/** + * Logger facade with dynamic level. + * @type {{level:(lvl:number)=>void,log:Function,debug:Function,info:Function,warn:Function,error:Function}} + */ export let logger = typeof console === 'undefined' ? { @@ -62,6 +82,10 @@ export let logger = error: console.error, }; +/** + * Set logger level. + * @param {number} log_level + */ function setup_logger_level(log_level) { if (log_level > LogLevel.debug) { logger.debug = _noop; diff --git a/src/jsmind.data_provider.js b/src/jsmind.data_provider.js index 78019c05..627f5345 100644 --- a/src/jsmind.data_provider.js +++ b/src/jsmind.data_provider.js @@ -10,16 +10,27 @@ import { logger } from './jsmind.common.js'; import { format } from './jsmind.format.js'; export class DataProvider { + /** + * Data provider: loads and serializes mind data by format. + * @param {import('./jsmind.js').default} jm - jsMind instance + */ constructor(jm) { this.jm = jm; } + /** Initialize data provider. */ init() { logger.debug('data.init'); } + /** Reset data provider state. */ reset() { logger.debug('data.reset'); } + /** + * Load a Mind from mixed source. + * @param {import('./jsmind.format.js').NodeTreeFormat|import('./jsmind.format.js').NodeArrayFormat|{meta?:{name:string,author:string,version:string},format:'freemind',data:string}|{meta?:{name:string,author:string,version:string},format:'text',data:string}} mind_data - object with {format,data} or a format-specific payload + * @returns {import('./jsmind.mind.js').Mind|null} + */ load(mind_data) { var df = null; var mind = null; @@ -46,6 +57,11 @@ export class DataProvider { } return mind; } + /** + * Serialize current mind to target format. + * @param {'node_tree'|'node_array'|'freemind'|'text'} data_format + * @returns {import('./jsmind.format.js').NodeTreeFormat|import('./jsmind.format.js').NodeArrayFormat|{meta:{name:string,author:string,version:string},format:'freemind',data:string}|{meta:{name:string,author:string,version:string},format:'text',data:string}} + */ get_data(data_format) { var data = null; if (data_format == 'node_array') { diff --git a/src/jsmind.dom.js b/src/jsmind.dom.js index 32f56244..c29d4dd3 100644 --- a/src/jsmind.dom.js +++ b/src/jsmind.dom.js @@ -6,16 +6,39 @@ * https://github.com/hizzgdev/jsmind/ */ +/** + * Lightweight DOM helpers bound to a window. + */ class Dom { + /** + * @param {Window} w + */ constructor(w) { + /** @type {Window} */ this.w = w; + /** @type {Document} */ this.d = w.document; + /** + * Get element by id. + * @param {string} id + * @returns {HTMLElement|null} + */ this.g = function (id) { return this.d.getElementById(id); }; + /** + * Create element with given tag. + * @param {string} tag + * @returns {HTMLElement} + */ this.c = function (tag) { return this.d.createElement(tag); }; + /** + * Set text content for element. + * @param {HTMLElement} n + * @param {string} t + */ this.t = function (n, t) { if (n.hasChildNodes()) { n.firstChild.nodeValue = t; @@ -24,6 +47,11 @@ class Dom { } }; + /** + * Set inner HTML or append element. + * @param {HTMLElement} n + * @param {string|HTMLElement} t + */ this.h = function (n, t) { if (t instanceof HTMLElement) { n.innerHTML = ''; @@ -33,6 +61,11 @@ class Dom { } }; // detect isElement + /** + * Runtime check for HTMLElement. + * @param {unknown} el + * @returns {el is HTMLElement} + */ this.i = function (el) { return ( !!el && @@ -44,6 +77,12 @@ class Dom { }; //target,eventType,handler + /** + * Add event listener with legacy fallback. + * @param {HTMLElement} t + * @param {string} e + * @param {(ev:Event)=>void} h + */ this.on = function (t, e, h) { if (!!t.addEventListener) { t.addEventListener(e, h, false); diff --git a/src/jsmind.format.js b/src/jsmind.format.js index b26dc553..d077883a 100644 --- a/src/jsmind.format.js +++ b/src/jsmind.format.js @@ -11,8 +11,58 @@ import { Mind } from './jsmind.mind.js'; import { Node } from './jsmind.node.js'; import { util } from './jsmind.util.js'; +/** @typedef {{name:string,author:string,version:string}} MindMapMeta */ +/** + * Node tree data item + * @typedef {{ + * id: string, + * topic: string, + * data?: Record, + * direction?: (number|string), + * expanded?: boolean, + * children?: NodeTreeData[] + * }} NodeTreeData + */ +/** + * Node tree formatted payload + * @typedef {{ + * meta?: MindMapMeta, + * format: 'node_tree', + * data: NodeTreeData + * }} NodeTreeFormat + */ +/** + * Node array data item + * @typedef {{ + * id: string, + * topic: string, + * parentid?: string, + * data?: Record, + * direction?: (number|string), + * expanded?: boolean, + * isroot?: boolean + * }} NodeArrayItem + */ +/** + * Node array formatted payload + * @typedef {{ + * meta?: MindMapMeta, + * format: 'node_array', + * data: NodeArrayItem[] + * }} NodeArrayFormat + */ +/** @type {MindMapMeta} */ const DEFAULT_META = { name: 'jsMind', author: __author__, version: __version__ }; +/** + * Mind data format handlers. + * @type {{ + * node_tree: { example:NodeTreeFormat, get_mind:(src:NodeTreeFormat)=>Mind, get_data:(mind:Mind)=>NodeTreeFormat }, + * node_array: { example:NodeArrayFormat, get_mind:(src:NodeArrayFormat)=>Mind, get_data:(mind:Mind)=>NodeArrayFormat }, + * freemind: { example:{meta:MindMapMeta,format:'freemind',data:string}, get_mind:(src:{meta?:MindMapMeta,format:'freemind',data:string})=>Mind, get_data:(mind:Mind)=>{meta:MindMapMeta,format:'freemind',data:string} }, + * text: { example:{meta:MindMapMeta,format:'text',data:string}, get_mind:(src:{meta?:MindMapMeta,format:'text',data:string})=>Mind, get_data:(mind:Mind)=>{meta:MindMapMeta,format:'text',data:string} } + * }} + */ export const format = { node_tree: { example: { @@ -20,6 +70,7 @@ export const format = { format: 'node_tree', data: { id: 'root', topic: 'jsMind node_tree example' }, }, + /** @param {NodeTreeFormat} source @returns {Mind} */ get_mind: function (source) { var df = format.node_tree; var mind = new Mind(); @@ -29,6 +80,7 @@ export const format = { df._parse(mind, source.data); return mind; }, + /** @param {Mind} mind */ get_data: function (mind) { var df = format.node_tree; var json = {}; @@ -42,6 +94,7 @@ export const format = { return json; }, + /** @param {Mind} mind @param {NodeTreeData} node_root */ _parse: function (mind, node_root) { var df = format.node_tree; var data = df._extract_data(node_root); @@ -54,6 +107,12 @@ export const format = { } }, + /** + * Extract custom data from node JSON, excluding standard properties. + * @private + * @param {Record} node_json - Node JSON object + * @returns {Record} Custom data object + */ _extract_data: function (node_json) { var data = {}; for (var k in node_json) { @@ -71,6 +130,7 @@ export const format = { return data; }, + /** @param {Mind} mind @param {Node} node_parent @param {NodeTreeData} node_json */ _extract_subnode: function (mind, node_parent, node_json) { var df = format.node_tree; var data = df._extract_data(node_json); @@ -94,6 +154,12 @@ export const format = { } }, + /** + * Build JSON object from a node. + * @private + * @param {Node} node - Node to convert + * @returns {NodeTreeData} JSON representation of node + */ _build_node: function (node) { var df = format.node_tree; if (!(node instanceof Node)) { @@ -131,6 +197,7 @@ export const format = { data: [{ id: 'root', topic: 'jsMind node_array example', isroot: true }], }, + /** @param {NodeArrayFormat} source @returns {Mind} */ get_mind: function (source) { var df = format.node_array; var mind = new Mind(); @@ -141,6 +208,7 @@ export const format = { return mind; }, + /** @param {Mind} mind */ get_data: function (mind) { var df = format.node_array; var json = {}; @@ -155,6 +223,7 @@ export const format = { return json; }, + /** @param {Mind} mind @param {NodeArrayItem[]} node_array */ _parse: function (mind, node_array) { var df = format.node_array; var nodes = node_array.slice(0); @@ -168,6 +237,7 @@ export const format = { } }, + /** @param {Mind} mind @param {NodeArrayItem[]} node_array */ _extract_root: function (mind, node_array) { var df = format.node_array; var i = node_array.length; @@ -183,6 +253,7 @@ export const format = { return null; }, + /** @param {Mind} mind @param {Node} parent_node @param {NodeArrayItem[]} node_array */ _extract_subnode: function (mind, parent_node, node_array) { var df = format.node_array; var i = node_array.length; @@ -219,6 +290,7 @@ export const format = { return extract_count; }, + /** @param {Record} node_json */ _extract_data: function (node_json) { var data = {}; for (var k in node_json) { @@ -237,11 +309,13 @@ export const format = { return data; }, + /** @param {Mind} mind @param {NodeArrayItem[]} node_array */ _array: function (mind, node_array) { var df = format.node_array; df._array_node(mind.root, node_array); }, + /** @param {Node} node @param {NodeArrayItem[]} node_array */ _array_node: function (node, node_array) { var df = format.node_array; if (!(node instanceof Node)) { @@ -281,6 +355,7 @@ export const format = { format: 'freemind', data: '', }, + /** @param {{meta:MindMapMeta,data:string}} source @returns {Mind} */ get_mind: function (source) { var df = format.freemind; var mind = new Mind(); @@ -294,6 +369,7 @@ export const format = { return mind; }, + /** @param {Mind} mind */ get_data: function (mind) { var df = format.freemind; var json = {}; @@ -311,6 +387,7 @@ export const format = { return json; }, + /** @param {string} xml */ _parse_xml: function (xml) { var xml_doc = null; if (window.DOMParser) { @@ -325,6 +402,7 @@ export const format = { return xml_doc; }, + /** @param {Document} xml_doc */ _find_root: function (xml_doc) { var nodes = xml_doc.childNodes; var node = null; @@ -351,6 +429,7 @@ export const format = { return node; }, + /** @param {Mind} mind @param {Node|null} parent_node @param {Element} xml_node */ _load_node: function (mind, parent_node, xml_node) { var df = format.freemind; var node_id = xml_node.getAttribute('ID'); @@ -411,6 +490,7 @@ export const format = { } }, + /** @param {Element} xml_node */ _load_attributes: function (xml_node) { var children = xml_node.childNodes; var attr = null; @@ -424,6 +504,7 @@ export const format = { return attr_data; }, + /** @param {Node} node @param {string[]} xml_lines */ _build_map: function (node, xml_lines) { var df = format.freemind; var pos = null; @@ -467,6 +548,7 @@ export const format = { xml_lines.push(''); }, + /** @param {string} text */ _escape: function (text) { return text .replace(/&/g, '&') @@ -483,6 +565,7 @@ export const format = { data: 'jsMind text example\n node1\n node1-sub\n node1-sub\n node2', }, _line_regex: /\s*/, + /** @param {{meta:MindMapMeta,data:string}} source @returns {Mind} */ get_mind: function (source) { var df = format.text; var mind = new Mind(); @@ -494,6 +577,7 @@ export const format = { return mind; }, + /** @param {Mind} mind @param {string[]} lines */ _fill_nodes: function (mind, lines) { let node_path = []; let i = 0; @@ -533,6 +617,7 @@ export const format = { node_path.length = 0; }, + /** @param {Mind} mind */ get_data: function (mind) { var df = format.text; var json = {}; @@ -548,6 +633,7 @@ export const format = { return json; }, + /** @param {string[]} lines @param {Node[]} nodes @param {number} level */ _build_lines: function (lines, nodes, level) { let prefix = new Array(level + 1).join(' '); for (let node of nodes) { diff --git a/src/jsmind.graph.js b/src/jsmind.graph.js index 5914f7fb..0c91ca81 100644 --- a/src/jsmind.graph.js +++ b/src/jsmind.graph.js @@ -9,13 +9,21 @@ import { $ } from './jsmind.dom.js'; import { logger } from './jsmind.common.js'; +/** + * SVG-based graph renderer. + */ class SvgGraph { + /** + * Create SVG graph renderer. + * @param {import('./jsmind.view_provider.js').ViewProvider} view - View provider instance + */ constructor(view) { this.view = view; this.opts = view.opts; this.e_svg = SvgGraph.c('svg'); this.e_svg.setAttribute('class', 'jsmind'); this.size = { w: 0, h: 0 }; + /** @type {SVGPathElement[]} */ this.lines = []; this.line_drawing = { straight: this._line_to, @@ -23,11 +31,14 @@ class SvgGraph { }; this.init_line_render(); } + /** @param {string} tag */ static c(tag) { return $.d.createElementNS('http://www.w3.org/2000/svg', tag); } + /** Choose line drawing renderer. */ init_line_render() { if (typeof this.opts.custom_line_render === 'function') { + /** @type {(path:SVGPathElement,x1:number,y1:number,x2:number,y2:number)=>void} */ this.drawing = (path, x1, y1, x2, y2) => { try { this.opts.custom_line_render.call(this, { @@ -40,12 +51,15 @@ class SvgGraph { } }; } else { + /** @type {(path:SVGPathElement,x1:number,y1:number,x2:number,y2:number)=>void} */ this.drawing = this.line_drawing[this.opts.line_style] || this.line_drawing.curved; } } + /** @returns {SVGSVGElement} */ element() { return this.e_svg; } + /** @param {number} w @param {number} h */ set_size(w, h) { this.size.w = w; this.size.h = h; @@ -59,6 +73,7 @@ class SvgGraph { } this.lines.length = 0; } + /** @param {{x:number,y:number}} pout @param {{x:number,y:number}} pin @param {{x:number,y:number}} offset @param {string=} color */ draw_line(pout, pin, offset, color) { var line = SvgGraph.c('path'); line.setAttribute('stroke', color || this.opts.line_color); @@ -75,6 +90,7 @@ class SvgGraph { ); } + /** @param {CanvasRenderingContext2D} dest_canvas_ctx @param {(()=>void)=} callback */ copy_to(dest_canvas_ctx, callback) { var img = new Image(); img.onload = function () { @@ -84,6 +100,15 @@ class SvgGraph { img.src = 'data:image/svg+xml;base64,' + btoa(new XMLSerializer().serializeToString(this.e_svg)); } + /** + * Draw bezier curve to SVG path. + * @internal + * @param {SVGPathElement} path - SVG path element + * @param {number} x1 - Start x coordinate + * @param {number} y1 - Start y coordinate + * @param {number} x2 - End x coordinate + * @param {number} y2 - End y coordinate + */ _bezier_to(path, x1, y1, x2, y2) { path.setAttribute( 'd', @@ -105,17 +130,33 @@ class SvgGraph { y2 ); } + /** + * Draw straight line to SVG path. + * @internal + * @param {SVGPathElement} path - SVG path element + * @param {number} x1 - Start x coordinate + * @param {number} y1 - Start y coordinate + * @param {number} x2 - End x coordinate + * @param {number} y2 - End y coordinate + */ _line_to(path, x1, y1, x2, y2) { path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2); } } +/** + * Canvas-based graph renderer. + */ class CanvasGraph { + /** + * Create canvas graph renderer. + * @param {import('./jsmind.view_provider.js').ViewProvider} view - View provider instance + */ constructor(view) { this.opts = view.opts; this.e_canvas = $.c('canvas'); this.e_canvas.className = 'jsmind'; - this.canvas_ctx = this.e_canvas.getContext('2d'); + this.canvas_ctx = /** @type {CanvasRenderingContext2D} */ (this.e_canvas.getContext('2d')); this.size = { w: 0, h: 0 }; this.line_drawing = { straight: this._line_to, @@ -124,8 +165,10 @@ class CanvasGraph { this.dpr = view.device_pixel_ratio; this.init_line_render(); } + /** Choose line drawing renderer. */ init_line_render() { if (typeof this.opts.custom_line_render === 'function') { + /** @type {(ctx:CanvasRenderingContext2D,x1:number,y1:number,x2:number,y2:number)=>void} */ this.drawing = (ctx, x1, y1, x2, y2) => { try { this.opts.custom_line_render.call(this, { @@ -138,12 +181,15 @@ class CanvasGraph { } }; } else { + /** @type {(ctx:CanvasRenderingContext2D,x1:number,y1:number,x2:number,y2:number)=>void} */ this.drawing = this.line_drawing[this.opts.line_style] || this.line_drawing.curved; } } + /** @returns {HTMLCanvasElement} */ element() { return this.e_canvas; } + /** @param {number} w @param {number} h */ set_size(w, h) { this.size.w = w; this.size.h = h; @@ -160,9 +206,11 @@ class CanvasGraph { } } + /** Clear the canvas. */ clear() { this.canvas_ctx.clearRect(0, 0, this.size.w, this.size.h); } + /** @param {{x:number,y:number}} pout @param {{x:number,y:number}} pin @param {{x:number,y:number}} offset @param {string=} color */ draw_line(pout, pin, offset, color) { var ctx = this.canvas_ctx; ctx.strokeStyle = color || this.opts.line_color; @@ -170,16 +218,35 @@ class CanvasGraph { ctx.lineCap = 'round'; this.drawing(ctx, pin.x + offset.x, pin.y + offset.y, pout.x + offset.x, pout.y + offset.y); } + /** @param {CanvasRenderingContext2D} dest_canvas_ctx @param {(()=>void)=} callback */ copy_to(dest_canvas_ctx, callback) { dest_canvas_ctx.drawImage(this.e_canvas, 0, 0, this.size.w, this.size.h); !!callback && callback(); } + /** + * Draw bezier curve on canvas. + * @internal + * @param {CanvasRenderingContext2D} ctx - Canvas context + * @param {number} x1 - Start x coordinate + * @param {number} y1 - Start y coordinate + * @param {number} x2 - End x coordinate + * @param {number} y2 - End y coordinate + */ _bezier_to(ctx, x1, y1, x2, y2) { ctx.beginPath(); ctx.moveTo(x1, y1); ctx.bezierCurveTo(x1 + ((x2 - x1) * 2) / 3, y1, x1, y2, x2, y2); ctx.stroke(); } + /** + * Draw straight line on canvas. + * @internal + * @param {CanvasRenderingContext2D} ctx - Canvas context + * @param {number} x1 - Start x coordinate + * @param {number} y1 - Start y coordinate + * @param {number} x2 - End x coordinate + * @param {number} y2 - End y coordinate + */ _line_to(ctx, x1, y1, x2, y2) { ctx.beginPath(); ctx.moveTo(x1, y1); @@ -188,6 +255,12 @@ class CanvasGraph { } } +/** + * Initialize graph renderer based on engine type. + * @param {import('./jsmind.view_provider.js').ViewProvider} view - View provider instance + * @param {'canvas'|'svg'} engine - Rendering engine type + * @returns {SvgGraph|CanvasGraph} Graph renderer instance + */ export function init_graph(view, engine) { return engine.toLowerCase() === 'svg' ? new SvgGraph(view) : new CanvasGraph(view); } diff --git a/src/jsmind.js b/src/jsmind.js index 9f7d88df..c0819658 100644 --- a/src/jsmind.js +++ b/src/jsmind.js @@ -19,6 +19,14 @@ import { format } from './jsmind.format.js'; import { $ } from './jsmind.dom.js'; import { util as _util } from './jsmind.util.js'; +/** + * Event callback payload + * @typedef {{ evt?: string, data?: unknown[], node?: string }} EventData + */ + +/** + * jsMind runtime: orchestrates data/layout/view/shortcut and exposes public API. + */ export default class jsMind { static mind = Mind; static node = Node; @@ -29,6 +37,10 @@ export default class jsMind { static register_plugin = _register_plugin; static util = _util; + /** + * Create a jsMind instance. + * @param {import('./jsmind.option.js').JsMindRuntimeOptions} options + */ constructor(options) { jsMind.current = this; this.options = merge_option(options); @@ -36,10 +48,12 @@ export default class jsMind { this.version = __version__; this.initialized = false; this.mind = null; + /** @type {Array<(type: number, data: EventData) => void>} */ this.event_handles = []; this.init(); } + /** Initialize sub-systems and plugins. */ init() { if (!!this.initialized) { return; @@ -85,34 +99,52 @@ export default class jsMind { apply_plugins(this, this.options.plugin); } + /** @returns {boolean} whether current mind map is editable */ get_editable() { return this.options.editable; } + /** enable editing */ enable_edit() { this.options.editable = true; } + /** disable editing */ disable_edit() { this.options.editable = false; } + /** @returns {boolean} whether view is draggable */ get_view_draggable() { return this.options.view.draggable; } + /** enable view dragging */ enable_view_draggable() { this.options.view.draggable = true; this.view.setup_canvas_draggable(true); } + /** disable view dragging */ disable_view_draggable() { this.options.view.draggable = false; this.view.setup_canvas_draggable(false); } // options are 'mousedown', 'click', 'dblclick', 'mousewheel' + /** + * Enable default event handle. + * @param {'mousedown'|'click'|'dblclick'|'mousewheel'} event_handle + */ enable_event_handle(event_handle) { this.options.default_event_handle['enable_' + event_handle + '_handle'] = true; } // options are 'mousedown', 'click', 'dblclick', 'mousewheel' + /** + * Disable default event handle. + * @param {'mousedown'|'click'|'dblclick'|'mousewheel'} event_handle + */ disable_event_handle(event_handle) { this.options.default_event_handle['enable_' + event_handle + '_handle'] = false; } + /** + * Set theme name. + * @param {string|null} theme + */ set_theme(theme) { var theme_old = this.options.theme; this.options.theme = !!theme ? theme : null; @@ -121,12 +153,14 @@ export default class jsMind { this.view.reset_custom_style(); } } + /** bind internal DOM events */ _event_bind() { this.view.add_event(this, 'mousedown', this.mousedown_handle); this.view.add_event(this, 'click', this.click_handle); this.view.add_event(this, 'dblclick', this.dblclick_handle); this.view.add_event(this, 'wheel', this.mousewheel_handle, true); } + /** @param {MouseEvent} e */ mousedown_handle(e) { if (!this.options.default_event_handle['enable_mousedown_handle']) { return; @@ -141,6 +175,7 @@ export default class jsMind { this.select_clear(); } } + /** @param {MouseEvent} e */ click_handle(e) { if (!this.options.default_event_handle['enable_click_handle']) { return; @@ -154,6 +189,7 @@ export default class jsMind { } } } + /** @param {MouseEvent} e */ dblclick_handle(e) { if (!this.options.default_event_handle['enable_dblclick_handle']) { return; @@ -170,6 +206,7 @@ export default class jsMind { } } // Use [Ctrl] + Mousewheel, to zoom in/out. + /** @param {WheelEvent} e */ mousewheel_handle(e) { // Test if mousewheel option is enabled and Ctrl key is pressed. var kc = (e.metaKey << 13) + (e.ctrlKey << 12) + (e.altKey << 11) + (e.shiftKey << 10); @@ -189,6 +226,11 @@ export default class jsMind { this.view.zoom_out(evt); } } + /** + * Begin editing a node. + * @param {string | import('./jsmind.node.js').Node} node + * @returns {boolean|void} + */ begin_edit(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -206,9 +248,15 @@ export default class jsMind { return; } } + /** End editing */ end_edit() { this.view.edit_node_end(); } + /** + * Toggle a node's expanded state. + * @param {string | import('./jsmind.node.js').Node} node + * @returns {void} + */ toggle_node(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -216,7 +264,8 @@ export default class jsMind { logger.error('the node[id=' + node + '] can not be found.'); return; } else { - return this.toggle_node(the_node); + this.toggle_node(the_node); + return; } } if (node.isroot) { @@ -227,6 +276,11 @@ export default class jsMind { this.view.relayout(); this.view.restore_location(node); } + /** + * Expand a node. + * @param {string | import('./jsmind.node.js').Node} node + * @returns {void} + */ expand_node(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -234,7 +288,8 @@ export default class jsMind { logger.error('the node[id=' + node + '] can not be found.'); return; } else { - return this.expand_node(the_node); + this.expand_node(the_node); + return; } } if (node.isroot) { @@ -245,6 +300,11 @@ export default class jsMind { this.view.relayout(); this.view.restore_location(node); } + /** + * Collapse a node. + * @param {string | import('./jsmind.node.js').Node} node + * @returns {void} + */ collapse_node(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -252,7 +312,8 @@ export default class jsMind { logger.error('the node[id=' + node + '] can not be found.'); return; } else { - return this.collapse_node(the_node); + this.collapse_node(the_node); + return; } } if (node.isroot) { @@ -263,23 +324,32 @@ export default class jsMind { this.view.relayout(); this.view.restore_location(node); } + /** Expand all nodes */ expand_all() { this.layout.expand_all(); this.view.relayout(); } + /** Collapse all nodes */ collapse_all() { this.layout.collapse_all(); this.view.relayout(); } + /** @param {number} depth */ expand_to_depth(depth) { this.layout.expand_to_depth(depth); this.view.relayout(); } + /** reset view/layout/data */ _reset() { this.view.reset(); this.layout.reset(); this.data.reset(); } + /** + * Internal show flow. + * @param {object | null} mind + * @param {boolean=} skip_centering + */ _show(mind, skip_centering) { var m = mind || format.node_array.example; this.mind = this.data.load(m); @@ -301,10 +371,16 @@ export default class jsMind { this.invoke_event_handle(EventType.show, { data: [mind] }); } + /** + * Show a mind (or example) on the canvas. + * @param {object | null} mind + * @param {boolean=} skip_centering + */ show(mind, skip_centering) { this._reset(); this._show(mind, skip_centering); } + /** @returns {{name:string,author:string,version:string}} */ get_meta() { return { name: this.mind.name, @@ -312,19 +388,38 @@ export default class jsMind { version: this.mind.version, }; } + /** + * Serialize current mind to given format. + * @param {'node_tree'|'node_array'|'freemind'|'text'} [data_format] + * @returns {object} + */ get_data(data_format) { var df = data_format || 'node_tree'; return this.data.get_data(df); } + /** @returns {import('./jsmind.node.js').Node} */ get_root() { return this.mind.root; } + /** + * @param {string | import('./jsmind.node.js').Node} node + * @returns {import('./jsmind.node.js').Node} + */ get_node(node) { if (Node.is_node(node)) { return node; } return this.mind.get_node(node); } + /** + * Add a node under parent. + * @param {string | import('./jsmind.node.js').Node} parent_node + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @param {number=} direction + * @returns {import('./jsmind.node.js').Node|null} + */ add_node(parent_node, node_id, topic, data, direction) { if (this.get_editable()) { var the_parent_node = this.get_node(parent_node); @@ -351,6 +446,15 @@ export default class jsMind { return null; } } + /** + * Insert a node before target node. + * @param {string | import('./jsmind.node.js').Node} node_before + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @param {number=} direction + * @returns {import('./jsmind.node.js').Node|null} + */ insert_node_before(node_before, node_id, topic, data, direction) { if (this.get_editable()) { var the_node_before = this.get_node(node_before); @@ -375,6 +479,15 @@ export default class jsMind { return null; } } + /** + * Insert a node after target node. + * @param {string | import('./jsmind.node.js').Node} node_after + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @param {number=} direction + * @returns {import('./jsmind.node.js').Node|null} + */ insert_node_after(node_after, node_id, topic, data, direction) { if (this.get_editable()) { var the_node_after = this.get_node(node_after); @@ -399,6 +512,11 @@ export default class jsMind { return null; } } + /** + * Remove a node. + * @param {string | import('./jsmind.node.js').Node} node + * @returns {boolean} + */ remove_node(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -434,6 +552,7 @@ export default class jsMind { return false; } } + /** @param {string} node_id @param {string} topic */ update_node(node_id, topic) { if (this.get_editable()) { if (_util.text.is_empty(topic)) { @@ -462,6 +581,13 @@ export default class jsMind { return; } } + /** + * Move a node and optionally change direction. + * @param {string} node_id + * @param {string=} before_id + * @param {string=} parent_id + * @param {number=} direction + */ move_node(node_id, before_id, parent_id, direction) { if (this.get_editable()) { var node = this.get_node(node_id); @@ -481,6 +607,10 @@ export default class jsMind { return; } } + /** + * @param {string | import('./jsmind.node.js').Node} node + * @returns {void} + */ select_node(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -488,7 +618,8 @@ export default class jsMind { logger.error('the node[id=' + node + '] can not be found.'); return; } else { - return this.select_node(the_node); + this.select_node(the_node); + return; } } if (!this.layout.is_visible(node)) { @@ -498,6 +629,7 @@ export default class jsMind { this.view.select_node(node); this.invoke_event_handle(EventType.select, { evt: 'select_node', data: [], node: node.id }); } + /** @returns {import('./jsmind.node.js').Node|null} */ get_selected_node() { if (!!this.mind) { return this.mind.selected; @@ -505,15 +637,18 @@ export default class jsMind { return null; } } + /** clear selection */ select_clear() { if (!!this.mind) { this.mind.selected = null; this.view.select_clear(); } } + /** @param {string | import('./jsmind.node.js').Node} node */ is_node_visible(node) { return this.layout.is_visible(node); } + /** @param {string | import('./jsmind.node.js').Node} node */ scroll_node_to_center(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -526,6 +661,12 @@ export default class jsMind { } this.view.center_node(node); } + /** + * Find the previous sibling node of the given node. + * + * @param {string | import('./jsmind.node.js').Node} node - Node id or Node instance + * @returns {import('./jsmind.node.js').Node | null} + */ find_node_before(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -558,6 +699,11 @@ export default class jsMind { } return n; } + /** + * Find the next sibling node of the given node. + * @param {string | import('./jsmind.node.js').Node} node + * @returns {import('./jsmind.node.js').Node | null} + */ find_node_after(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -593,6 +739,12 @@ export default class jsMind { } return n; } + /** + * @param {string} node_id + * @param {string=} bg_color + * @param {string=} fg_color + * @returns {void} + */ set_node_color(node_id, bg_color, fg_color) { if (this.get_editable()) { var node = this.mind.get_node(node_id); @@ -610,6 +762,13 @@ export default class jsMind { return null; } } + /** + * @param {string} node_id + * @param {number=} size + * @param {string=} weight + * @param {string=} style + * @returns {void} + */ set_node_font_style(node_id, size, weight, style) { if (this.get_editable()) { var node = this.mind.get_node(node_id); @@ -633,6 +792,14 @@ export default class jsMind { return null; } } + /** + * @param {string} node_id + * @param {string} image + * @param {number=} width + * @param {number=} height + * @param {number=} rotation + * @returns {void} + */ set_node_background_image(node_id, image, width, height, rotation) { if (this.get_editable()) { var node = this.mind.get_node(node_id); @@ -659,6 +826,11 @@ export default class jsMind { return null; } } + /** + * @param {string} node_id + * @param {number} rotation + * @returns {void} + */ set_node_background_rotation(node_id, rotation) { if (this.get_editable()) { var node = this.mind.get_node(node_id); @@ -680,24 +852,29 @@ export default class jsMind { return null; } } + /** trigger view resize */ resize() { this.view.resize(); } // callback(type ,data) + /** @param {(type:number, data: EventData)=>void} callback */ add_event_listener(callback) { if (typeof callback === 'function') { this.event_handles.push(callback); } } + /** clear event listeners */ clear_event_listener() { this.event_handles = []; } + /** @param {number} type @param {EventData} data */ invoke_event_handle(type, data) { var j = this; $.w.setTimeout(function () { j._invoke_event_handle(type, data); }, 0); } + /** @param {number} type @param {EventData} data */ _invoke_event_handle(type, data) { var l = this.event_handles.length; for (var i = 0; i < l; i++) { @@ -705,6 +882,12 @@ export default class jsMind { } } + /** + * Deprecated: static show constructor helper. + * @param {import('./jsmind.option.js').JsMindRuntimeOptions} options + * @param {object | null} mind + * @returns {jsMind} + */ static show(options, mind) { logger.warn( '`jsMind.show(options, mind)` is deprecated, please use `jm = new jsMind(options); jm.show(mind);` instead' diff --git a/src/jsmind.layout_provider.js b/src/jsmind.layout_provider.js index dc06fa74..04b63833 100644 --- a/src/jsmind.layout_provider.js +++ b/src/jsmind.layout_provider.js @@ -8,6 +8,11 @@ import { logger, Direction, EventType } from './jsmind.common.js'; export class LayoutProvider { + /** + * Layout engine for positioning nodes and lines. + * @param {import('./jsmind.js').default} jm - jsMind instance + * @param {{mode:'full'|'side', hspace:number, vspace:number, pspace:number, cousin_space:number}} options - Layout configuration options + */ constructor(jm, options) { this.opts = options; this.jm = jm; @@ -16,13 +21,20 @@ export class LayoutProvider { this.cache_valid = false; } + /** Initialize layout provider. */ init() { logger.debug('layout.init'); } + /** Reset layout state and bounds. */ reset() { logger.debug('layout.reset'); this.bounds = { n: 0, s: 0, w: 0, e: 0 }; } + /** + * Decide the next child's direction for a parent node. + * @param {import('./jsmind.node.js').Node} node + * @returns {number} + */ calculate_next_child_direction(node) { if (this.isside) { return Direction.right; @@ -39,14 +51,20 @@ export class LayoutProvider { } return children_len > 1 && r > 0 ? Direction.left : Direction.right; } + /** Perform layout and offsets recalculation. */ layout() { logger.debug('layout.layout'); this.layout_direction(); this.layout_offset(); } + /** Calculate and set direction for all nodes. */ layout_direction() { this._layout_direction_root(); } + /** + * Set direction layout for root node and its children. + * @private + */ _layout_direction_root() { var node = this.jm.mind.root; var layout_data = null; @@ -78,6 +96,13 @@ export class LayoutProvider { } } } + /** + * Set direction layout for a node and its descendants. + * @private + * @param {import('./jsmind.node.js').Node} node - Target node + * @param {number} direction - Direction constant (-1, 0, 1) + * @param {number} side_index - Index among siblings + */ _layout_direction_side(node, direction, side_index) { var layout_data = null; if ('layout' in node._data) { @@ -96,6 +121,7 @@ export class LayoutProvider { this._layout_direction_side(children[i], direction, i); } } + /** Calculate and set position offsets for all nodes. */ layout_offset() { var node = this.jm.mind.root; var layout_data = node._data.layout; @@ -124,7 +150,12 @@ export class LayoutProvider { this.bounds.n = 0; this.bounds.s = Math.max(layout_data.outer_height_left, layout_data.outer_height_right); } - // layout both the x and y axis + /** + * Layout both the x and y axis for subnodes. + * @private + * @param {import('./jsmind.node.js').Node[]} nodes - Array of nodes to layout + * @returns {number} Total height of all nodes + */ _layout_offset_subnodes(nodes) { var total_height = 0; var nodes_count = nodes.length; @@ -175,7 +206,12 @@ export class LayoutProvider { } return total_height; } - // layout the y axis only, for collapse/expand a node + /** + * Layout the y axis only, for collapse/expand a node. + * @private + * @param {import('./jsmind.node.js').Node[]} nodes - Array of nodes to layout + * @returns {number} Total height of all nodes + */ _layout_offset_subnodes_height(nodes) { var total_height = 0; var nodes_count = nodes.length; @@ -217,9 +253,20 @@ export class LayoutProvider { } return total_height; } + /** + * Check if node should reserve cousin space. + * @private + * @param {import('./jsmind.node.js').Node} node - Node to check + * @returns {boolean} True if cousin space should be reserved + */ _should_reserve_cousin_space(node) { return node.children.length > 0 && node.parent.children.length > 1; } + /** + * Get absolute offset for a node. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {{x:number, y:number}} Absolute position offset + */ get_node_offset(node) { var layout_data = node._data.layout; var offset_cache = null; @@ -242,6 +289,11 @@ export class LayoutProvider { } return offset_cache; } + /** + * Get anchor point for lines on a node. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {{x:number, y:number}} Anchor point coordinates + */ get_node_point(node) { var view_data = node._data.view; var offset_p = this.get_node_offset(node); @@ -250,10 +302,20 @@ export class LayoutProvider { p.y = offset_p.y - view_data.height / 2; return p; } + /** + * Get input point for lines on a node. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {{x:number, y:number}} Input point coordinates + */ get_node_point_in(node) { var p = this.get_node_offset(node); return p; } + /** + * Get output point for lines on a node. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {{x:number, y:number}} Output point coordinates + */ get_node_point_out(node) { var layout_data = node._data.layout; var pout_cache = null; @@ -277,6 +339,11 @@ export class LayoutProvider { } return pout_cache; } + /** + * Get expander point for a node. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {{x:number, y:number}} Expander point coordinates + */ get_expander_point(node) { var p = this.get_node_point_out(node); var ex_p = {}; @@ -288,6 +355,10 @@ export class LayoutProvider { ex_p.y = p.y - Math.ceil(this.opts.pspace / 2); return ex_p; } + /** + * Get minimal canvas size to contain all nodes. + * @returns {{w:number, h:number}} Minimum size required + */ get_min_size() { var nodes = this.jm.mind.nodes; var node = null; @@ -307,6 +378,10 @@ export class LayoutProvider { h: this.bounds.s - this.bounds.n, }; } + /** + * Toggle node expanded/collapsed state. + * @param {import('./jsmind.node.js').Node} node - Target node + */ toggle_node(node) { if (node.isroot) { return; @@ -317,6 +392,10 @@ export class LayoutProvider { this.expand_node(node); } } + /** + * Expand a node and show its children. + * @param {import('./jsmind.node.js').Node} node - Target node + */ expand_node(node) { node.expanded = true; this.part_layout(node); @@ -327,6 +406,10 @@ export class LayoutProvider { node: node.id, }); } + /** + * Collapse a node and hide its children. + * @param {import('./jsmind.node.js').Node} node - Target node + */ collapse_node(node) { node.expanded = false; this.part_layout(node); @@ -337,6 +420,7 @@ export class LayoutProvider { node: node.id, }); } + /** Expand all nodes in the mind map. */ expand_all() { var nodes = this.jm.mind.nodes; var c = 0; @@ -354,6 +438,7 @@ export class LayoutProvider { this.set_visible(root.children, true); } } + /** Collapse all nodes in the mind map. */ collapse_all() { var nodes = this.jm.mind.nodes; var c = 0; @@ -371,6 +456,12 @@ export class LayoutProvider { this.set_visible(root.children, true); } } + /** + * Expand nodes to a specific depth level. + * @param {number} target_depth - Target depth level + * @param {import('./jsmind.node.js').Node[]=} curr_nodes - Current nodes to process + * @param {number=} curr_depth - Current depth level + */ expand_to_depth(target_depth, curr_nodes, curr_depth) { if (target_depth < 1) { return; @@ -394,6 +485,10 @@ export class LayoutProvider { } } } + /** + * Perform partial layout for a node and its subtree. + * @param {import('./jsmind.node.js').Node} node - Target node + */ part_layout(node) { var root = this.jm.mind.root; if (!!root) { @@ -425,6 +520,11 @@ export class LayoutProvider { logger.warn('can not found root node'); } } + /** + * Set visibility for nodes and their children. + * @param {import('./jsmind.node.js').Node[]} nodes - Array of nodes + * @param {boolean} visible - Visibility state + */ set_visible(nodes, visible) { var i = nodes.length; var node = null; @@ -442,9 +542,19 @@ export class LayoutProvider { } } } + /** + * Check if a node is expanded. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {boolean} True if node is expanded + */ is_expand(node) { return node.expanded; } + /** + * Check if a node is visible. + * @param {import('./jsmind.node.js').Node} node - Target node + * @returns {boolean} True if node is visible + */ is_visible(node) { var layout_data = node._data.layout; if ('visible' in layout_data && !layout_data.visible) { diff --git a/src/jsmind.mind.js b/src/jsmind.mind.js index 62999d83..42355ade 100644 --- a/src/jsmind.mind.js +++ b/src/jsmind.mind.js @@ -10,14 +10,28 @@ import { Node } from './jsmind.node.js'; import { logger, Direction } from './jsmind.common.js'; export class Mind { + /** + * Mind model: holds nodes and selection. + */ constructor() { + /** @type {string | null} */ this.name = null; + /** @type {string | null} */ this.author = null; + /** @type {string | null} */ this.version = null; + /** @type {Node | null} */ this.root = null; + /** @type {Node | null} */ this.selected = null; + /** @type {Record} */ this.nodes = {}; } + /** + * Get a node by id. + * @param {string} node_id + * @returns {Node | null} + */ get_node(node_id) { if (node_id in this.nodes) { return this.nodes[node_id]; @@ -26,6 +40,13 @@ export class Mind { return null; } } + /** + * Set the root node, only once. + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @returns {Node | null} + */ set_root(node_id, topic, data) { if (this.root == null) { this.root = new Node(node_id, 0, topic, data, true); @@ -36,6 +57,17 @@ export class Mind { return null; } } + /** + * Add a child node under parent. + * @param {Node} parent_node + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @param {number=} direction + * @param {boolean=} expanded + * @param {number=} idx + * @returns {Node | null} + */ add_node(parent_node, node_id, topic, data, direction, expanded, idx) { if (!Node.is_node(parent_node)) { logger.error('the parent_node ' + parent_node + ' is not a node.'); @@ -64,6 +96,15 @@ export class Mind { } return node; } + /** + * Insert a node before target node. + * @param {Node} node_before + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @param {number=} direction + * @returns {Node | null} + */ insert_node_before(node_before, node_id, topic, data, direction) { if (!Node.is_node(node_before)) { logger.error('the node_before ' + node_before + ' is not a node.'); @@ -72,6 +113,11 @@ export class Mind { var node_index = node_before.index - 0.5; return this.add_node(node_before.parent, node_id, topic, data, direction, true, node_index); } + /** + * Get previous sibling of a node or node id. + * @param {string | Node} node + * @returns {Node | null} + */ get_node_before(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -92,6 +138,15 @@ export class Mind { return null; } } + /** + * Insert a node after target node. + * @param {Node} node_after + * @param {string} node_id + * @param {string} topic + * @param {Record=} data + * @param {number=} direction + * @returns {Node | null} + */ insert_node_after(node_after, node_id, topic, data, direction) { if (!Node.is_node(node_after)) { logger.error('the node_after ' + node_after + ' is not a node.'); @@ -100,6 +155,11 @@ export class Mind { var node_index = node_after.index + 0.5; return this.add_node(node_after.parent, node_id, topic, data, direction, true, node_index); } + /** + * Get next sibling of a node or node id. + * @param {string | Node} node + * @returns {Node | null} + */ get_node_after(node) { if (!Node.is_node(node)) { var the_node = this.get_node(node); @@ -121,6 +181,14 @@ export class Mind { return null; } } + /** + * Move a node to new parent/position. + * @param {Node} node + * @param {string=} before_id + * @param {string=} parent_id + * @param {number=} direction + * @returns {Node | null} + */ move_node(node, before_id, parent_id, direction) { if (!Node.is_node(node)) { logger.error('the parameter node ' + node + ' is not a node.'); @@ -131,6 +199,11 @@ export class Mind { } return this._move_node(node, before_id, parent_id, direction); } + /** + * Propagate direction to descendants. + * @param {Node} node + * @param {number=} direction + */ _flow_node_direction(node, direction) { if (typeof direction === 'undefined') { direction = node.direction; @@ -142,6 +215,12 @@ export class Mind { this._flow_node_direction(node.children[len], direction); } } + /** + * Re-index node among siblings based on before_id marker. + * @param {Node} node + * @param {string} before_id + * @returns {Node} + */ _move_node_internal(node, before_id) { if (!!node && !!before_id) { if (before_id == '_last_') { @@ -164,6 +243,14 @@ export class Mind { } return node; } + /** + * Internal move implementation. + * @param {Node} node + * @param {string} before_id + * @param {string} parent_id + * @param {number=} direction + * @returns {Node | null} + */ _move_node(node, before_id, parent_id, direction) { if (!!node && !!parent_id) { var parent_node = this.get_node(parent_id); @@ -201,6 +288,11 @@ export class Mind { } return node; } + /** + * Remove a node from the mind. + * @param {Node} node + * @returns {boolean} + */ remove_node(node) { if (!Node.is_node(node)) { logger.error('the parameter node ' + node + ' is not a node.'); @@ -242,6 +334,11 @@ export class Mind { this._update_index(node_parent); return true; } + /** + * Put node into the map if id is not taken. + * @param {Node} node + * @returns {boolean} + */ _put_node(node) { if (node.id in this.nodes) { logger.warn("the node_id '" + node.id + "' has been already exist."); @@ -251,6 +348,10 @@ export class Mind { return true; } } + /** + * Re-index children by Node.compare. + * @param {Node} node + */ _update_index(node) { if (node instanceof Node) { node.children.sort(Node.compare); diff --git a/src/jsmind.node.js b/src/jsmind.node.js index 6d136ee6..84253a44 100644 --- a/src/jsmind.node.js +++ b/src/jsmind.node.js @@ -8,6 +8,17 @@ import { logger } from './jsmind.common.js'; export class Node { + /** + * Create a Node instance. + * @param {string} sId - Node id + * @param {number} iIndex - Node index (order among siblings). Use -1 for tail + * @param {string} sTopic - Node topic text + * @param {Record=} oData - Arbitrary node data + * @param {boolean=} bIsRoot - Whether it is the root node + * @param {Node | null=} oParent - Parent node + * @param {number=} eDirection - Direction for children under root (-1 left, 0 center, 1 right) + * @param {boolean=} bExpanded - Expanded state + */ constructor(sId, iIndex, sTopic, oData, bIsRoot, oParent, eDirection, bExpanded) { if (!sId) { logger.error('invalid node id'); @@ -23,15 +34,21 @@ export class Node { this.id = sId; this.index = iIndex; this.topic = sTopic; + /** @type {Record} */ this.data = oData || {}; this.isroot = bIsRoot; this.parent = oParent; this.direction = eDirection; this.expanded = !!bExpanded; + /** @type {Node[]} */ this.children = []; this._data = {}; } + /** + * Get absolute location of this node in view coordinates. + * @returns {{x:number,y:number}} + */ get_location() { var vd = this._data.view; return { @@ -39,6 +56,10 @@ export class Node { y: vd.abs_y, }; } + /** + * Get rendered size of this node. + * @returns {{w:number,h:number}} + */ get_size() { var vd = this._data.view; return { @@ -47,6 +68,12 @@ export class Node { }; } + /** + * Compare two nodes by index for ordering. + * @param {Node} node1 + * @param {Node} node2 + * @returns {number} + */ static compare(node1, node2) { // '-1' is always the latest var r = 0; @@ -65,6 +92,12 @@ export class Node { } return r; } + /** + * Check if node is the same as or a descendant of parent_node. + * @param {Node} parent_node + * @param {Node} node + * @returns {boolean} + */ static inherited(parent_node, node) { if (!!parent_node && !!node) { if (parent_node.id === node.id) { @@ -84,6 +117,11 @@ export class Node { } return false; } + /** + * Runtime check for Node instance. + * @param {unknown} n + * @returns {n is Node} + */ static is_node(n) { return !!n && n instanceof Node; } diff --git a/src/jsmind.option.js b/src/jsmind.option.js index 1cac2919..1c1ec6ac 100644 --- a/src/jsmind.option.js +++ b/src/jsmind.option.js @@ -8,6 +8,37 @@ import { util } from './jsmind.util.js'; +/** + * @typedef {{ + * container: string|HTMLElement, + * editable: boolean, + * theme: (string|null), + * mode: ('full'|'side'), + * support_html: boolean, + * log_level: 'debug'|'info'|'warn'|'error'|'disable', + * view: { + * engine: 'canvas'|'svg', + * enable_device_pixel_ratio: boolean, + * hmargin: number, + * vmargin: number, + * line_width: number, + * line_color: string, + * line_style: 'curved'|'straight', + * custom_line_render?: (this: object, arg:{ ctx: CanvasRenderingContext2D|SVGPathElement, start_point:{x:number,y:number}, end_point:{x:number,y:number} })=>void, + * draggable: boolean, + * hide_scrollbars_when_draggable: boolean, + * node_overflow: 'hidden'|'wrap', + * zoom: { min:number, max:number, step:number, mask_key:number }, + * custom_node_render: (null|((jm: import('./jsmind.js').default, ele: HTMLElement, node: import('./jsmind.node.js').Node)=>void)), + * expander_style: 'char'|'number' + * }, + * layout: { hspace:number, vspace:number, pspace:number, cousin_space:number }, + * default_event_handle: { enable_mousedown_handle:boolean, enable_click_handle:boolean, enable_dblclick_handle:boolean, enable_mousewheel_handle:boolean }, + * shortcut: { enable:boolean, handles: Recordvoid>, mapping: Record, id_generator?: ()=>string }, + * plugin: Record + * }} JsMindRuntimeOptions + */ +/** @type {JsMindRuntimeOptions} */ const default_options = { container: '', // id of the container editable: false, // you can change it in your options @@ -66,6 +97,11 @@ const default_options = { plugin: {}, }; +/** + * Merge user options with defaults. Throws if container missing. + * @param {Partial} options + * @returns {JsMindRuntimeOptions} + */ export function merge_option(options) { var opts = {}; util.json.merge(opts, default_options); diff --git a/src/jsmind.plugin.js b/src/jsmind.plugin.js index 26f5c6cf..becd47f3 100644 --- a/src/jsmind.plugin.js +++ b/src/jsmind.plugin.js @@ -8,10 +8,15 @@ import { $ } from './jsmind.dom.js'; +/** @type {{ plugins: Array> }} */ const plugin_data = { plugins: [], }; +/** + * Register a plugin instance. + * @param {Plugin} plugin + */ export function register(plugin) { if (!(plugin instanceof Plugin)) { throw new Error('can not register plugin, it is not an instance of Plugin'); @@ -22,18 +27,30 @@ export function register(plugin) { plugin_data.plugins.push(plugin); } +/** + * Apply registered plugins asynchronously. + * @param {import('./jsmind.js').default} jm + * @param {Record} options + */ export function apply(jm, options) { $.w.setTimeout(function () { _apply(jm, options); }, 0); } +/** + * @param {import('./jsmind.js').default} jm + * @param {Record} options */ function _apply(jm, options) { plugin_data.plugins.forEach(p => p.fn_init(jm, options[p.name])); } export class Plugin { - // function fn_init(jm, options){ } + /** + * @template [TOptions=object] + * @param {string} name + * @param {(jm: import('./jsmind.js').default, options: TOptions)=>void} fn_init + */ constructor(name, fn_init) { if (!name) { throw new Error('plugin must has a name'); diff --git a/src/jsmind.shortcut_provider.js b/src/jsmind.shortcut_provider.js index c0255288..6115ce27 100644 --- a/src/jsmind.shortcut_provider.js +++ b/src/jsmind.shortcut_provider.js @@ -11,12 +11,20 @@ import { util } from './jsmind.util.js'; import { Direction } from './jsmind.common.js'; export class ShortcutProvider { + /** + * @param {import('./jsmind.js').default} jm + * @param {{ enable:boolean, handles: Recordvoid>, mapping: Record, id_generator?: ()=>string }} options + */ constructor(jm, options) { this.jm = jm; this.opts = options; + /** @type {Record} */ this.mapping = options.mapping; + /** @type {Recordvoid>} */ this.handles = options.handles; + /** @type {()=>string|null} */ this._newid = null; + /** @type {Recordvoid>} */ this._mapping = {}; } init() { @@ -56,6 +64,7 @@ export class ShortcutProvider { disable_shortcut() { this.opts.enable = false; } + /** @param {KeyboardEvent} e */ handler(e) { if (e.which == 9) { e.preventDefault(); @@ -77,6 +86,7 @@ export class ShortcutProvider { this._mapping[kc].call(this, this.jm, e); } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_addchild(_jm, e) { var selected_node = _jm.get_selected_node(); if (!!selected_node) { @@ -88,6 +98,7 @@ export class ShortcutProvider { } } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_addbrother(_jm, e) { var selected_node = _jm.get_selected_node(); if (!!selected_node && !selected_node.isroot) { @@ -99,12 +110,14 @@ export class ShortcutProvider { } } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_editnode(_jm, e) { var selected_node = _jm.get_selected_node(); if (!!selected_node) { _jm.begin_edit(selected_node); } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_delnode(_jm, e) { var selected_node = _jm.get_selected_node(); if (!!selected_node && !selected_node.isroot) { @@ -112,6 +125,7 @@ export class ShortcutProvider { _jm.remove_node(selected_node); } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_toggle(_jm, e) { var evt = e || event; var selected_node = _jm.get_selected_node(); @@ -121,6 +135,7 @@ export class ShortcutProvider { evt.preventDefault(); } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_up(_jm, e) { var evt = e || event; var selected_node = _jm.get_selected_node(); @@ -139,6 +154,7 @@ export class ShortcutProvider { evt.preventDefault(); } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_down(_jm, e) { var evt = e || event; var selected_node = _jm.get_selected_node(); @@ -157,12 +173,15 @@ export class ShortcutProvider { evt.preventDefault(); } } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_left(_jm, e) { this._handle_direction(_jm, e, Direction.left); } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e */ handle_right(_jm, e) { this._handle_direction(_jm, e, Direction.right); } + /** @param {import('./jsmind.js').default} _jm @param {KeyboardEvent} e @param {number} d */ _handle_direction(_jm, e, d) { var evt = e || event; var selected_node = _jm.get_selected_node(); diff --git a/src/jsmind.util.js b/src/jsmind.util.js index 9e64d9fb..bf1b2c22 100644 --- a/src/jsmind.util.js +++ b/src/jsmind.util.js @@ -8,6 +8,15 @@ import { $ } from './jsmind.dom.js'; +/** + * Misc utility collection. + * @type {{ + * file: { read: (file: File, cb:(result:string,name:string)=>void)=>void, save:(data:string,type:string,name:string)=>void}, + * json: { json2string:(v:unknown)=>string, string2json:(s:string)=>unknown, merge:(b:object,a:object)=>object }, + * uuid: { newid:()=>string }, + * text: { is_empty:(s?:string)=>boolean } + * }} + */ export const util = { file: { read: function (file_data, fn_callback) { diff --git a/src/jsmind.view_provider.js b/src/jsmind.view_provider.js index 8058d165..d5cd5c2d 100644 --- a/src/jsmind.view_provider.js +++ b/src/jsmind.view_provider.js @@ -11,6 +11,26 @@ import { init_graph } from './jsmind.graph.js'; import { util } from './jsmind.util.js'; export class ViewProvider { + /** + * View layer: DOM nodes, editor, graph and zoom. + * @param {import('./jsmind.js').default} jm - jsMind instance + * @param {{ + * engine: 'canvas'|'svg', + * enable_device_pixel_ratio: boolean, + * hmargin: number, + * vmargin: number, + * line_width: number, + * line_color: string, + * line_style: 'curved'|'straight', + * custom_line_render?: Function, + * draggable: boolean, + * hide_scrollbars_when_draggable: boolean, + * node_overflow: 'hidden'|'wrap', + * zoom: {min:number, max:number, step:number, mask_key:number}, + * custom_node_render?: Function, + * expander_style: 'char'|'number' + * }} options - View configuration options + */ constructor(jm, options) { this.opts = options; this.jm = jm; @@ -35,11 +55,14 @@ export class ViewProvider { : 1; this._initialized = false; } + /** Initialize DOM structure, graph and editor. */ init() { logger.debug(this.opts); logger.debug('view.init'); - this.container = $.i(this.opts.container) ? this.opts.container : $.g(this.opts.container); + this.container = $.i(this.opts.container) + ? /** @type {HTMLElement} */ (this.opts.container) + : /** @type {HTMLElement} */ ($.g(this.opts.container)); if (!this.container) { logger.error('the options.view.container was not be found in dom'); return; @@ -81,6 +104,13 @@ export class ViewProvider { } } + /** + * Add a delegated event handler. + * @param {import('./jsmind.js').default} obj + * @param {string} event_name + * @param {(e:Event)=>void} event_handle + * @param {boolean=} capture_by_panel + */ add_event(obj, event_name, event_handle, capture_by_panel) { let target = !!capture_by_panel ? this.e_panel : this.e_nodes; $.on(target, event_name, function (e) { @@ -88,6 +118,10 @@ export class ViewProvider { event_handle.call(obj, evt); }); } + /** + * @param {HTMLElement|null} element + * @returns {string|null} + */ get_binded_nodeid(element) { if (element == null) { return null; @@ -101,6 +135,10 @@ export class ViewProvider { return this.get_binded_nodeid(element.parentElement); } } + /** + * @param {HTMLElement|null} element + * @returns {boolean} + */ is_node(element) { if (element == null) { return false; @@ -114,6 +152,10 @@ export class ViewProvider { return this.is_node(element.parentElement); } } + /** + * @param {HTMLElement} element + * @returns {boolean} + */ is_expander(element) { return element.tagName.toLowerCase() == 'jmexpander'; } @@ -132,18 +174,21 @@ export class ViewProvider { this.e_nodes.className = ''; } } + /** Reset custom styles for all nodes. */ reset_custom_style() { var nodes = this.jm.mind.nodes; for (var nodeid in nodes) { this.reset_node_custom_style(nodes[nodeid]); } } + /** Load and initialize the view. */ load() { logger.debug('view.load'); this.setup_canvas_draggable(this.opts.draggable); this.init_nodes(); this._initialized = true; } + /** Calculate and set the expanded canvas size. */ expand_size() { var min_size = this.layout.get_min_size(); var min_width = min_size.w + this.opts.hmargin * 2; @@ -159,11 +204,16 @@ export class ViewProvider { this.size.w = client_w; this.size.h = client_h; } + /** + * Initialize size data for a node. + * @param {import('./jsmind.node.js').Node} node - Target node + */ init_nodes_size(node) { var view_data = node._data.view; view_data.width = view_data.element.clientWidth; view_data.height = view_data.element.clientHeight; } + /** Initialize DOM elements for all nodes. */ init_nodes() { var nodes = this.jm.mind.nodes; var doc_frag = $.d.createDocumentFragment(); @@ -178,12 +228,20 @@ export class ViewProvider { } }); } + /** + * Add a new node to the view. + * @param {import('./jsmind.node.js').Node} node - Node to add + */ add_node(node) { this.create_node_element(node, this.e_nodes); this.run_in_c11y_mode_if_needed(() => { this.init_nodes_size(node); }); } + /** + * Run function in compatibility mode if container is not visible. + * @param {Function} func - Function to execute + */ run_in_c11y_mode_if_needed(func) { if (!!this.container.offsetParent) { func(); @@ -200,6 +258,11 @@ export class ViewProvider { this.e_panel.style.position = null; this.e_panel.style.top = null; } + /** + * Create a DOM element for a node and append to parent container. + * @param {import('./jsmind.node.js').Node} node + * @param {HTMLElement} parent_node + */ create_node_element(node, parent_node) { var view_data = null; if ('view' in node._data) { @@ -230,6 +293,10 @@ export class ViewProvider { parent_node.appendChild(d); view_data.element = d; } + /** + * Remove a node from the view. + * @param {import('./jsmind.node.js').Node} node - Node to remove + */ remove_node(node) { if (this.selected_node != null && this.selected_node.id == node.id) { this.selected_node = null; @@ -252,6 +319,10 @@ export class ViewProvider { node._data.view.expander = null; } } + /** + * Update a node's display. + * @param {import('./jsmind.node.js').Node} node - Node to update + */ update_node(node) { var view_data = node._data.view; var element = view_data.element; @@ -269,6 +340,10 @@ export class ViewProvider { element.style = origin_style; } } + /** + * Select a node visually. + * @param {import('./jsmind.node.js').Node|null} node - Node to select + */ select_node(node) { if (!!this.selected_node) { var element = this.selected_node._data.view.element; @@ -281,15 +356,28 @@ export class ViewProvider { this.clear_selected_node_custom_style(node); } } + /** Clear node selection. */ select_clear() { this.select_node(null); } + /** + * Get currently editing node. + * @returns {import('./jsmind.node.js').Node|null} Currently editing node + */ get_editing_node() { return this.editing_node; } + /** + * Check if any node is being edited. + * @returns {boolean} True if editing + */ is_editing() { return !!this.editing_node; } + /** + * Begin editing a node. + * @param {import('./jsmind.node.js').Node} node - Node to edit + */ edit_node_begin(node) { if (!node.topic) { logger.warn("don't edit image nodes"); @@ -315,6 +403,7 @@ export class ViewProvider { this.e_editor.focus(); this.e_editor.select(); } + /** End editing current node. */ edit_node_end() { if (this.editing_node != null) { var node = this.editing_node; @@ -332,12 +421,14 @@ export class ViewProvider { } this.e_panel.focus(); } + /** @returns {{x:number,y:number}} */ get_view_offset() { var bounds = this.layout.bounds; var _x = (this.size.w - bounds.e - bounds.w) / 2; var _y = this.size.h / 2; return { x: _x, y: _y }; } + /** Resize the view to fit container. */ resize() { this.graph.set_size(1, 1); this.e_nodes.style.width = '1px'; @@ -346,6 +437,10 @@ export class ViewProvider { this.expand_size(); this._show(); } + /** + * Internal show implementation. + * @private + */ _show() { this.graph.set_size(this.size.w, this.size.h); this.e_nodes.style.width = this.size.w + 'px'; @@ -355,12 +450,27 @@ export class ViewProvider { //this.layout.cache_valid = true; this.jm.invoke_event_handle(EventType.resize, { data: [] }); } + /** + * Zoom in the view. + * @param {MouseEvent=} e - Mouse event for zoom center + * @returns {boolean} True if zoom succeeded + */ zoom_in(e) { return this.set_zoom(this.zoom_current + this.opts.zoom.step, e); } + /** + * Zoom out the view. + * @param {MouseEvent=} e - Mouse event for zoom center + * @returns {boolean} True if zoom succeeded + */ zoom_out(e) { return this.set_zoom(this.zoom_current - this.opts.zoom.step, e); } + /** + * Set zoom level and keep scroll around zoom center. + * @param {number} zoom + * @param {MouseEvent=} e + */ set_zoom(zoom, e) { if (zoom < this.opts.zoom.min || zoom > this.opts.zoom.max) { return false; @@ -391,6 +501,7 @@ export class ViewProvider { this.e_panel.scrollTop = panel_scroll_y; return true; } + /** @param {boolean=} keep_center */ show(keep_center) { logger.debug(`view.show: {keep_center: ${keep_center}}`); this.expand_size(); @@ -403,6 +514,7 @@ export class ViewProvider { this.expand_size(); this._show(); } + /** @param {import('./jsmind.node.js').Node} node */ save_location(node) { var vd = node._data.view; vd._saved_location = { @@ -410,6 +522,7 @@ export class ViewProvider { y: parseInt(vd.element.style.top) - this.e_panel.scrollTop, }; } + /** @param {import('./jsmind.node.js').Node} node */ restore_location(node) { var vd = node._data.view; this.e_panel.scrollLeft = parseInt(vd.element.style.left) - vd._saved_location.x; @@ -429,6 +542,7 @@ export class ViewProvider { } this.e_nodes.innerHTML = ''; } + /** Render node elements and expanders to screen. */ show_nodes() { var nodes = this.jm.mind.nodes; var node = null; @@ -456,6 +570,7 @@ export class ViewProvider { this._show_expander(node, view_offset); } } + /** @param {import('./jsmind.node.js').Node} node @param {{x:number,y:number}} view_offset */ _show_expander(node, view_offset) { if (node.isroot) { return; @@ -478,6 +593,7 @@ export class ViewProvider { expander.style.visibility = 'visible'; } + /** @param {import('./jsmind.node.js').Node} node */ _get_expander_text(node) { let style = !!this.opts.expander_style ? this.opts.expander_style.toLowerCase() : 'char'; if (style === 'number') { @@ -488,6 +604,7 @@ export class ViewProvider { } } + /** @param {HTMLElement} ele @param {import('./jsmind.node.js').Node} node */ _default_node_render(ele, node) { if (this.opts.support_html) { $.h(ele, node.topic); @@ -495,15 +612,18 @@ export class ViewProvider { $.t(ele, node.topic); } } + /** @param {HTMLElement} ele @param {import('./jsmind.node.js').Node} node */ _custom_node_render(ele, node) { let rendered = this.opts.custom_node_render(this.jm, ele, node); if (!rendered) { this._default_node_render(ele, node); } } + /** @param {import('./jsmind.node.js').Node} node */ reset_node_custom_style(node) { this._reset_node_custom_style(node._data.view.element, node.data); } + /** @param {HTMLElement} node_element @param {Record} node_data */ _reset_node_custom_style(node_element, node_data) { if ('background-color' in node_data) { node_element.style.backgroundColor = node_data['background-color']; @@ -561,6 +681,7 @@ export class ViewProvider { } } } + /** @param {import('./jsmind.node.js').Node} node */ restore_selected_node_custom_style(node) { var node_element = node._data.view.element; var node_data = node.data; @@ -571,6 +692,7 @@ export class ViewProvider { node_element.style.color = node_data['foreground-color']; } } + /** @param {import('./jsmind.node.js').Node} node */ clear_selected_node_custom_style(node) { var node_element = node._data.view.element; node_element.style.backgroundColor = ''; @@ -602,6 +724,10 @@ export class ViewProvider { } } // Drag the whole mind map with your mouse, when it's larger that the container + /** + * Enable/disable dragging the whole canvas with mouse. + * @param {boolean} enabled + */ setup_canvas_draggable(enabled) { this.opts.draggable = enabled; if (!this._initialized) { @@ -637,6 +763,7 @@ export class ViewProvider { }); } } + /** @param {import('./jsmind.node.js').Node} node */ center_node(node) { if (!this.layout.is_visible(node)) { logger.warn('can not scroll to the node, because it is invisible'); @@ -655,14 +782,17 @@ export class ViewProvider { return true; } + /** @param {MouseEvent=} e */ zoomIn(e) { logger.warn('please use zoom_in instead'); return this.zoom_in(e); } + /** @param {MouseEvent=} e */ zoomOut(e) { logger.warn('please use zoom_out instead'); return this.zoom_out(e); } + /** @param {number} zoom @param {MouseEvent=} e */ setZoom(zoom, e) { logger.warn('please use set_zoom instead'); return this.set_zoom(zoom, e); diff --git a/src/plugins/jsmind.draggable-node.js b/src/plugins/jsmind.draggable-node.js index 7cf61a8b..d676486d 100644 --- a/src/plugins/jsmind.draggable-node.js +++ b/src/plugins/jsmind.draggable-node.js @@ -23,6 +23,18 @@ const clear_selection = $.d.selection.empty(); }; +/** + * Default options for draggable node plugin. + * @typedef {Object} DraggableNodeOptions + * @property {number} [line_width] + * @property {string} [line_color] + * @property {string} [line_color_invalid] + * @property {number} [lookup_delay] + * @property {number} [lookup_interval] + * @property {number} [scrolling_trigger_width] + * @property {number} [scrolling_step_length] + * @property {string} [shadow_node_class_name] + */ const DEFAULT_OPTIONS = { line_width: 5, line_color: 'rgba(0,0,0,0.3)', @@ -34,47 +46,81 @@ const DEFAULT_OPTIONS = { shadow_node_class_name: 'jsmind-draggable-shadow-node', }; -class DraggableNode { +/** + * Draggable node plugin for jsMind. + */ +export class DraggableNode { + /** + * Create draggable node plugin instance. + * @param {import('../jsmind.js').default} jm - jsMind instance + * @param {Partial} options - Plugin options + */ constructor(jm, options) { var opts = {}; jsMind.util.json.merge(opts, DEFAULT_OPTIONS); jsMind.util.json.merge(opts, options); this.version = '0.4.0'; + /** @type {import('../jsmind.js').default} */ this.jm = jm; + /** @type {DraggableNodeOptions} */ this.options = opts; + /** @type {HTMLCanvasElement|null} */ this.e_canvas = null; + /** @type {CanvasRenderingContext2D|null} */ this.canvas_ctx = null; + /** @type {HTMLElement|null} */ this.shadow = null; + /** @type {number} */ this.shadow_p_x = 0; + /** @type {number} */ this.shadow_p_y = 0; + /** @type {number} */ this.shadow_w = 0; + /** @type {number} */ this.shadow_h = 0; + /** @type {import('../jsmind.node.js').Node|null} */ this.active_node = null; + /** @type {import('../jsmind.node.js').Node|null} */ this.target_node = null; + /** @type {number|null} */ this.target_direct = null; + /** @type {number} */ this.client_w = 0; + /** @type {number} */ this.client_h = 0; + /** @type {number} */ this.offset_x = 0; + /** @type {number} */ this.offset_y = 0; + /** @type {number} */ this.hlookup_delay = 0; + /** @type {number} */ this.hlookup_timer = 0; + /** @type {boolean} */ this.capture = false; + /** @type {boolean} */ this.moved = false; + /** @type {boolean} */ this.canvas_draggable = jm.get_view_draggable(); + /** @type {HTMLElement} */ this.view_panel = jm.view.e_panel; + /** @type {DOMRect|null} */ this.view_panel_rect = null; } + /** Initialize the draggable node plugin. */ init() { this.create_canvas(); this.create_shadow(); this.event_bind(); } + /** Resize canvas and shadow elements. */ resize() { this.jm.view.e_nodes.appendChild(this.shadow); this.e_canvas.width = this.jm.view.size.w; this.e_canvas.height = this.jm.view.size.h; } + /** Create canvas for drawing drag lines. */ create_canvas() { var c = $.c('canvas'); this.jm.view.e_panel.appendChild(c); @@ -91,6 +137,10 @@ class DraggableNode { s.className = this.options.shadow_node_class_name; this.shadow = s; } + /** + * Reset shadow element style and cache its size. + * @param {HTMLElement} el - The node element to mirror as shadow + */ reset_shadow(el) { var s = this.shadow.style; this.shadow.innerHTML = el.innerHTML; @@ -104,14 +154,22 @@ class DraggableNode { this.shadow_w = this.shadow.clientWidth; this.shadow_h = this.shadow.clientHeight; } + /** Show the shadow element. */ show_shadow() { if (!this.moved) { this.shadow.style.visibility = 'visible'; } } + /** Hide the shadow element. */ hide_shadow() { this.shadow.style.visibility = 'hidden'; } + /** + * Draw a helper line between the shadow and target node. + * @param {{x:number,y:number}} shadow_p - Shadow anchor point + * @param {{x:number,y:number}} node_p - Target node anchor point + * @param {boolean} invalid - Whether current target is invalid + */ magnet_shadow(shadow_p, node_p, invalid) { this.canvas_ctx.lineWidth = this.options.line_width; this.canvas_ctx.strokeStyle = invalid @@ -121,15 +179,24 @@ class DraggableNode { this.clear_lines(); this.canvas_lineto(shadow_p.x, shadow_p.y, node_p.x, node_p.y); } + /** Clear helper lines from canvas. */ clear_lines() { this.canvas_ctx.clearRect(0, 0, this.jm.view.size.w, this.jm.view.size.h); } + /** + * Draw a straight helper line. + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + */ canvas_lineto(x1, y1, x2, y2) { this.canvas_ctx.beginPath(); this.canvas_ctx.moveTo(x1, y1); this.canvas_ctx.lineTo(x2, y2); this.canvas_ctx.stroke(); } + /** Bind mouse/touch events for dragging. */ event_bind() { var jd = this; var container = this.jm.view.container; @@ -156,6 +223,10 @@ class DraggableNode { jd.dragend.call(jd, e); }); } + /** + * Begin dragging interaction. + * @param {MouseEvent|TouchEvent} e - Pointer down event + */ dragstart(e) { if (!this.jm.get_editable()) { return; @@ -207,6 +278,10 @@ class DraggableNode { } } } + /** + * Drag handler to move shadow and auto-scroll container. + * @param {MouseEvent|TouchEvent} e - Pointer move event + */ drag(e) { if (!this.jm.get_editable()) { return; @@ -258,6 +333,10 @@ class DraggableNode { clear_selection(); } } + /** + * Finish dragging, move the node if applicable. + * @param {MouseEvent|TouchEvent} e - Pointer up event + */ dragend(e) { if (!this.jm.get_editable()) { return; @@ -288,6 +367,11 @@ class DraggableNode { this.moved = false; this.capture = false; } + /** + * Find the closest node element from an event target. + * @param {HTMLElement} el - Current DOM element + * @returns {HTMLElement|null} Matched node element or null + */ find_node_element(el) { if ( el === this.jm.view.e_nodes || @@ -301,6 +385,7 @@ class DraggableNode { } return this.find_node_element(el.parentNode); } + /** Recompute target node under the shadow and draw helper. */ lookup_target_node() { let sx = this.shadow.offsetLeft; let sy = this.shadow.offsetTop; @@ -324,6 +409,10 @@ class DraggableNode { this.target_direct = target_direction; } } + /** + * Get X coordinate of root node center. + * @returns {number} + */ get_root_x() { let root = this.jm.get_root(); let root_location = root.get_location(); @@ -331,6 +420,11 @@ class DraggableNode { return root_location.x + root_size.w / 2; } + /** + * Lookup overlapping node's parent near the shadow position. + * @param {number} direction - Direction constant + * @returns {import('../jsmind.node.js').Node|null} + */ lookup_overlapping_node_parent(direction) { let shadowRect = this.shadow.getBoundingClientRect(); let x = shadowRect.x + (shadowRect.width * (1 - direction)) / 2; @@ -355,6 +449,12 @@ class DraggableNode { } } + /** + * Find node's parent by a screen location. + * @param {number} x - Client X + * @param {number} y - Client Y + * @returns {import('../jsmind.node.js').Node|null} + */ lookup_node_parent_by_location(x, y) { return $.d .elementsFromPoint(x, y) @@ -367,6 +467,11 @@ class DraggableNode { .find(n => n); } + /** + * Lookup the closest node along a direction. + * @param {number} direction + * @returns {import('../jsmind.node.js').Node} + */ lookup_close_node(direction) { return Object.values(this.jm.mind.nodes) .filter(n => n.direction == direction || n.isroot) @@ -381,6 +486,12 @@ class DraggableNode { ).node; } + /** + * Check if shadow is on the target side of a node. + * @param {import('../jsmind.node.js').Node} node + * @param {number} dir + * @returns {boolean} + */ shadow_on_target_side(node, dir) { return ( (dir == jsMind.direction.right && this.shadow_to_right_of_node(node) > 0) || @@ -388,18 +499,39 @@ class DraggableNode { ); } + /** + * Distance from shadow to the right side of a node. + * @param {import('../jsmind.node.js').Node} node + * @returns {number} + */ shadow_to_right_of_node(node) { return this.shadow_p_x - node.get_location().x - node.get_size().w; } + /** + * Distance from shadow to the left side of a node. + * @param {import('../jsmind.node.js').Node} node + * @returns {number} + */ shadow_to_left_of_node(node) { return node.get_location().x - this.shadow_p_x - this.shadow_w; } + /** + * Vertical distance between shadow centerline and node centerline. + * @param {import('../jsmind.node.js').Node} node + * @returns {number} + */ shadow_to_base_line_of_node(node) { return this.shadow_p_y + this.shadow_h / 2 - node.get_location().y - node.get_size().h / 2; } + /** + * Manhattan distance to a node along a direction. + * @param {import('../jsmind.node.js').Node} node + * @param {number} dir + * @returns {number} + */ shadow_to_node(node, dir) { let distance_x = dir === jsMind.direction.right @@ -409,6 +541,12 @@ class DraggableNode { return distance_x + distance_y; } + /** + * Calculate connection points of a node and the shadow. + * @param {import('../jsmind.node.js').Node} node + * @param {number} dir + * @returns {{sp:{x:number,y:number}, np:{x:number,y:number}}} + */ calc_point_of_node(node, dir) { let ns = node.get_size(); let nl = node.get_location(); @@ -425,6 +563,12 @@ class DraggableNode { }; } + /** + * Move a node to a new parent/position. + * @param {import('../jsmind.node.js').Node} src_node + * @param {import('../jsmind.node.js').Node|null} target_node + * @param {number|null} target_direct + */ move_node(src_node, target_node, target_direct) { var shadow_h = this.shadow.offsetTop; if (!!target_node && !!src_node && !jsMind.node.inherited(src_node, target_node)) { @@ -455,6 +599,11 @@ class DraggableNode { this.target_node = null; this.target_direct = null; } + /** + * Handle jsMind events. + * @param {number|string} type - Event type + * @param {object} [data] - Event data + */ jm_event_handle(type, data) { if (type === jsMind.event_type.resize) { this.resize(); @@ -462,7 +611,11 @@ class DraggableNode { } } -var draggable_plugin = new jsMind.plugin('draggable_node', function (jm, options) { +/** + * Draggable node plugin registration. + * @type {import('../jsmind.plugin.js').Plugin>} + */ +export const draggable_plugin = new jsMind.plugin('draggable_node', function (jm, options) { var jd = new DraggableNode(jm, options); jd.init(); jm.add_event_listener(function (type, data) { @@ -471,3 +624,5 @@ var draggable_plugin = new jsMind.plugin('draggable_node', function (jm, options }); jsMind.register_plugin(draggable_plugin); + +export default DraggableNode; diff --git a/src/plugins/jsmind.screenshot.js b/src/plugins/jsmind.screenshot.js index 338459e3..b718c7da 100644 --- a/src/plugins/jsmind.screenshot.js +++ b/src/plugins/jsmind.screenshot.js @@ -19,6 +19,13 @@ if (!domtoimage) { const $ = jsMind.$; +/** + * Default options for screenshot plugin. + * @typedef {Object} ScreenshotOptions + * @property {string|null} [filename] + * @property {{left?:string|Location,right?:string}} [watermark] + * @property {string} [background] + */ const DEFAULT_OPTIONS = { filename: null, watermark: { @@ -28,18 +35,30 @@ const DEFAULT_OPTIONS = { background: 'transparent', }; -class JmScreenshot { +/** + * Screenshot plugin for jsMind. + */ +export class JmScreenshot { + /** + * Create screenshot plugin instance. + * @param {import('../jsmind.js').default} jm - jsMind instance + * @param {Partial} options - Plugin options + */ constructor(jm, options) { var opts = {}; jsMind.util.json.merge(opts, DEFAULT_OPTIONS); jsMind.util.json.merge(opts, options); this.version = '0.2.0'; + /** @type {import('../jsmind.js').default} */ this.jm = jm; + /** @type {ScreenshotOptions} */ this.options = opts; + /** @type {number} */ this.dpr = jm.view.device_pixel_ratio; } + /** Take a screenshot of the mind map. */ shoot() { let c = this.create_canvas(); let ctx = c.getContext('2d'); @@ -53,6 +72,10 @@ class JmScreenshot { .then(() => this.clear(c)); } + /** + * Create canvas for screenshot. + * @returns {HTMLCanvasElement} Canvas element + */ create_canvas() { let c = $.c('canvas'); const w = this.jm.view.size.w; @@ -67,10 +90,19 @@ class JmScreenshot { return c; } + /** + * Clean up canvas element. + * @param {HTMLCanvasElement} c - Canvas to remove + */ clear(c) { c.parentNode.removeChild(c); } + /** + * Draw background on canvas. + * @param {CanvasRenderingContext2D} ctx - Canvas context + * @returns {Promise} Promise resolving to context + */ draw_background(ctx) { return new Promise( function (resolve, _) { @@ -84,6 +116,11 @@ class JmScreenshot { ); } + /** + * Draw connection lines on canvas by copying from view graph. + * @param {CanvasRenderingContext2D} ctx + * @returns {Promise} + */ draw_lines(ctx) { return new Promise( function (resolve, _) { @@ -94,6 +131,11 @@ class JmScreenshot { ); } + /** + * Draw node DOM into canvas via SVG snapshot. + * @param {CanvasRenderingContext2D} ctx + * @returns {Promise} + */ draw_nodes(ctx) { return domtoimage .toSvg(this.jm.view.e_nodes, { style: { zoom: 1 } }) @@ -104,6 +146,12 @@ class JmScreenshot { }); } + /** + * Draw watermark text on canvas. + * @param {HTMLCanvasElement} c + * @param {CanvasRenderingContext2D} ctx + * @returns {CanvasRenderingContext2D} + */ draw_watermark(c, ctx) { ctx.textBaseline = 'bottom'; ctx.fillStyle = '#000'; @@ -119,6 +167,11 @@ class JmScreenshot { return ctx; } + /** + * Load image from URL and resolve img element. + * @param {string} url + * @returns {Promise} + */ load_image(url) { return new Promise(function (resolve, reject) { let img = new Image(); @@ -130,6 +183,10 @@ class JmScreenshot { }); } + /** + * Trigger download of canvas content as PNG. + * @param {HTMLCanvasElement} c + */ download(c) { var name = (this.options.filename || this.jm.mind.name) + '.png'; @@ -155,7 +212,11 @@ class JmScreenshot { } } -let screenshot_plugin = new jsMind.plugin('screenshot', function (jm, options) { +/** + * Screenshot plugin registration. + * @type {import('../jsmind.plugin.js').Plugin>} + */ +export const screenshot_plugin = new jsMind.plugin('screenshot', function (jm, options) { var jmss = new JmScreenshot(jm, options); jm.screenshot = jmss; jm.shoot = function () { @@ -164,3 +225,5 @@ let screenshot_plugin = new jsMind.plugin('screenshot', function (jm, options) { }); jsMind.register_plugin(screenshot_plugin); + +export default JmScreenshot; diff --git a/src/types-index.ts b/src/types-index.ts new file mode 100644 index 00000000..a3aea304 --- /dev/null +++ b/src/types-index.ts @@ -0,0 +1,24 @@ +// Aggregated public typings entry: re-export generated types without compatibility layer +export { default } from './jsmind'; + +// Legacy named exports back-compat: re-export classes and key option/data types if present +export { Node } from './jsmind.node'; +export { Mind } from './jsmind.mind'; + +// Export strict options and meta types from generated +export type { JsMindRuntimeOptions as JsMindOptions } from './jsmind.option'; +export type { + MindMapMeta, + NodeTreeData, + NodeTreeFormat, + NodeArrayItem, + NodeArrayFormat, +} from './jsmind.format'; + +// Static singletons / enums passthrough +export { Direction as direction, EventType as event_type } from './jsmind.common'; +export { $ } from './jsmind.dom'; +export { util } from './jsmind.util'; + +// Plugin export paths remain under subpath exports +// typings for subpath are referenced from package.json exports.types diff --git a/tests/fixtures/typescript-test.ts b/tests/fixtures/typescript-test.ts new file mode 100644 index 00000000..d7dbb1fe --- /dev/null +++ b/tests/fixtures/typescript-test.ts @@ -0,0 +1,319 @@ +/** + * TypeScript typings validation example + * This file exercises jsMind's public API for type-checking purposes only. + */ + +// Import core library (resolved via package name to types/) +import jsMind, { + Node, + Mind, + JsMindOptions, + MindMapMeta, + NodeTreeFormat, + NodeTreeData, +} from 'jsmind'; +// Note: in real usage, plugins should be imported to register themselves +// import 'jsmind/draggable-node'; +// import 'jsmind/screenshot'; + +// ============================================================================ +// Basic options +// ============================================================================ + +// Minimal options (must satisfy JsMindRuntimeOptions strict fields) +const basicOptions: JsMindOptions = { + container: 'jsmind_container', + editable: true, + theme: 'orange', + mode: 'full', + support_html: true, + log_level: 'info', + view: { + engine: 'canvas', + enable_device_pixel_ratio: true, + hmargin: 120, + vmargin: 60, + line_width: 3, + line_color: '#333', + line_style: 'straight', + draggable: true, + hide_scrollbars_when_draggable: true, + node_overflow: 'wrap', + zoom: { min: 0.3, max: 3.0, step: 0.2, mask_key: 4096 }, + custom_node_render: null, + expander_style: 'number', + }, + layout: { hspace: 40, vspace: 25, pspace: 15, cousin_space: 5 }, + default_event_handle: { + enable_mousedown_handle: true, + enable_click_handle: true, + enable_dblclick_handle: false, + enable_mousewheel_handle: true, + }, + shortcut: { enable: true, handles: {}, mapping: {} }, + plugin: {}, +}; + +// Full options +const fullOptions: JsMindOptions = { + container: 'jsmind_container', + editable: true, + theme: 'greensea', + mode: 'side', + support_html: false, + log_level: 'debug', + view: { + engine: 'canvas', + enable_device_pixel_ratio: true, + hmargin: 120, + vmargin: 60, + line_width: 3, + line_color: '#333', + line_style: 'straight', + draggable: true, + hide_scrollbars_when_draggable: true, + node_overflow: 'wrap', + zoom: { + min: 0.3, + max: 3.0, + step: 0.2, + mask_key: 4096, + }, + custom_node_render: (jm, element, node) => { + element.innerHTML = `${node.topic}`; + }, + expander_style: 'number', + }, + layout: { + hspace: 40, + vspace: 25, + pspace: 15, + cousin_space: 5, + }, + default_event_handle: { + enable_mousedown_handle: true, + enable_click_handle: true, + enable_dblclick_handle: false, + enable_mousewheel_handle: true, + }, + shortcut: { + enable: true, + handles: { + custom_action: () => console.log('Custom action triggered'), + }, + mapping: { + addchild: [45, 4096 + 13], + addbrother: 13, + editnode: 113, + delnode: 46, + toggle: 32, + left: 37, + up: 38, + right: 39, + down: 40, + }, + }, + plugin: { + draggable_node: { + line_width: 6, + line_color: 'rgba(255,0,0,0.5)', + lookup_delay: 300, + }, + screenshot: { + filename: 'my-mindmap', + background: '#ffffff', + watermark: { + left: 'My Company', + right: 'https://example.com', + }, + }, + }, +}; + +// ============================================================================ +// Data formats +// ============================================================================ + +// Strictly typed NodeTree data using generated MindMapMeta +const nodeTreeData: NodeTreeFormat = { + meta: { name: 'Test Mind Map', author: 'TypeScript Tester', version: '1.0' }, + format: 'node_tree', + data: { + id: 'root', + topic: 'Root Topic', + data: { background: '#ff0000', foreground: '#ffffff' }, + children: [ + { + id: 'child1', + topic: 'Child 1', + direction: 1, + expanded: true, + children: [ + { id: 'grandchild1', topic: 'Grandchild 1', data: { note: 'This is a note' } }, + ], + }, + { id: 'child2', topic: 'Child 2', direction: -1, expanded: false }, + ], + }, +}; + +// ============================================================================ +// jsMind instance +// ============================================================================ + +// Create jsMind instance +const jm = new jsMind(basicOptions); + +// Static members +const NodeClass = jsMind.node; +const MindClass = jsMind.mind; +const direction = jsMind.direction; +const eventType = jsMind.event_type; +const domUtil = jsMind.$; +const util = jsMind.util; + +// Direction enum +const leftDirection: number = direction.left; // -1 +const rightDirection: number = direction.right; // 1 +const centerDirection: number = direction.center; // 0 +const parsedDirection: number | undefined = direction.of('left'); + +// Event types +const showEvent: number = eventType.show; +const resizeEvent: number = eventType.resize; + +// ============================================================================ +// API methods +// ============================================================================ + +// Show mind map +jm.show(nodeTreeData); + +// Query state +const meta = jm.get_meta(); +const data = jm.get_data('node_tree'); +const root = jm.get_root(); + +// Node operations +if (root) { + const newNode = jm.add_node(root, 'new_node', 'New Topic', { color: 'blue' }, 1); + if (newNode) { + jm.update_node(newNode.id, 'Updated Topic'); + jm.select_node(newNode); + + const selectedNode = jm.get_selected_node(); + if (selectedNode) { + jm.set_node_color(selectedNode.id, '#ff0000', '#ffffff'); + jm.set_node_font_style(selectedNode.id, 16, 'bold', 'italic'); + } + } +} + +// Edit operations +jm.enable_edit(); +const isEditable: boolean = jm.get_editable(); +// begin_edit requires a node id or Node +jm.end_edit(); +jm.disable_edit(); + +// Layout operations +jm.expand_all(); +jm.collapse_all(); +jm.expand_to_depth(2); + +// View operations +jm.enable_view_draggable(); +const isDraggable: boolean = jm.get_view_draggable(); +jm.disable_view_draggable(); +jm.resize(); + +// Event listener with stricter data shape + +jm.add_event_listener((type, data) => { + // data: { evt?: string; data?: unknown[]; node?: string } + console.log(`Event ${type} triggered with data:`, data); +}); + +// ============================================================================ +// Node class +// ============================================================================ + +// Create node instance (normally created via API) +const testNode = new Node('test_id', 1, 'Test Topic', { custom: 'data' }, false, null, 1, true); + +// Node fields +const nodeId: string = testNode.id; +const nodeTopic: string = testNode.topic; +const nodeChildren: Node[] = testNode.children; +const isRoot: boolean = testNode.isroot; + +// Node methods +const location = testNode.get_location(); +const size = testNode.get_size(); + +// Static helpers +const isNodeInstance: boolean = Node.is_node(testNode); +const comparison: number = Node.compare(testNode, testNode); + +// ============================================================================ +// Utils +// ============================================================================ + +// JSON utils +const jsonString: string = util.json.json2string({ test: 'data' }); +const jsonObject = util.json.string2json('{"test":"data"}') as { test: string }; +const mergedObject = util.json.merge({}, { test: 'data' }) as object; + +// UUID utils +const newId: string = util.uuid.newid(); + +// Text utils +const isEmpty: boolean = util.text.is_empty(''); +const isNotEmpty: boolean = util.text.is_empty('hello'); + +// File utils (would require a File object) +// util.file.read(fileObject, (result, name) => { +// console.log(`File ${name} content:`, result); +// }); + +// DOM utils +const element = domUtil.g('some_id'); +const newElement = domUtil.c('div'); + +// ============================================================================ +// Type validation +// ============================================================================ + +// The code below should pass the TS compiler type-check +function validateTypes() { + // Validate option types + const config: JsMindOptions = basicOptions; + + // Validate data format types + const mindData: NodeTreeFormat = nodeTreeData; + + // Validate return types + const rootNode: Node | null = jm.get_root(); + const selectedNode: Node | null = jm.get_selected_node(); + + // Validate event handler shape + const eventHandler = (type: number, data: any) => { + if (type === jsMind.event_type.show) { + console.log('Mind map shown'); + } + }; + + return { + config, + mindData, + rootNode, + selectedNode, + eventHandler, + }; +} + +// Export for the Jest runner to import if needed +export { validateTypes }; + +console.log('TypeScript typings validation example done.'); +console.log('If this file compiles, typings are consistent.'); diff --git a/tests/unit/typescript.types.test.js b/tests/unit/typescript.types.test.js new file mode 100644 index 00000000..6d0ceedf --- /dev/null +++ b/tests/unit/typescript.types.test.js @@ -0,0 +1,54 @@ +/** @jest-environment node */ + +// Run type-check using TypeScript Compiler API, equivalent to `tsc --noEmit` +import * as path from 'path'; +import ts from 'typescript'; + +function parseTsConfig(tsconfigPath) { + const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + if (configFile.error) { + const message = ts.formatDiagnosticsWithColorAndContext([configFile.error], formatHost()); + throw new Error(`Failed to read tsconfig:\n${message}`); + } + return ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(tsconfigPath)); +} + +function formatHost() { + return { + getCanonicalFileName: f => f, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => ts.sys.newLine, + }; +} + +describe('TypeScript typings validation', () => { + test('tests/fixtures/typescript-test.ts should pass type-check (no diagnostics)', () => { + const projectRoot = process.cwd(); + const tsconfigPath = path.join(projectRoot, 'tsconfig.json'); + + const parsed = parseTsConfig(tsconfigPath); + + // Force no output (even if `declaration` is on); only type-check + const compilerOptions = { ...parsed.options, noEmit: true }; + + const program = ts.createProgram({ rootNames: parsed.fileNames, options: compilerOptions }); + + const diagnostics = [ + ...program.getConfigFileParsingDiagnostics(), + ...program.getOptionsDiagnostics(), + ...program.getSyntacticDiagnostics(), + ...program.getSemanticDiagnostics(), + ]; + + if (diagnostics.length > 0) { + const message = ts.formatDiagnosticsWithColorAndContext(diagnostics, formatHost()); + // Provide detailed diagnostics for debugging + // eslint-disable-next-line no-console + console.error('\nTypeScript diagnostics:\n'); + // eslint-disable-next-line no-console + console.error(message); + } + + expect(diagnostics.length).toBe(0); + }); +}); diff --git a/tsconfig.decls.json b/tsconfig.decls.json new file mode 100644 index 00000000..4b4ef31d --- /dev/null +++ b/tsconfig.decls.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "strict": false, + "skipLibCheck": true, + "esModuleInterop": true, + "declaration": true, + "emitDeclarationOnly": true, + "stripInternal": true, + "allowJs": true, + "checkJs": false, + "outDir": "types/generated", + "rootDir": "src", + "baseUrl": "." + }, + "include": ["src/**/*.js", "src/**/*.ts"], + "exclude": ["node_modules", "dist", "tests", "example", "es6", "js-legacy", "js-legacy/**/*.js"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..afc73871 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "outDir": "./dist", + "rootDir": "./", + "noEmit": true, + "baseUrl": ".", + "paths": { + "jsmind": ["types/generated/types-index.d.ts"], + "jsmind/draggable-node": ["types/generated/plugins/jsmind.draggable-node.d.ts"], + "jsmind/screenshot": ["types/generated/plugins/jsmind.screenshot.d.ts"] + } + }, + "include": ["types/**/*.d.ts", "tests/fixtures/typescript-test.ts"], + "exclude": ["node_modules", "dist"] +}