├── .gitattributes ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── build.yml │ └── wiki.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── docs ├── img │ ├── custom_user_table.gif │ ├── example_perspective_dashboard.gif │ ├── layout_restoration.gif │ ├── logo.png │ └── ml_example.gif └── wiki │ ├── Build-from-Source.md │ ├── Contribute.md │ ├── Installation.md │ ├── Key-Features.md │ ├── Local-Development-Setup.md │ ├── _Footer.md │ ├── _Home.md │ ├── _Sidebar.md │ └── images │ ├── example_custom_metrics.gif │ ├── example_perspective_dashboard.gif │ ├── example_perspective_dashboard_layouts.gif │ └── example_task_metadata.gif ├── js ├── build.js ├── package.json ├── pnpm-lock.yaml └── src │ ├── index.css │ ├── index.html │ ├── index.js │ └── layouts │ └── default.json ├── pyproject.toml └── raydar ├── __init__.py ├── dashboard ├── __init__.py ├── demo.py └── server.py ├── task_tracker ├── __init__.py ├── schema.py └── task_tracker.py └── tests ├── __init__.py ├── conftest.py └── test_task_tracker.py /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-documentation 2 | *.ipynb linguist-documentation 3 | *.js text eol=lf 4 | *.py text eol=lf 5 | *.md text eol=lf 6 | docs/img/*.gif filter=lfs diff=lfs merge=lfs -text 7 | raydar/dashboard/static/index.psp2.js filter=lfs diff=lfs merge=lfs 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Team Review 2 | * @timkpaine @gauglertodd @ptomecek 3 | 4 | # CI/CD 5 | .github @timkpaine 6 | 7 | # Administrative 8 | LICENSE @ptomecek @timkpaine 9 | NOTICE @ptomecek @timkpaine 10 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | OpenSource@Point72.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | labels: 8 | - "part: github_actions" 9 | 10 | - package-ecosystem: "pip" 11 | directory: "/" 12 | schedule: 13 | interval: "monthly" 14 | labels: 15 | - "lang: python" 16 | - "part: dependencies" 17 | 18 | - package-ecosystem: "npm" 19 | directory: "/" 20 | schedule: 21 | interval: "monthly" 22 | labels: 23 | - "lang: javascript" 24 | - "part: dependencies" 25 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Status 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | paths-ignore: 10 | - LICENSE 11 | - README.md 12 | pull_request: 13 | workflow_dispatch: 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 17 | cancel-in-progress: true 18 | 19 | permissions: 20 | contents: read 21 | checks: write 22 | pull-requests: write 23 | 24 | jobs: 25 | build: 26 | runs-on: ${{ matrix.os }} 27 | 28 | strategy: 29 | matrix: 30 | # os: [ubuntu-latest, macos-latest, windows-latest] 31 | os: [ubuntu-latest, macos-latest] 32 | python-version: [3.9, 3.11] 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - name: Set up Python ${{ matrix.python-version }} 38 | uses: actions/setup-python@v5 39 | with: 40 | python-version: ${{ matrix.python-version }} 41 | cache: "pip" 42 | cache-dependency-path: 'pyproject.toml' 43 | 44 | - name: Install pnpm 45 | uses: pnpm/action-setup@v4 46 | with: 47 | version: 9 48 | package_json_file: js/package.json 49 | 50 | - name: Install dependencies 51 | run: make develop 52 | 53 | - name: Build 54 | run: make build 55 | 56 | - name: Lint 57 | run: make lint 58 | 59 | - name: Test 60 | run: make tests 61 | if: ${{ matrix.os == 'ubuntu-latest' }} 62 | 63 | - name: Upload test results (Python) 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: py-test-results-${{ matrix.os }}-${{ matrix.python-version }} 67 | path: junit.xml 68 | if: ${{ matrix.os == 'ubuntu-latest' }} 69 | 70 | - name: Upload test results (JS) 71 | uses: actions/upload-artifact@v4 72 | with: 73 | name: js-test-results-${{ matrix.os }}-${{ matrix.python-version }} 74 | path: js/junit.xml 75 | if: ${{ matrix.os == 'ubuntu-latest' }} 76 | 77 | - name: Publish Unit Test Results 78 | uses: EnricoMi/publish-unit-test-result-action@v2 79 | with: 80 | files: | 81 | **/junit.xml 82 | if: ${{ matrix.os == 'ubuntu-latest' }} 83 | 84 | - name: Twine check 85 | run: make dist 86 | 87 | -------------------------------------------------------------------------------- /.github/workflows/wiki.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "docs/**" 9 | - "README.md" 10 | workflow_dispatch: 11 | 12 | concurrency: 13 | group: docs 14 | cancel-in-progress: true 15 | 16 | permissions: 17 | contents: write 18 | 19 | jobs: 20 | deploy: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | 26 | - name: Make home file 27 | run: cat README.md docs/wiki/_Home.md > docs/wiki/Home.md 28 | 29 | - name: Upload Documentation to Wiki 30 | uses: Andrew-Chen-Wang/github-wiki-action@v4 31 | with: 32 | path: docs/wiki 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | python_junit.xml 44 | junit.xml 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | .pytest_cache 50 | .ruff_cache 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask instance folder 61 | instance/ 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | docs/source 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # IPython Notebook 74 | .ipynb_checkpoints 75 | *.ipynb 76 | .autoversion 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # ========================= 98 | # Operating System Files 99 | # ========================= 100 | 101 | # OSX 102 | # ========================= 103 | 104 | .DS_Store 105 | .AppleDouble 106 | .LSOverride 107 | 108 | # Thumbnails 109 | ._* 110 | 111 | # Files that might appear in the root of a volume 112 | .DocumentRevisions-V100 113 | .fseventsd 114 | .Spotlight-V100 115 | .TemporaryItems 116 | .Trashes 117 | .VolumeIcon.icns 118 | 119 | # Directories potentially created on remote AFP share 120 | .AppleDB 121 | .AppleDesktop 122 | Network Trash Folder 123 | Temporary Items 124 | .apdisk 125 | 126 | # Windows 127 | # ========================= 128 | 129 | # Windows image file caches 130 | Thumbs.db 131 | ehthumbs.db 132 | 133 | # Folder config file 134 | Desktop.ini 135 | 136 | # Recycle Bin used on file shares 137 | $RECYCLE.BIN/ 138 | 139 | # Windows Installer files 140 | *.cab 141 | *.msi 142 | *.msm 143 | *.msp 144 | 145 | # Windows shortcuts 146 | *.lnk 147 | 148 | 149 | # NPM 150 | # ---- 151 | **/node_modules/ 152 | 153 | # Coverage data 154 | # ------------- 155 | **/coverage/ 156 | 157 | # Compiled JS assets 158 | raydar/dashboard/static/* 159 | !raydar/dashboard/static/index.psp2.js -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2024 Point72, L.P. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############### 2 | # Build Tools # 3 | ############### 4 | .PHONY: build develop install 5 | 6 | build: ## build python/javascript 7 | python -m build . 8 | 9 | requirements: ## install prerequisite python build requirements 10 | python -m pip install --upgrade pip toml 11 | python -m pip install `python -c 'import toml; c = toml.load("pyproject.toml"); print("\n".join(c["build-system"]["requires"]))'` 12 | python -m pip install `python -c 'import toml; c = toml.load("pyproject.toml"); print(" ".join(c["project"]["optional-dependencies"]["develop"]))'` 13 | 14 | develop: ## install to site-packages in editable mode 15 | cd js; pnpm install 16 | python -m pip install -e .[develop] 17 | 18 | install: ## install to site-packages 19 | python -m pip install . 20 | 21 | ########### 22 | # Testing # 23 | ########### 24 | .PHONY: testpy testjs test tests 25 | 26 | testpy: ## run the python unit tests 27 | python -m pytest -v raydar/tests --junitxml=junit.xml --cov=raydar --cov-report=xml:.coverage.xml --cov-branch --cov-fail-under=1 --cov-report term-missing 28 | 29 | testjs: ## run the javascript unit tests 30 | cd js; pnpm run test 31 | 32 | test: tests 33 | tests: testpy testjs ## run all the unit tests 34 | 35 | ########### 36 | # Linting # 37 | ########### 38 | .PHONY: lintpy lintjs lint fixpy fixjs fix format 39 | 40 | lintpy: ## lint python with ruff 41 | python -m ruff check raydar 42 | python -m ruff format --check raydar 43 | 44 | lintjs: ## lint javascript with eslint 45 | cd js; pnpm run lint 46 | 47 | lint: lintpy lintjs ## run all linters 48 | 49 | fixpy: ## autoformat python code with isort and ruff 50 | python -m ruff check --fix raydar 51 | python -m ruff format raydar 52 | 53 | fixjs: ## autoformat javascript code with eslint 54 | cd js; pnpm run fix 55 | 56 | fix: fixpy fixjs ## run all autofixers 57 | format: fix 58 | 59 | ################# 60 | # Other Checks # 61 | ################# 62 | .PHONY: check checks check-manifest 63 | 64 | check: checks 65 | 66 | checks: check-manifest ## run security, packaging, and other checks 67 | 68 | check-manifest: ## run manifest checker for sdist 69 | check-manifest -v 70 | 71 | ################ 72 | # Distribution # 73 | ################ 74 | .PHONY: dist publishpy publishjs publish 75 | 76 | dist: clean build ## create dists 77 | python -m twine check dist/* 78 | 79 | publishpy: ## dist to pypi 80 | python -m twine upload dist/* --skip-existing 81 | 82 | publishjs: ## dist to npm 83 | cd js; npm publish || echo "can't publish - might already exist" 84 | 85 | publish: dist publishpy publishjs ## dist to pypi and npm 86 | 87 | ############ 88 | # Cleaning # 89 | ############ 90 | .PHONY: clean 91 | 92 | clean: ## clean the repository 93 | find . -name "__pycache__" | xargs rm -rf 94 | find . -name "*.pyc" | xargs rm -rf 95 | find . -name ".ipynb_checkpoints" | xargs rm -rf 96 | rm -rf .coverage coverage *.xml build dist *.egg-info lib node_modules .pytest_cache *.egg-info 97 | rm -rf raydar/dashboard/static 98 | cd js; pnpm run clean 99 | git clean -fd 100 | 101 | ########### 102 | # Helpers # 103 | ########### 104 | .PHONY: help 105 | 106 | # Thanks to Francoise at marmelab.com for this 107 | .DEFAULT_GOAL := help 108 | help: 109 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 110 | 111 | print-%: 112 | @echo '$*=$($*)' 113 | 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | raydar 3 | 4 |
5 |
6 | 7 | [![Build Status](https://github.com/Point72/raydar/actions/workflows/build.yml/badge.svg?branch=main&event=push)](https://github.com/Point72/raydar/actions/workflows/build.yml) 8 | [![GitHub issues](https://img.shields.io/github/issues/point72/raydar.svg)](https://github.com/point72/raydar/issues) 9 | [![PyPI Version](https://img.shields.io/pypi/v/raydar.svg)](https://pypi.python.org/pypi/raydar) 10 | [![License](https://img.shields.io/pypi/l/raydar.svg)](https://github.com/Point72/raydar/blob/main/LICENSE) 11 | 12 | A [perspective](https://perspective.finos.org/) powered, user editable ray dashboard via ray serve. 13 | 14 | Ray offers powerful metrics visualizations powered by graphana and prometheus. Although useful, the setup can take time - and customizations can be challenging. 15 | 16 | Raydar, enables out-of-the-box live cluster metrics and user visualizations for Ray workflows with just a simple pip install. It helps unlock distributed machine learning visualizations on Anyscale clusters, runs live and at scale, is easily customizable, and enables all the in-browser aggregations that [perspective](https://perspective.finos.org/) has to offer. 17 | 18 | ![Example](https://media.githubusercontent.com/media/Point72/raydar/refs/heads/main/docs/img/ml_example.gif) 19 | 20 | ## Features 21 | 22 | - Convenience wrappers for the tracking and persistence of ray GCS task metdata. Can scale beyond the existing ray dashboard / GCS task tracking limitations. 23 | - Serves a UI through [ray serve](https://docs.ray.io/en/latest/serve/index.html) for the vizualization of [perspective](https://github.com/finos/perspective) tables. 24 | - A python interface to create and update perspective tables from within ray tasks. 25 | 26 | [More information is available in our wiki](https://github.com/Point72/raydar/wiki) 27 | 28 | ## Installation 29 | 30 | `raydar` can be installed via [pip](https://pip.pypa.io) or [conda](https://docs.conda.io/en/latest/), the two primary package managers for the Python ecosystem. See [our wiki](https://github.com/Point72/raydar/wiki/Installation) for more information. 31 | 32 | ## Launching The UI, Tracking Tasks, Creating/Updating Custom Tables 33 | 34 | The raydar module provides an actor which can process collections of ray object references on your behalf, and can serve a perspective dashboard in which to visualize that data. 35 | 36 | ```python 37 | from raydar import RayTaskTracker 38 | task_tracker = RayTaskTracker(enable_perspective_dashboard=True) 39 | ``` 40 | 41 | Passing collections of object references to this actor's process method causes those references to be tracked in an internal polars dataframe, as they finish running. 42 | 43 | ```python 44 | @ray.remote 45 | def example_remote_function(): 46 | import time 47 | import random 48 | time.sleep(1) 49 | if random.randint(1,100) > 90: 50 | raise Exception("This task should sometimes fail!") 51 | return True 52 | 53 | refs = [example_remote_function.remote() for _ in range(100)] 54 | task_tracker.process(refs) 55 | ``` 56 | 57 | The perspective UI is served on port 8000 by default. 58 | 59 | ![Example](https://media.githubusercontent.com/media/Point72/raydar/refs/heads/main/docs/img/example_perspective_dashboard.gif) 60 | 61 | Passing a `name` and `namespace` arguments allows the RayTaskTracker to skip construction when an actor already exists. This also means we can access the correct ray actor handle from arbitrary ray code, once the correct name and namespace are provided. 62 | 63 | ```python 64 | from raydar import RayTaskTracker 65 | 66 | task_tracker = RayTaskTracker( 67 | enable_perspective_dashboard=True, 68 | name="my_actor_name", 69 | namespace="my_actor_namespace” 70 | ) 71 | 72 | task_tracker.create_table( 73 | table_name="demo_table", 74 | table_schema=dict( 75 | worker_id="str", 76 | metric_value="int", 77 | other_metric_value="float", 78 | timestamp="datetime” 79 | ) 80 | ) 81 | ``` 82 | 83 | Now, from an arbitrary remote function: 84 | 85 | ```python 86 | @ray.remote 87 | def add_data_to_demo_table(i): 88 | task_tracker = RayTaskTracker(name="my_actor_name", namespace="my_actor_namespace") 89 | 90 | import datetime 91 | import random 92 | data = dict( 93 | worker_id="worker_1", 94 | metric_value=i, 95 | other_metric_value=i * random.uniform(1.5, 1.8), 96 | timestamp = datetime.datetime.now() 97 | ) 98 | task_tracker.update_table("demo_table", [data]) 99 | ``` 100 | 101 | ![Example](https://media.githubusercontent.com/media/Point72/raydar/refs/heads/main/docs/img/custom_user_table.gif) 102 | 103 | ## FAQ 104 | 105 | - _Where is the perspective data stored?_ 106 | 107 | Currently, in memory. There are plans to integrate alternatives to this configuration, but currently the data is stored in machine memory on the ray head. 108 | 109 | - _How can I save and restore my perspective layouts?_ 110 | 111 | The `Save Layout` button saves a json file containing layout information. Dragging and dropping this file into the UI browser window restores that layout. 112 | 113 | ![Example](https://media.githubusercontent.com/media/Point72/raydar/refs/heads/main/docs/img/layout_restoration.gif) 114 | 115 | ## License 116 | 117 | This software is licensed under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details. 118 | -------------------------------------------------------------------------------- /docs/img/custom_user_table.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e29379c2ec028cea1e4812b7dbf3409814c8bae027754994c9693713a39e2b66 3 | size 4192860 4 | -------------------------------------------------------------------------------- /docs/img/example_perspective_dashboard.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:24518a51518a1ecc2cb82483adf0abe3fac9e0307d6c395c581219b674934574 3 | size 307734 4 | -------------------------------------------------------------------------------- /docs/img/layout_restoration.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:79dbfaf0887ad9eca77d0956dee10dee59a6d00264fa86fe8cc50a3f1152dbf5 3 | size 954120 4 | -------------------------------------------------------------------------------- /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Point72/raydar/7137824716788cd59c783c80fff652742d027ee5/docs/img/logo.png -------------------------------------------------------------------------------- /docs/img/ml_example.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:97aa0580cb3c62994436556b56cea02b512372235b81f09fa1a73bd2b8c50edd 3 | size 14800973 4 | -------------------------------------------------------------------------------- /docs/wiki/Build-from-Source.md: -------------------------------------------------------------------------------- 1 | `raydar` is written in Python and Javascript. While prebuilt wheels are provided for end users, it is also straightforward to build `raydar` from either the Python [source distribution](https://packaging.python.org/en/latest/specifications/source-distribution-format/) or the GitHub repository. 2 | 3 | ## Table of Contents 4 | 5 | - [Table of Contents](#table-of-contents) 6 | - [Make commands](#make-commands) 7 | - [Prerequisites](#prerequisites) 8 | - [Clone](#clone) 9 | - [Install NodeJS](#install-nodejs) 10 | - [Install Python dependencies](#install-python-dependencies) 11 | - [Build](#build) 12 | - [Lint and Autoformat](#lint-and-autoformat) 13 | - [Testing](#testing) 14 | 15 | ## Make commands 16 | 17 | As a convenience, `raydar` uses a `Makefile` for commonly used commands. You can print the main available commands by running `make` with no arguments 18 | 19 | ```bash 20 | > make 21 | 22 | build build the library 23 | clean clean the repository 24 | fix run autofixers 25 | install install library 26 | lint run lints 27 | test run the tests 28 | ``` 29 | 30 | ## Prerequisites 31 | 32 | `raydar` has a few system-level dependencies which you can install from your machine package manager. Other package managers like `conda`, `nix`, etc, should also work fine. 33 | 34 | ## Clone 35 | 36 | Clone the repo with: 37 | 38 | ```bash 39 | git clone https://github.com/Point72/raydar.git 40 | cd raydar 41 | ``` 42 | 43 | ## Install NodeJS 44 | 45 | Follow the instructions for [installing NodeJS](https://nodejs.org/en/download/package-manager/all) for your system. Once installed, you can [install `pnpm`](https://pnpm.io) with: 46 | 47 | ```bash 48 | npm install --global pnpm 49 | ``` 50 | 51 | ## Install Python dependencies 52 | 53 | Python build and develop dependencies are specified in the `pyproject.toml`, but you can manually install them: 54 | 55 | ```bash 56 | make requirements 57 | ``` 58 | 59 | Note that these dependencies would otherwise be installed normally as part of [PEP517](https://peps.python.org/pep-0517/) / [PEP518](https://peps.python.org/pep-0518/). 60 | 61 | ## Build 62 | 63 | Build the python project in the usual manner: 64 | 65 | ```bash 66 | make build 67 | ``` 68 | 69 | ## Lint and Autoformat 70 | 71 | `raydar` has linting and auto formatting. 72 | 73 | | Language | Linter | Autoformatter | Description | 74 | | :--------- | :--------- | :------------ | :---------- | 75 | | Python | `ruff` | `ruff` | Style | 76 | | Python | `isort` | `isort` | Imports | 77 | | JavaScript | `prettier` | `prettier` | Style | 78 | | Markdown | `prettier` | `prettier` | Style | 79 | 80 | **Python Linting** 81 | 82 | ```bash 83 | make lintpy 84 | ``` 85 | 86 | **Python Autoformatting** 87 | 88 | ```bash 89 | make fixpy 90 | ``` 91 | 92 | **JavaScript Linting** 93 | 94 | ```bash 95 | make lintjs 96 | ``` 97 | 98 | **JavaScript Autoformatting** 99 | 100 | ```bash 101 | make fixjs 102 | ``` 103 | 104 | **Documentation Linting** 105 | 106 | We use `prettier` for our Markdown linting, so follow the above docs. 107 | 108 | ## Testing 109 | 110 | `raydar` has both Python and JavaScript tests. The bulk of the functionality is tested in Python, which can be run via `pytest`. First, install the Python development dependencies with 111 | 112 | ```bash 113 | make develop 114 | ``` 115 | 116 | **Python** 117 | 118 | ```bash 119 | make testpy 120 | ``` 121 | 122 | **JavaScript** 123 | 124 | ```bash 125 | make testjs 126 | ``` 127 | -------------------------------------------------------------------------------- /docs/wiki/Contribute.md: -------------------------------------------------------------------------------- 1 | Contributions are welcome on this project. We distribute under the terms of the [Apache 2.0 license](https://github.com/Point72/raydar/blob/main/LICENSE). 2 | 3 | > \[!NOTE\] 4 | > 5 | > `raydar` requires [Developer Certificate of Origin](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin) for all contributions. 6 | > This is enforced by a [Probot GitHub App](https://probot.github.io/apps/dco/), which checks that commits are "signed". 7 | > Read [instructions to configure commit signing](Local-Development-Setup#configure-commit-signing). 8 | 9 | For **bug reports** or **small feature requests**, please open an issue on our [issues page](https://github.com/Point72/raydar/issues). 10 | 11 | For **questions** or to discuss **larger changes or features**, please use our [discussions page](https://github.com/Point72/raydar/discussions). 12 | 13 | For **contributions**, please see our [developer documentation](Local-Development-Setup). We have `help wanted` and `good first issue` tags on our issues page, so these are a great place to start. 14 | 15 | For **documentation updates**, make PRs that update the pages in `/docs/wiki`. The documentation is pushed to the GitHub wiki automatically through a GitHub workflow. Note that direct updates to this wiki will be overwritten. 16 | -------------------------------------------------------------------------------- /docs/wiki/Installation.md: -------------------------------------------------------------------------------- 1 | ## Pre-requisites 2 | 3 | `raydar` can be installed via [pip](https://pip.pypa.io) or [conda](https://docs.conda.io/en/latest/), the two primary package managers for the Python ecosystem. You need Python >=3.8 on your machine to install `raydar`. 4 | 5 | ## Install with `pip` 6 | 7 | ```bash 8 | pip install raydar 9 | ``` 10 | 11 | ## Install with `conda` 12 | 13 | ```bash 14 | conda install raydar --channel conda-forge 15 | ``` 16 | 17 | ## Source installation 18 | 19 | For other platforms and for development installations, [build `raydar` from source](Build-from-Source). 20 | -------------------------------------------------------------------------------- /docs/wiki/Key-Features.md: -------------------------------------------------------------------------------- 1 | # Key Features 2 | 3 | ## Task Tracker 4 | 5 | The `raydar` module provides an actor which can process collections of ray object references on your behalf, and can serve a [perspective](https://github.com/finos/perspective) dashboard in which to visualize that data. 6 | 7 | ```python 8 | from raydar import RayTaskTracker 9 | task_tracker = RayTaskTracker() 10 | ``` 11 | 12 | Passing collections of object references to this actor's `process` method causes those references to be tracked in an internal polars dataframe, as they finish running. 13 | 14 | ```python 15 | @ray.remote 16 | def example_remote_function(): 17 | import time 18 | import random 19 | time.sleep(1) 20 | if random.randint(1,100) > 90: 21 | raise Exception("This task should sometimes fail!") 22 | return True 23 | 24 | refs = [example_remote_function.remote() for _ in range(100)] 25 | task_tracker.process(refs) 26 | ``` 27 | 28 | This internal dataframe can be accessed via the `.get_df()` method. 29 | 30 | | task_id | user_defined_metadata | attempt_number | name | ... | start_time_ms | end_time_ms | task_log_info | error_message | 31 | | :------------ | :-------------------- | :------------- | :------------------------ | :-- | :------------------------------ | :------------------------------ | :------------------------------------ | :------------ | 32 | | `str` | `f32` | `i64` | `str` | | `datetime[ms,America/New_York]` | `datetime[ms,America/New_York]` | `struct[6]` | `str` | 33 | | | | | | | | | | | 34 | | 16310a0f0a... | `null` | 0 | `example_remote_function` | ... | 2024-01-29 07:17:09.340 EST | 2024-01-29 07:17:12.115 EST | `{"/tmp/ray/session_2024-01-29_07...` | `null` | 35 | | c2668a65bd... | `null` | 0 | `example_remote_function` | ... | 2024-01-29 07:17:09.341 EST | 2024-01-29 07:17:12.107 EST | `{"/tmp/ray/session_2024-01-29_07...` | `null` | 36 | | 32d950ec0c... | `null` | 0 | `example_remote_function` | ... | 2024-01-29 07:17:09.342 EST | 2024-01-29 07:17:12.115 EST | `{"/tmp/ray/session_2024-01-29_07...` | `null` | 37 | | e0dc174c83... | `null` | 0 | `example_remote_function` | ... | 2024-01-29 07:17:09.343 EST | 2024-01-29 07:17:12.115 EST | `{"/tmp/ray/session_2024-01-29_07...` | `null` | 38 | | f4402ec78d... | `null` | 0 | `example_remote_function` | ... | 2024-01-29 07:17:09.343 EST | 2024-01-29 07:17:12.115 EST | `{"/tmp/ray/session_2024-01-29_07...` | `null` | 39 | 40 | Additionally, setting the `enable_perspective_dashboard` flag to `True` in the `RayTaskTracker`'s construction serves a perspective dashboard with live views of your completed references. 41 | 42 | ```python 43 | task_tracker = RayTaskTracker(enable_perspective_dashboard=True) 44 | ``` 45 | 46 | ![Example](images/example_perspective_dashboard.gif) 47 | 48 | ## Create/Store Custom Views 49 | 50 | From the developer console, save your workspace layout locally. 51 | 52 | ```javascript 53 | let workspace = document.getElementById("perspective-workspace"); 54 | 55 | // Save the current layout 56 | workspace.save().then((config) => { 57 | // Convert the configuration object to a JSON string 58 | let json = JSON.stringify(config); 59 | 60 | // Create a Blob object from the JSON string 61 | let blob = new Blob([json], { type: "application/json" }); 62 | 63 | // Create a download link 64 | let link = document.createElement("a"); 65 | link.href = URL.createObjectURL(blob); 66 | link.download = "workspace.json"; 67 | 68 | // Append the link to the document body and click it to start the download 69 | document.body.appendChild(link); 70 | link.click(); 71 | document.body.removeChild(link); 72 | }); 73 | ``` 74 | 75 | Then, move this json file to `js/src/layouts/default.json`. 76 | 77 | ![Example](images/example_perspective_dashboard_layouts.gif) 78 | 79 | ## Expose Ray GCS Information 80 | 81 | The data available to you includes much of what Ray's GCS tracks, and also allows for user defined metadata per task. 82 | 83 | Specifically, tracked fields include: 84 | 85 | - `task_id` 86 | - `user_defined_metadata` 87 | - `attempt_number` 88 | - `name` 89 | - `state` 90 | - `job_id` 91 | - `actor_id` 92 | - `type` 93 | - `func_or_class_name` 94 | - `parent_task_id` 95 | - `node_id` 96 | - `worker_id` 97 | - `error_type` 98 | - `language` 99 | - `required_resources` 100 | - `runtime_env_info` 101 | - `placement_group_id` 102 | - `events` 103 | - `profiling_data` 104 | - `creation_time_ms` 105 | - `start_time_ms` 106 | - `end_time_ms` 107 | - `task_log_info` 108 | - `error_message` 109 | 110 | ![Example](images/example_task_metadata.gif) 111 | 112 | ## Custom Sources / Update Logic 113 | 114 | The proxy server helpd by the `RayTaskTracker` is exposed via the `.proxy_server()` property, meaning we can create new tables as follows: 115 | 116 | ```python 117 | task_tracker = RayTaskTracker(enable_perspective_dashboard=True) 118 | proxy_server = task_tracker.proxy_server() 119 | proxy_server.remote( 120 | "new", 121 | "metrics_table", 122 | { 123 | "node_id": "str", 124 | "metric_name": "str", 125 | "value": "float", 126 | "timestamp": "datetime", 127 | }, 128 | ) 129 | ``` 130 | 131 | ### Example: Live Per-Node Training Loss Metrics 132 | 133 | If a user were to then update this table with data coming from, for example, a pytorch model training loop with metrics: 134 | 135 | ```python 136 | def my_model_training_loop() 137 | 138 | for epoch in range(num_epochs): 139 | # ... my training code here ... 140 | 141 | data = dict( 142 | node_id=ray.get_runtime_context().get_node_id(), 143 | metric_name="loss", 144 | value=loss.item(), 145 | timestamp=time.time(), 146 | ) 147 | proxy_server.remote("update", "metrics_table", [data]) 148 | ``` 149 | 150 | Then they can expose a live view at per-node loss metrics across our model training process: 151 | 152 | ![Example](images/example_custom_metrics.gif) 153 | -------------------------------------------------------------------------------- /docs/wiki/Local-Development-Setup.md: -------------------------------------------------------------------------------- 1 | ## Table of Contents 2 | 3 | - [Table of Contents](#table-of-contents) 4 | - [Step 1: Build from Source](#step-1-build-from-source) 5 | - [Step 2: Configuring Git and GitHub for Development](#step-2-configuring-git-and-github-for-development) 6 | - [Create your fork](#create-your-fork) 7 | - [Configure remotes](#configure-remotes) 8 | - [Authenticating with GitHub](#authenticating-with-github) 9 | - [Guidelines](#guidelines) 10 | 11 | ## Step 1: Build from Source 12 | 13 | To work on `raydar`, you are going to need to build it from source. See 14 | [Build from Source](Build-from-Source) for 15 | detailed build instructions. 16 | 17 | Once you've built `raydar` from a `git` clone, you will also need to 18 | configure `git` and your GitHub account for `raydar` development. 19 | 20 | ## Step 2: Configuring Git and GitHub for Development 21 | 22 | ### Create your fork 23 | 24 | The first step is to create a personal fork of `raydar`. To do so, click 25 | the "fork" button at https://github.com/Point72/raydar, or just navigate 26 | [here](https://github.com/Point72/raydar/fork) in your browser. Set the 27 | owner of the repository to your personal GitHub account if it is not 28 | already set that way and click "Create fork". 29 | 30 | ### Configure remotes 31 | 32 | Next, you should set some names for the `git` remotes corresponding to 33 | main Point72 repository and your fork. See the [GitHub Docs](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/configuring-a-remote-repository-for-a-fork) for more information. 34 | 35 | ### Authenticating with GitHub 36 | 37 | If you have not already configured `ssh` access to GitHub, you can find 38 | instructions to do so 39 | [here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh), 40 | including instructions to create an SSH key if you have not done 41 | so. Authenticating with SSH is usually the easiest route. If you are working in 42 | an environment that does not allow SSH connections to GitHub, you can look into 43 | [configuring a hardware 44 | passkey](https://docs.github.com/en/authentication/authenticating-with-a-passkey/about-passkeys) 45 | or adding a [personal access 46 | token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) 47 | to avoid the need to type in your password every time you push to your fork. 48 | 49 | ## Guidelines 50 | 51 | After developing a change locally, ensure that both [lints](Build-from-Source#lint-and-autoformat) and [tests](Build-from-Source#testing) pass. Commits should be squashed into logical units, and all commits must be signed (e.g. with the `-s` git flag). We require [Developer Certificate of Origin](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin) for all contributions. 52 | 53 | If your work is still in-progress, open a [draft pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests). Otherwise, open a normal pull request. It might take a few days for a maintainer to review and provide feedback, so please be patient. If a maintainer asks for changes, please make said changes and squash your commits if necessary. If everything looks good to go, a maintainer will approve and merge your changes for inclusion in the next release. 54 | 55 | Please note that non substantive changes, large changes without prior discussion, etc, are not accepted and pull requests may be closed. 56 | -------------------------------------------------------------------------------- /docs/wiki/_Footer.md: -------------------------------------------------------------------------------- 1 | _This wiki is autogenerated. To made updates, open a PR against the original source file in [`docs/wiki`](https://github.com/Point72/raydar/tree/main/docs/wiki)._ 2 | -------------------------------------------------------------------------------- /docs/wiki/_Home.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Community 4 | 5 | - [Contribute](Contribute) to `raydar` and help improve the project 6 | 7 | > \[!TIP\] 8 | > 9 | > Find relevant docs with GitHub’s search function, use `repo:Point72/raydar type:wiki ` to search the documentation Wiki Pages. 10 | -------------------------------------------------------------------------------- /docs/wiki/_Sidebar.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | - [Home](Home) 10 | 11 | **Get Started** 12 | 13 | - [Installation](Installation) 14 | - [Key features](Key-Features) 15 | 16 | **Developer Guide** 17 | 18 | - [Contributing](Contribute) 19 | - [Development Setup](Local-Development-Setup) 20 | - [Build from Source](Build-from-Source) 21 | -------------------------------------------------------------------------------- /docs/wiki/images/example_custom_metrics.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Point72/raydar/7137824716788cd59c783c80fff652742d027ee5/docs/wiki/images/example_custom_metrics.gif -------------------------------------------------------------------------------- /docs/wiki/images/example_perspective_dashboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Point72/raydar/7137824716788cd59c783c80fff652742d027ee5/docs/wiki/images/example_perspective_dashboard.gif -------------------------------------------------------------------------------- /docs/wiki/images/example_perspective_dashboard_layouts.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Point72/raydar/7137824716788cd59c783c80fff652742d027ee5/docs/wiki/images/example_perspective_dashboard_layouts.gif -------------------------------------------------------------------------------- /docs/wiki/images/example_task_metadata.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Point72/raydar/7137824716788cd59c783c80fff652742d027ee5/docs/wiki/images/example_task_metadata.gif -------------------------------------------------------------------------------- /js/build.js: -------------------------------------------------------------------------------- 1 | import { PerspectiveEsbuildPlugin } from "@finos/perspective-esbuild-plugin"; 2 | import { build } from "esbuild"; 3 | import { BuildCss } from "@prospective.co/procss/target/cjs/procss.js"; 4 | import fs from "fs"; 5 | import { copyFile } from "fs/promises"; 6 | 7 | async function compile_css() { 8 | const builder = new BuildCss(""); 9 | builder.add("index.css", fs.readFileSync("./src/index.css").toString()); 10 | fs.writeFileSync("dist/index.css", builder.compile().get("index.css")); 11 | } 12 | 13 | async function build_all() { 14 | await copyFile("./src/index.html", "dist/index.html"); 15 | await copyFile("./src/layouts/default.json", "dist/layouts/default.json"); 16 | await compile_css(); 17 | await build({ 18 | entryPoints: ["src/index.js"], 19 | outfile: "dist/index.js", 20 | plugins: [PerspectiveEsbuildPlugin()], 21 | format: "esm", 22 | bundle: true, 23 | minify: true, 24 | loader: { 25 | ".ttf": "file", 26 | }, 27 | }); 28 | } 29 | 30 | build_all(); 31 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.4", 3 | "private": true, 4 | "name": "raydar", 5 | "repository": "https://github.com/point72/raydar", 6 | "author": "OpenSource@Point72.com", 7 | "license": "Apache-2.0", 8 | "type": "module", 9 | "scripts": { 10 | "build:pre": "mkdir -p dist/layouts ../raydar/dashboard/static", 11 | "build:esbuild": "node build.js", 12 | "build:cpjs": "cp -r ./dist/* ../raydar/dashboard/static", 13 | "build": "npm-run-all -s build:*", 14 | "clean": "rimraf dist lib coverage junit.xml", 15 | "lint": "prettier --check \"src/*.js\" \"src/*.html\" \"src/*.css\" \"*.js\" \"*.json\" \"../*.md\" \"../docs/wiki/*.md\"", 16 | "fix": "prettier --write \"src/*.js\" \"src/*.html\" \"src/*.css\" \"*.js\" \"*.json\" \"../*.md\" \"../docs/wiki/*.md\"", 17 | "test": ":", 18 | "watch:esbuild": "pnpm run build:esbuild --watch", 19 | "watch": "npm-run-all -p watch:*" 20 | }, 21 | "dependencies": { 22 | "@finos/perspective": "^3.4.3", 23 | "@finos/perspective-viewer": "^3.4.3", 24 | "@finos/perspective-viewer-d3fc": "^3.4.3", 25 | "@finos/perspective-viewer-datagrid": "^3.4.3", 26 | "@finos/perspective-workspace": "^3.4.3" 27 | }, 28 | "devDependencies": { 29 | "@finos/perspective-esbuild-plugin": "^3.2.1", 30 | "@prospective.co/procss": "^0.1.13", 31 | "esbuild": "^0.25.2", 32 | "esbuild-plugin-less": "^1.3.20", 33 | "eslint": "^9.24.0", 34 | "npm-run-all": "^4.1.5", 35 | "prettier": "^3.5.3", 36 | "rimraf": "^6.0.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /js/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@finos/perspective': 12 | specifier: ^3.4.3 13 | version: 3.4.3 14 | '@finos/perspective-viewer': 15 | specifier: ^3.4.3 16 | version: 3.4.3 17 | '@finos/perspective-viewer-d3fc': 18 | specifier: ^3.4.3 19 | version: 3.4.3(d3-brush@3.0.0)(d3-dispatch@3.0.1)(d3-fetch@3.0.1)(d3-path@3.1.0)(d3-random@3.0.1)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-shape@3.2.0)(d3-time@3.1.0)(d3-zoom@3.0.0) 20 | '@finos/perspective-viewer-datagrid': 21 | specifier: ^3.4.3 22 | version: 3.4.3 23 | '@finos/perspective-workspace': 24 | specifier: ^3.4.3 25 | version: 3.4.3 26 | devDependencies: 27 | '@finos/perspective-esbuild-plugin': 28 | specifier: ^3.2.1 29 | version: 3.2.1 30 | '@prospective.co/procss': 31 | specifier: ^0.1.13 32 | version: 0.1.16 33 | esbuild: 34 | specifier: ^0.25.2 35 | version: 0.25.2 36 | esbuild-plugin-less: 37 | specifier: ^1.3.20 38 | version: 1.3.20(esbuild@0.25.2) 39 | eslint: 40 | specifier: ^9.24.0 41 | version: 9.24.0 42 | npm-run-all: 43 | specifier: ^4.1.5 44 | version: 4.1.5 45 | prettier: 46 | specifier: ^3.5.3 47 | version: 3.5.3 48 | rimraf: 49 | specifier: ^6.0.1 50 | version: 6.0.1 51 | 52 | packages: 53 | 54 | '@d3fc/d3fc-annotation@3.0.16': 55 | resolution: {integrity: sha512-4tA7RHLUbj/i3KqWRqCVDOHi435qBRXbAFrAajVYM2juNzFdX5RCoF3opyXKOFncs339caxgeySGStTVfulO9w==} 56 | peerDependencies: 57 | d3-scale: '*' 58 | d3-selection: '*' 59 | 60 | '@d3fc/d3fc-axis@3.0.7': 61 | resolution: {integrity: sha512-S4pILxkQUkD7WQmimWxIEHfXUYEonlXWuWvMP6iq3KXL3d+cj4flvwNqLYZvSVaQDdK990cKkjrK/ZAKnReGlg==} 62 | peerDependencies: 63 | d3-scale: '*' 64 | d3-selection: '*' 65 | d3-shape: '*' 66 | 67 | '@d3fc/d3fc-brush@3.0.3': 68 | resolution: {integrity: sha512-fc1XBuNWl6DQVFSnBdauI/WNRvtNaeUWwDiLEIMY+v0VlrmGOgWEGCB5otVepGZiL56cjgRtHdwYBdKCvgGBSg==} 69 | peerDependencies: 70 | d3-brush: '*' 71 | d3-dispatch: '*' 72 | d3-scale: '*' 73 | d3-selection: '*' 74 | 75 | '@d3fc/d3fc-chart@5.1.9': 76 | resolution: {integrity: sha512-xFO9lDUi2wAkjfyWlVZuFwSWEgnk0WOEagR2R9KzQ+uxeg3oZOPzwE+AZbpALyQbjjQ2uIxcOk56xnmnnayk3w==} 77 | peerDependencies: 78 | d3-scale: '*' 79 | d3-selection: '*' 80 | 81 | '@d3fc/d3fc-data-join@6.0.3': 82 | resolution: {integrity: sha512-fd1D2Cl4YGjzl3gBhcrvTl/VxaSncY0ZcokWsN8ahtmk9DZK4DnAgHGrdecnXVLkOx+ANDcqxqscYz6MWXLbcA==} 83 | peerDependencies: 84 | d3-selection: '*' 85 | 86 | '@d3fc/d3fc-discontinuous-scale@4.1.1': 87 | resolution: {integrity: sha512-cyhtq4XPtK8RCSBtzctRAl4RkDyYJqJY0SJpi3QcfJz/OX9iB6At7UjITO7tAmJ7RUHb/xZvAVpJU8BM5gaQqg==} 88 | peerDependencies: 89 | d3-scale: '*' 90 | d3-time: '*' 91 | 92 | '@d3fc/d3fc-element@6.2.0': 93 | resolution: {integrity: sha512-AvdZ3V4mVxF9dGYLiDCoqr3GhrFOUQEc1FcP20QEhQ3fJ3qYRwx7/uhL7G/L2xbe6k4delPgnLOvtoaDenhpZw==} 94 | 95 | '@d3fc/d3fc-extent@4.0.2': 96 | resolution: {integrity: sha512-m7w7Dof6KAIDtgzIsTcprWTEoiqExJGsGoQbb97bF+EwIkuEZWRUl1jkoeNL00efpX1o6zSdqSr6lojoR0aI/g==} 97 | peerDependencies: 98 | d3-array: '*' 99 | 100 | '@d3fc/d3fc-financial-feed@7.1.0': 101 | resolution: {integrity: sha512-K8jktdRJQAiJepglErsuY2ZMKsm0YFWTeuhYnTFb8rWmyhwoPeem9QW+e6xBTiAvbElJm4yTrkal09KmO2cLlQ==} 102 | peerDependencies: 103 | d3-fetch: '*' 104 | 105 | '@d3fc/d3fc-group@3.0.1': 106 | resolution: {integrity: sha512-GBUR6a4hkqfSo77iaFS4qPMS5tupH8hmJ8eniiD45GFmWQs69+Dlf8Uhx+GCCvQkE+px6rQyZWVCJKBq6gkz2Q==} 107 | 108 | '@d3fc/d3fc-label-layout@7.0.4': 109 | resolution: {integrity: sha512-4CCyOx6uQA/Eq1VHnNy9dpI5QE2sDd1EP4W0Nw2rwnhFtlQqv37V38b9y5zT4YcdFrEgJqSB81fXIt3ZK1yxSg==} 110 | peerDependencies: 111 | d3-array: '*' 112 | d3-scale: '*' 113 | d3-selection: '*' 114 | 115 | '@d3fc/d3fc-pointer@3.0.3': 116 | resolution: {integrity: sha512-hXY7LqliDEJBH/do4YZusdLoikLYlWoN7efPC7YKYJ8igoEQFa48BqEHcANLs1qH+r7vzL2SS8V39MzgnJ1yQA==} 117 | peerDependencies: 118 | d3-dispatch: '*' 119 | d3-selection: '*' 120 | 121 | '@d3fc/d3fc-random-data@4.0.2': 122 | resolution: {integrity: sha512-T7+PbG1n23jyVMOWIuHJjY12PhPcYeRShuF0MK0ohifcNmwslurFMQKLBWkZk4x19In6JX7lNR0lwsaUUrcZNA==} 123 | peerDependencies: 124 | d3-random: '*' 125 | d3-time: '*' 126 | 127 | '@d3fc/d3fc-rebind@6.0.1': 128 | resolution: {integrity: sha512-+ryBZ53ALMffbADwnFAtTYQJcT7PE5BwpducGYS0X6Jux6ESnp+fP+cDQvBGbDBOVqaziGnfeLeJXjtMnZujmQ==} 129 | 130 | '@d3fc/d3fc-sample@5.0.2': 131 | resolution: {integrity: sha512-+ZJu+TOL4MFzFvAYc+QqDKude9DS7x4uK+133vCknAlSxL/l8Sh6ecWFXTSaC8tqHGywT7SK1arf0DiYCyS3Nw==} 132 | peerDependencies: 133 | d3-array: '*' 134 | 135 | '@d3fc/d3fc-series@6.1.3': 136 | resolution: {integrity: sha512-OSbt60SohTIib1xihX9ufneyJY7s9Feg9hvXVyEBZEBkwNE1NaeesZJ4nkmslu39RWuwsvpK3apL/XuBw7i5WA==} 137 | peerDependencies: 138 | d3-array: '*' 139 | d3-scale: '*' 140 | d3-scale-chromatic: '*' 141 | d3-selection: '*' 142 | d3-shape: '*' 143 | 144 | '@d3fc/d3fc-shape@6.0.1': 145 | resolution: {integrity: sha512-/dD3S8BWrOjO2mSptUmwe38V7KG4Kw6liIE5NXZJjX/XidfZhuDu7WWuya3i90HeNYDZNcs6Z+4qM3FnvlZf8g==} 146 | peerDependencies: 147 | d3-path: '*' 148 | 149 | '@d3fc/d3fc-technical-indicator@8.1.1': 150 | resolution: {integrity: sha512-ci5q+/4jCbbnT9M/JnrsBoHJfNJI4HFeCGVT+sy221OTdaFnrLj+IELFKFUS2h0uEOSu+CYT3D1K0BcAnMooxA==} 151 | peerDependencies: 152 | d3-array: '*' 153 | 154 | '@d3fc/d3fc-webgl@3.2.1': 155 | resolution: {integrity: sha512-yNYHW/tC05rrJs5fpVM5zdmInL8NH6UP09ZJ2tmKw62ELYHWySV8vuXOVZN3V/3WihPa60p2w23H0Hrp2b5qeg==} 156 | peerDependencies: 157 | d3-scale: '*' 158 | d3-shape: '*' 159 | 160 | '@d3fc/d3fc-zoom@1.2.0': 161 | resolution: {integrity: sha512-NfY6gPfcatw1gUtZoWq17SA7LV00t7Rl3eq6Bgj17++7K1H77Gpsb1hFa3XhzF85RrODuUzQW33/IIMYPgim9A==} 162 | peerDependencies: 163 | d3-dispatch: '*' 164 | d3-selection: '*' 165 | d3-zoom: '*' 166 | 167 | '@esbuild/aix-ppc64@0.25.2': 168 | resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} 169 | engines: {node: '>=18'} 170 | cpu: [ppc64] 171 | os: [aix] 172 | 173 | '@esbuild/android-arm64@0.25.2': 174 | resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} 175 | engines: {node: '>=18'} 176 | cpu: [arm64] 177 | os: [android] 178 | 179 | '@esbuild/android-arm@0.25.2': 180 | resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} 181 | engines: {node: '>=18'} 182 | cpu: [arm] 183 | os: [android] 184 | 185 | '@esbuild/android-x64@0.25.2': 186 | resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} 187 | engines: {node: '>=18'} 188 | cpu: [x64] 189 | os: [android] 190 | 191 | '@esbuild/darwin-arm64@0.25.2': 192 | resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} 193 | engines: {node: '>=18'} 194 | cpu: [arm64] 195 | os: [darwin] 196 | 197 | '@esbuild/darwin-x64@0.25.2': 198 | resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} 199 | engines: {node: '>=18'} 200 | cpu: [x64] 201 | os: [darwin] 202 | 203 | '@esbuild/freebsd-arm64@0.25.2': 204 | resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} 205 | engines: {node: '>=18'} 206 | cpu: [arm64] 207 | os: [freebsd] 208 | 209 | '@esbuild/freebsd-x64@0.25.2': 210 | resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} 211 | engines: {node: '>=18'} 212 | cpu: [x64] 213 | os: [freebsd] 214 | 215 | '@esbuild/linux-arm64@0.25.2': 216 | resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} 217 | engines: {node: '>=18'} 218 | cpu: [arm64] 219 | os: [linux] 220 | 221 | '@esbuild/linux-arm@0.25.2': 222 | resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} 223 | engines: {node: '>=18'} 224 | cpu: [arm] 225 | os: [linux] 226 | 227 | '@esbuild/linux-ia32@0.25.2': 228 | resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} 229 | engines: {node: '>=18'} 230 | cpu: [ia32] 231 | os: [linux] 232 | 233 | '@esbuild/linux-loong64@0.25.2': 234 | resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} 235 | engines: {node: '>=18'} 236 | cpu: [loong64] 237 | os: [linux] 238 | 239 | '@esbuild/linux-mips64el@0.25.2': 240 | resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} 241 | engines: {node: '>=18'} 242 | cpu: [mips64el] 243 | os: [linux] 244 | 245 | '@esbuild/linux-ppc64@0.25.2': 246 | resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} 247 | engines: {node: '>=18'} 248 | cpu: [ppc64] 249 | os: [linux] 250 | 251 | '@esbuild/linux-riscv64@0.25.2': 252 | resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} 253 | engines: {node: '>=18'} 254 | cpu: [riscv64] 255 | os: [linux] 256 | 257 | '@esbuild/linux-s390x@0.25.2': 258 | resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} 259 | engines: {node: '>=18'} 260 | cpu: [s390x] 261 | os: [linux] 262 | 263 | '@esbuild/linux-x64@0.25.2': 264 | resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} 265 | engines: {node: '>=18'} 266 | cpu: [x64] 267 | os: [linux] 268 | 269 | '@esbuild/netbsd-arm64@0.25.2': 270 | resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} 271 | engines: {node: '>=18'} 272 | cpu: [arm64] 273 | os: [netbsd] 274 | 275 | '@esbuild/netbsd-x64@0.25.2': 276 | resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} 277 | engines: {node: '>=18'} 278 | cpu: [x64] 279 | os: [netbsd] 280 | 281 | '@esbuild/openbsd-arm64@0.25.2': 282 | resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} 283 | engines: {node: '>=18'} 284 | cpu: [arm64] 285 | os: [openbsd] 286 | 287 | '@esbuild/openbsd-x64@0.25.2': 288 | resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} 289 | engines: {node: '>=18'} 290 | cpu: [x64] 291 | os: [openbsd] 292 | 293 | '@esbuild/sunos-x64@0.25.2': 294 | resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} 295 | engines: {node: '>=18'} 296 | cpu: [x64] 297 | os: [sunos] 298 | 299 | '@esbuild/win32-arm64@0.25.2': 300 | resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} 301 | engines: {node: '>=18'} 302 | cpu: [arm64] 303 | os: [win32] 304 | 305 | '@esbuild/win32-ia32@0.25.2': 306 | resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} 307 | engines: {node: '>=18'} 308 | cpu: [ia32] 309 | os: [win32] 310 | 311 | '@esbuild/win32-x64@0.25.2': 312 | resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} 313 | engines: {node: '>=18'} 314 | cpu: [x64] 315 | os: [win32] 316 | 317 | '@eslint-community/eslint-utils@4.5.1': 318 | resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} 319 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 320 | peerDependencies: 321 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 322 | 323 | '@eslint-community/regexpp@4.12.1': 324 | resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 325 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 326 | 327 | '@eslint/config-array@0.20.0': 328 | resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} 329 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 330 | 331 | '@eslint/config-helpers@0.2.1': 332 | resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} 333 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 334 | 335 | '@eslint/core@0.12.0': 336 | resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} 337 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 338 | 339 | '@eslint/core@0.13.0': 340 | resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} 341 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 342 | 343 | '@eslint/eslintrc@3.3.1': 344 | resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} 345 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 346 | 347 | '@eslint/js@9.24.0': 348 | resolution: {integrity: sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==} 349 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 350 | 351 | '@eslint/object-schema@2.1.6': 352 | resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} 353 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 354 | 355 | '@eslint/plugin-kit@0.2.8': 356 | resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} 357 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 358 | 359 | '@finos/perspective-esbuild-plugin@3.2.1': 360 | resolution: {integrity: sha512-6EqacFm5rGA0EtzD2p9RuxtVLEci9AEC4NPiOCpqFdgxxX8Msv8LlL7Ml3opxyaEeKpndVsLFdOD5U+PNaaa+A==} 361 | 362 | '@finos/perspective-viewer-d3fc@3.4.3': 363 | resolution: {integrity: sha512-7FdIUwQjUhQuAeWDljtCR1CDxWxKd+KF+/JjTuLRjKfmOgfqHafsUkhxPSZcuP54AH4NKsK8QoQNGxQ1Vla2Sg==} 364 | 365 | '@finos/perspective-viewer-datagrid@3.4.3': 366 | resolution: {integrity: sha512-bjx56+qPG1PFzh6xw336JYtSFpuFTrTzFVvBmPBU0kdKi2nm+TdwDJic1uEV49eC/sgGgB3Kv2OY2e4rzOCkFg==} 367 | 368 | '@finos/perspective-viewer@3.4.3': 369 | resolution: {integrity: sha512-I25kPLYAAs9CPKPq9SAtu7nW5SagpdXZvB60FhGwacJrNnkNjVzaMbIzAi7xk3NGJeBrTv6u6U+8bpU0te470A==} 370 | 371 | '@finos/perspective-workspace@3.4.3': 372 | resolution: {integrity: sha512-PYOIXEGfxqPyJeJNrt7kSXE3SbRQj7zpeFg9aP8BxlPcy9E48DLfA/ZJ6gPJkQE/SZ0s7dYRXWYs2DGN9Yg2Ng==} 373 | 374 | '@finos/perspective@3.4.3': 375 | resolution: {integrity: sha512-FH0c9eSP8x/uiG042ud3aerYRg0jBL3PvfrTYl268xGXRjeN+d4vCmtjXg0WsUyJhLh+xtMFhLMW7/4MnayrJg==} 376 | 377 | '@humanfs/core@0.19.1': 378 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 379 | engines: {node: '>=18.18.0'} 380 | 381 | '@humanfs/node@0.16.6': 382 | resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} 383 | engines: {node: '>=18.18.0'} 384 | 385 | '@humanwhocodes/module-importer@1.0.1': 386 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 387 | engines: {node: '>=12.22'} 388 | 389 | '@humanwhocodes/retry@0.3.1': 390 | resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} 391 | engines: {node: '>=18.18'} 392 | 393 | '@humanwhocodes/retry@0.4.2': 394 | resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} 395 | engines: {node: '>=18.18'} 396 | 397 | '@isaacs/cliui@8.0.2': 398 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 399 | engines: {node: '>=12'} 400 | 401 | '@lumino/algorithm@2.0.3': 402 | resolution: {integrity: sha512-DIcF7cIrGEC1Wh8DNjdwaL7IdcNs4Jj1VjO/90oHefeQPszKgc6DSfCxvbQiRanKR6tl/JL7tq4ZRPZES2oVAA==} 403 | 404 | '@lumino/collections@2.0.3': 405 | resolution: {integrity: sha512-Y9Wtvuk8SD6xcKgkqe3SUx5RywOcRz6DyWE4f5FvBT3OdANq76p9r0wtf8OKPt4BGE41kAQR+bdq3k+MfHlUsQ==} 406 | 407 | '@lumino/commands@2.3.2': 408 | resolution: {integrity: sha512-aAFEiUpp2hrkQU82Z85w1L80g0iDzsQRncLBa+pqVR/k0k1lz6H9F6xZ1ff+lBumZKKtsxBxNomvd0hfxLLqGw==} 409 | 410 | '@lumino/coreutils@2.2.1': 411 | resolution: {integrity: sha512-yij4TnxDIum7xfFUsVvZB0oLv4shs2mNbn3juwtEIsruvVBPmurNzKX0Y8z2QetbP2AZ6MSFtBzEKsihf0H0VA==} 412 | 413 | '@lumino/disposable@2.1.4': 414 | resolution: {integrity: sha512-qTJiDbglPE2QnG4x4gtBcRbcfKQibxyyinNGKcNDrcK2TGTbbhK5PpMQ8d70l2V2Xw2pb/LfksBAg5pxkJ/G4A==} 415 | 416 | '@lumino/domutils@2.0.3': 417 | resolution: {integrity: sha512-bXAbZg3mf2ZDNdBBpCGDike3U+osRGHePTh8H2Ud2KwaN4g/5IryFJm/TiO4K5IYs91bWF2Zqhf3FsdbZKHlGw==} 418 | 419 | '@lumino/dragdrop@2.1.6': 420 | resolution: {integrity: sha512-N9aqdOYl5HTuTAIVvjTXxlPKK3e9JAj5yEtJ4nA/tNE7J8C9y3BRQ4fBAtk7O0z/8yx8tO8a0j5oSt2zAfvYuA==} 421 | 422 | '@lumino/keyboard@2.0.3': 423 | resolution: {integrity: sha512-bU2OxAR8a9eNBdV0YFjU6/lVVpbOw1gM7yHOuDGDdNu4J0UpKapFoR9gopNGSaHTmTwDtx9RHdFfIAgHwjZ+VQ==} 424 | 425 | '@lumino/messaging@2.0.3': 426 | resolution: {integrity: sha512-//NAE+FG9UWSoXs4i5imduGY1XDvhjMGNF82aMyFTv8wVU6bdRfRmj0xLlQ8ixzA0eXPOFL4ugWZNdlu48P1+Q==} 427 | 428 | '@lumino/properties@2.0.3': 429 | resolution: {integrity: sha512-zkXIU5uYz/ScHCHGl5Bt4gMYsfPxZEduZd80zqDslBWvTIMro3NnzLe66NMnecbdr5N3hDJagYyA8//Qy3XjiA==} 430 | 431 | '@lumino/signaling@2.1.4': 432 | resolution: {integrity: sha512-nC5Z6d9om369Jkh1Vp3b7C89hV4cjr1fQDVcxhemyKXwc9r6VW7FpKixC+jElcAknP5KLj1FAa8Np+K06mMkEA==} 433 | 434 | '@lumino/virtualdom@2.0.3': 435 | resolution: {integrity: sha512-q2C8eBxPvvOOQjN3KuxZ+vJi082JH/GF7KwMdaWsy5g+7wjKdnXPuLQFTBLOrVqIzmbxBDlLeFr93CEhdQXcyQ==} 436 | 437 | '@lumino/widgets@2.7.0': 438 | resolution: {integrity: sha512-I7vXPY0YFo2p9JOI0M5Nh5qnF+cGOCwtTwx+T5f4mtfaJFKsA0SY8hE/Z8IgDswMiY2cYpvSSj220Ilh/qtg7Q==} 439 | 440 | '@prospective.co/procss@0.1.16': 441 | resolution: {integrity: sha512-Glfk6HVFVMAZL1rYsNicsODiFrikgyquYkqePWnAL+IyFDu6wfM4SHw8i/0mYuQmIxi0FR2IJJxJNiGAD86v4g==} 442 | 443 | '@types/d3-selection@1.0.10': 444 | resolution: {integrity: sha512-mHICSFHpIwgTycsvgINYCwItk039eofbGRzVNdeUUtv0S2BD1vXFFUKaeMJN3ARbVl+hlsVOIwdzhzub5tjr6Q==} 445 | 446 | '@types/estree@1.0.7': 447 | resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} 448 | 449 | '@types/json-schema@7.0.15': 450 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 451 | 452 | '@types/less@3.0.8': 453 | resolution: {integrity: sha512-Gjm4+H9noDJgu5EdT3rUw5MhPBag46fiOy27BefvWkNL8mlZnKnCaVVVTLKj6RYXed9b62CPKnPav9govyQDzA==} 454 | 455 | acorn-jsx@5.3.2: 456 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 457 | peerDependencies: 458 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 459 | 460 | acorn@8.14.1: 461 | resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} 462 | engines: {node: '>=0.4.0'} 463 | hasBin: true 464 | 465 | ajv@6.12.6: 466 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 467 | 468 | ansi-regex@5.0.1: 469 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 470 | engines: {node: '>=8'} 471 | 472 | ansi-regex@6.1.0: 473 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 474 | engines: {node: '>=12'} 475 | 476 | ansi-styles@3.2.1: 477 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 478 | engines: {node: '>=4'} 479 | 480 | ansi-styles@4.3.0: 481 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 482 | engines: {node: '>=8'} 483 | 484 | ansi-styles@6.2.1: 485 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 486 | engines: {node: '>=12'} 487 | 488 | argparse@2.0.1: 489 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 490 | 491 | array-buffer-byte-length@1.0.1: 492 | resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} 493 | engines: {node: '>= 0.4'} 494 | 495 | arraybuffer.prototype.slice@1.0.3: 496 | resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} 497 | engines: {node: '>= 0.4'} 498 | 499 | available-typed-arrays@1.0.7: 500 | resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} 501 | engines: {node: '>= 0.4'} 502 | 503 | balanced-match@1.0.2: 504 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 505 | 506 | brace-expansion@1.1.11: 507 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 508 | 509 | brace-expansion@2.0.1: 510 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 511 | 512 | call-bind@1.0.7: 513 | resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} 514 | engines: {node: '>= 0.4'} 515 | 516 | callsites@3.1.0: 517 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 518 | engines: {node: '>=6'} 519 | 520 | chalk@2.4.2: 521 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 522 | engines: {node: '>=4'} 523 | 524 | chalk@4.1.2: 525 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 526 | engines: {node: '>=10'} 527 | 528 | chownr@2.0.0: 529 | resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} 530 | engines: {node: '>=10'} 531 | 532 | chroma-js@1.4.1: 533 | resolution: {integrity: sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ==} 534 | 535 | color-convert@1.9.3: 536 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 537 | 538 | color-convert@2.0.1: 539 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 540 | engines: {node: '>=7.0.0'} 541 | 542 | color-name@1.1.3: 543 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 544 | 545 | color-name@1.1.4: 546 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 547 | 548 | commander@7.2.0: 549 | resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} 550 | engines: {node: '>= 10'} 551 | 552 | concat-map@0.0.1: 553 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 554 | 555 | copy-anything@2.0.6: 556 | resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} 557 | 558 | cross-spawn@6.0.6: 559 | resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} 560 | engines: {node: '>=4.8'} 561 | 562 | cross-spawn@7.0.5: 563 | resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} 564 | engines: {node: '>= 8'} 565 | 566 | cross-spawn@7.0.6: 567 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 568 | engines: {node: '>= 8'} 569 | 570 | d3-array@1.0.1: 571 | resolution: {integrity: sha512-VPS5OH5Xb43tkFkxHEc4r5yWhlDwST47zh1q+qvgTj7xB9xDXn+UEcofhvNC7s8gD55y9Q/MCSPSBUVvnzo3Dw==} 572 | 573 | d3-array@3.2.4: 574 | resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} 575 | engines: {node: '>=12'} 576 | 577 | d3-axis@3.0.0: 578 | resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} 579 | engines: {node: '>=12'} 580 | 581 | d3-brush@3.0.0: 582 | resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} 583 | engines: {node: '>=12'} 584 | 585 | d3-chord@3.0.1: 586 | resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} 587 | engines: {node: '>=12'} 588 | 589 | d3-collection@1.0.7: 590 | resolution: {integrity: sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==} 591 | 592 | d3-color@1.4.1: 593 | resolution: {integrity: sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==} 594 | 595 | d3-color@3.1.0: 596 | resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} 597 | engines: {node: '>=12'} 598 | 599 | d3-contour@4.0.2: 600 | resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} 601 | engines: {node: '>=12'} 602 | 603 | d3-delaunay@6.0.4: 604 | resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} 605 | engines: {node: '>=12'} 606 | 607 | d3-dispatch@1.0.1: 608 | resolution: {integrity: sha512-BRTp95mobTSKx8EtpOLbxXuYVtNNr0PmelkH9Uzg5cgcO5O1M0i3+2C0FeM2I95BwQoIlsuZXQTPIoIt5xOtmw==} 609 | 610 | d3-dispatch@3.0.1: 611 | resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} 612 | engines: {node: '>=12'} 613 | 614 | d3-drag@3.0.0: 615 | resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} 616 | engines: {node: '>=12'} 617 | 618 | d3-dsv@3.0.1: 619 | resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} 620 | engines: {node: '>=12'} 621 | hasBin: true 622 | 623 | d3-ease@1.0.7: 624 | resolution: {integrity: sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==} 625 | 626 | d3-ease@3.0.1: 627 | resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} 628 | engines: {node: '>=12'} 629 | 630 | d3-fetch@3.0.1: 631 | resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} 632 | engines: {node: '>=12'} 633 | 634 | d3-force@3.0.0: 635 | resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} 636 | engines: {node: '>=12'} 637 | 638 | d3-format@1.0.2: 639 | resolution: {integrity: sha512-VHFdLLjGkeGrRL8T/rlIIDhI3vvVX/oOTM/GaDJfB1sIb4dU5ZgiEjg3EeidJdQ/70u60tM015TSWa1gqqLRhg==} 640 | 641 | d3-format@3.1.0: 642 | resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} 643 | engines: {node: '>=12'} 644 | 645 | d3-geo@3.1.1: 646 | resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} 647 | engines: {node: '>=12'} 648 | 649 | d3-hierarchy@3.1.2: 650 | resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} 651 | engines: {node: '>=12'} 652 | 653 | d3-interpolate@1.4.0: 654 | resolution: {integrity: sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==} 655 | 656 | d3-interpolate@3.0.1: 657 | resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} 658 | engines: {node: '>=12'} 659 | 660 | d3-path@3.1.0: 661 | resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} 662 | engines: {node: '>=12'} 663 | 664 | d3-polygon@3.0.1: 665 | resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} 666 | engines: {node: '>=12'} 667 | 668 | d3-quadtree@3.0.1: 669 | resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} 670 | engines: {node: '>=12'} 671 | 672 | d3-random@3.0.1: 673 | resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} 674 | engines: {node: '>=12'} 675 | 676 | d3-scale-chromatic@3.1.0: 677 | resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} 678 | engines: {node: '>=12'} 679 | 680 | d3-scale@1.0.3: 681 | resolution: {integrity: sha512-ah2Xqywu96gau2iET3T0ZTsu0/X0gfoB8vDTuZ1OaG5F0SgGJLXreBVBknSZf2HKnxjenRvFok3qY2FgY4RpFg==} 682 | 683 | d3-scale@4.0.2: 684 | resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} 685 | engines: {node: '>=12'} 686 | 687 | d3-selection@1.0.2: 688 | resolution: {integrity: sha512-nInNdsdhljkDqkU/83bdWwtiJ7xsX3l57YZMlqsAOMeQROeCv7osPqQgYnao0NmRZEGc11hNakY+EOkaIdsWpQ==} 689 | 690 | d3-selection@3.0.0: 691 | resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} 692 | engines: {node: '>=12'} 693 | 694 | d3-shape@3.2.0: 695 | resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} 696 | engines: {node: '>=12'} 697 | 698 | d3-svg-legend@2.25.6: 699 | resolution: {integrity: sha512-6dueSjQr3+g9SlQ1SOzc4V58cCjjBeyo4WEcY8PW80i9XD/s562W/4xk05bpky0vzQx+i2XmXj3CYT+9KIRlnw==} 700 | 701 | d3-time-format@2.3.0: 702 | resolution: {integrity: sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==} 703 | 704 | d3-time-format@4.1.0: 705 | resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} 706 | engines: {node: '>=12'} 707 | 708 | d3-time@1.1.0: 709 | resolution: {integrity: sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==} 710 | 711 | d3-time@3.1.0: 712 | resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} 713 | engines: {node: '>=12'} 714 | 715 | d3-timer@1.0.10: 716 | resolution: {integrity: sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==} 717 | 718 | d3-timer@3.0.1: 719 | resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} 720 | engines: {node: '>=12'} 721 | 722 | d3-transition@1.0.3: 723 | resolution: {integrity: sha512-Facxcbma0nA2GVrx7B/Mgnn5ju6SwUMzGa9YcYmQjpqmaIq1Zbp5vVJLjtH6b08Lu0vcX7O6a4z+AlLmdCxrCQ==} 724 | 725 | d3-transition@3.0.1: 726 | resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} 727 | engines: {node: '>=12'} 728 | peerDependencies: 729 | d3-selection: 2 - 3 730 | 731 | d3-zoom@3.0.0: 732 | resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} 733 | engines: {node: '>=12'} 734 | 735 | d3@7.9.0: 736 | resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} 737 | engines: {node: '>=12'} 738 | 739 | d3fc@15.2.13: 740 | resolution: {integrity: sha512-wfBvY9TcoeV/bRxPjawlzDHS/r2y03STspyZc9ydiz4ANMt+y+ynL9oahTZ4aAbp5JK850VxWNkQ8jaaN/cDYg==} 741 | 742 | data-view-buffer@1.0.1: 743 | resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} 744 | engines: {node: '>= 0.4'} 745 | 746 | data-view-byte-length@1.0.1: 747 | resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} 748 | engines: {node: '>= 0.4'} 749 | 750 | data-view-byte-offset@1.0.0: 751 | resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} 752 | engines: {node: '>= 0.4'} 753 | 754 | debug@4.4.0: 755 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 756 | engines: {node: '>=6.0'} 757 | peerDependencies: 758 | supports-color: '*' 759 | peerDependenciesMeta: 760 | supports-color: 761 | optional: true 762 | 763 | deep-is@0.1.4: 764 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 765 | 766 | define-data-property@1.1.4: 767 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 768 | engines: {node: '>= 0.4'} 769 | 770 | define-properties@1.2.1: 771 | resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} 772 | engines: {node: '>= 0.4'} 773 | 774 | delaunator@5.0.1: 775 | resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} 776 | 777 | eastasianwidth@0.2.0: 778 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 779 | 780 | emoji-regex@8.0.0: 781 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 782 | 783 | emoji-regex@9.2.2: 784 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 785 | 786 | errno@0.1.8: 787 | resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} 788 | hasBin: true 789 | 790 | error-ex@1.3.2: 791 | resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} 792 | 793 | es-abstract@1.23.5: 794 | resolution: {integrity: sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==} 795 | engines: {node: '>= 0.4'} 796 | 797 | es-define-property@1.0.0: 798 | resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} 799 | engines: {node: '>= 0.4'} 800 | 801 | es-errors@1.3.0: 802 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 803 | engines: {node: '>= 0.4'} 804 | 805 | es-object-atoms@1.0.0: 806 | resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} 807 | engines: {node: '>= 0.4'} 808 | 809 | es-set-tostringtag@2.0.3: 810 | resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} 811 | engines: {node: '>= 0.4'} 812 | 813 | es-to-primitive@1.2.1: 814 | resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} 815 | engines: {node: '>= 0.4'} 816 | 817 | esbuild-plugin-less@1.3.20: 818 | resolution: {integrity: sha512-35voRNMF5uJZpEUbxp54JTmf/50ek9T2wTvEq//cnfwokzjWhvf2xEdzZtDrTmxW26og1ra4I36xp4P74R62xw==} 819 | peerDependencies: 820 | esbuild: '>=0.14.0 <0.25.3' 821 | 822 | esbuild@0.25.2: 823 | resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} 824 | engines: {node: '>=18'} 825 | hasBin: true 826 | 827 | escape-string-regexp@1.0.5: 828 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 829 | engines: {node: '>=0.8.0'} 830 | 831 | escape-string-regexp@4.0.0: 832 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 833 | engines: {node: '>=10'} 834 | 835 | eslint-scope@8.3.0: 836 | resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} 837 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 838 | 839 | eslint-visitor-keys@3.4.3: 840 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 841 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 842 | 843 | eslint-visitor-keys@4.2.0: 844 | resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} 845 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 846 | 847 | eslint@9.24.0: 848 | resolution: {integrity: sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==} 849 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 850 | hasBin: true 851 | peerDependencies: 852 | jiti: '*' 853 | peerDependenciesMeta: 854 | jiti: 855 | optional: true 856 | 857 | espree@10.3.0: 858 | resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} 859 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 860 | 861 | esquery@1.6.0: 862 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 863 | engines: {node: '>=0.10'} 864 | 865 | esrecurse@4.3.0: 866 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 867 | engines: {node: '>=4.0'} 868 | 869 | estraverse@5.3.0: 870 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 871 | engines: {node: '>=4.0'} 872 | 873 | esutils@2.0.3: 874 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 875 | engines: {node: '>=0.10.0'} 876 | 877 | fast-deep-equal@3.1.3: 878 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 879 | 880 | fast-json-stable-stringify@2.1.0: 881 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 882 | 883 | fast-levenshtein@2.0.6: 884 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 885 | 886 | file-entry-cache@8.0.0: 887 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 888 | engines: {node: '>=16.0.0'} 889 | 890 | find-up@5.0.0: 891 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 892 | engines: {node: '>=10'} 893 | 894 | flat-cache@4.0.1: 895 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 896 | engines: {node: '>=16'} 897 | 898 | flatted@3.3.3: 899 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 900 | 901 | for-each@0.3.3: 902 | resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} 903 | 904 | foreground-child@3.3.0: 905 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} 906 | engines: {node: '>=14'} 907 | 908 | fs-minipass@2.1.0: 909 | resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} 910 | engines: {node: '>= 8'} 911 | 912 | function-bind@1.1.2: 913 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 914 | 915 | function.prototype.name@1.1.6: 916 | resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} 917 | engines: {node: '>= 0.4'} 918 | 919 | functions-have-names@1.2.3: 920 | resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} 921 | 922 | get-intrinsic@1.2.4: 923 | resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} 924 | engines: {node: '>= 0.4'} 925 | 926 | get-symbol-description@1.0.2: 927 | resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} 928 | engines: {node: '>= 0.4'} 929 | 930 | glob-parent@6.0.2: 931 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 932 | engines: {node: '>=10.13.0'} 933 | 934 | glob@11.0.0: 935 | resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} 936 | engines: {node: 20 || >=22} 937 | hasBin: true 938 | 939 | globals@14.0.0: 940 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 941 | engines: {node: '>=18'} 942 | 943 | globalthis@1.0.4: 944 | resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} 945 | engines: {node: '>= 0.4'} 946 | 947 | gopd@1.0.1: 948 | resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} 949 | 950 | graceful-fs@4.2.11: 951 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 952 | 953 | gradient-parser@1.0.2: 954 | resolution: {integrity: sha512-gR6nY33xC9yJoH4wGLQtZQMXDi6RI3H37ERu7kQCVUzlXjNedpZM7xcA489Opwbq0BSGohtWGsWsntupmxelMg==} 955 | engines: {node: '>=0.10.0'} 956 | 957 | has-bigints@1.0.2: 958 | resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} 959 | 960 | has-flag@3.0.0: 961 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 962 | engines: {node: '>=4'} 963 | 964 | has-flag@4.0.0: 965 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 966 | engines: {node: '>=8'} 967 | 968 | has-property-descriptors@1.0.2: 969 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 970 | 971 | has-proto@1.0.3: 972 | resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} 973 | engines: {node: '>= 0.4'} 974 | 975 | has-symbols@1.0.3: 976 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 977 | engines: {node: '>= 0.4'} 978 | 979 | has-tostringtag@1.0.2: 980 | resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 981 | engines: {node: '>= 0.4'} 982 | 983 | hasown@2.0.2: 984 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 985 | engines: {node: '>= 0.4'} 986 | 987 | hosted-git-info@2.8.9: 988 | resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} 989 | 990 | iconv-lite@0.6.3: 991 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} 992 | engines: {node: '>=0.10.0'} 993 | 994 | ignore@5.3.2: 995 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 996 | engines: {node: '>= 4'} 997 | 998 | image-size@0.5.5: 999 | resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} 1000 | engines: {node: '>=0.10.0'} 1001 | hasBin: true 1002 | 1003 | import-fresh@3.3.1: 1004 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 1005 | engines: {node: '>=6'} 1006 | 1007 | imurmurhash@0.1.4: 1008 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 1009 | engines: {node: '>=0.8.19'} 1010 | 1011 | internal-slot@1.0.7: 1012 | resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} 1013 | engines: {node: '>= 0.4'} 1014 | 1015 | internmap@2.0.3: 1016 | resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} 1017 | engines: {node: '>=12'} 1018 | 1019 | is-array-buffer@3.0.4: 1020 | resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} 1021 | engines: {node: '>= 0.4'} 1022 | 1023 | is-arrayish@0.2.1: 1024 | resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} 1025 | 1026 | is-bigint@1.0.4: 1027 | resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} 1028 | 1029 | is-boolean-object@1.1.2: 1030 | resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} 1031 | engines: {node: '>= 0.4'} 1032 | 1033 | is-callable@1.2.7: 1034 | resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} 1035 | engines: {node: '>= 0.4'} 1036 | 1037 | is-core-module@2.15.1: 1038 | resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} 1039 | engines: {node: '>= 0.4'} 1040 | 1041 | is-data-view@1.0.1: 1042 | resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} 1043 | engines: {node: '>= 0.4'} 1044 | 1045 | is-date-object@1.0.5: 1046 | resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} 1047 | engines: {node: '>= 0.4'} 1048 | 1049 | is-extglob@2.1.1: 1050 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1051 | engines: {node: '>=0.10.0'} 1052 | 1053 | is-fullwidth-code-point@3.0.0: 1054 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 1055 | engines: {node: '>=8'} 1056 | 1057 | is-glob@4.0.3: 1058 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1059 | engines: {node: '>=0.10.0'} 1060 | 1061 | is-negative-zero@2.0.3: 1062 | resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} 1063 | engines: {node: '>= 0.4'} 1064 | 1065 | is-number-object@1.0.7: 1066 | resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} 1067 | engines: {node: '>= 0.4'} 1068 | 1069 | is-regex@1.1.4: 1070 | resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} 1071 | engines: {node: '>= 0.4'} 1072 | 1073 | is-shared-array-buffer@1.0.3: 1074 | resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} 1075 | engines: {node: '>= 0.4'} 1076 | 1077 | is-string@1.0.7: 1078 | resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} 1079 | engines: {node: '>= 0.4'} 1080 | 1081 | is-symbol@1.0.4: 1082 | resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} 1083 | engines: {node: '>= 0.4'} 1084 | 1085 | is-typed-array@1.1.13: 1086 | resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} 1087 | engines: {node: '>= 0.4'} 1088 | 1089 | is-weakref@1.0.2: 1090 | resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} 1091 | 1092 | is-what@3.14.1: 1093 | resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} 1094 | 1095 | isarray@2.0.5: 1096 | resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} 1097 | 1098 | isexe@2.0.0: 1099 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1100 | 1101 | jackspeak@4.0.2: 1102 | resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==} 1103 | engines: {node: 20 || >=22} 1104 | 1105 | js-yaml@4.1.0: 1106 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1107 | hasBin: true 1108 | 1109 | json-buffer@3.0.1: 1110 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 1111 | 1112 | json-parse-better-errors@1.0.2: 1113 | resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} 1114 | 1115 | json-schema-traverse@0.4.1: 1116 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 1117 | 1118 | json-stable-stringify-without-jsonify@1.0.1: 1119 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 1120 | 1121 | keyv@4.5.4: 1122 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 1123 | 1124 | less@4.3.0: 1125 | resolution: {integrity: sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==} 1126 | engines: {node: '>=14'} 1127 | hasBin: true 1128 | 1129 | levn@0.4.1: 1130 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1131 | engines: {node: '>= 0.8.0'} 1132 | 1133 | load-json-file@4.0.0: 1134 | resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} 1135 | engines: {node: '>=4'} 1136 | 1137 | locate-path@6.0.0: 1138 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1139 | engines: {node: '>=10'} 1140 | 1141 | lodash.merge@4.6.2: 1142 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 1143 | 1144 | lodash@4.17.21: 1145 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 1146 | 1147 | lru-cache@11.0.2: 1148 | resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} 1149 | engines: {node: 20 || >=22} 1150 | 1151 | make-dir@2.1.0: 1152 | resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} 1153 | engines: {node: '>=6'} 1154 | 1155 | memorystream@0.3.1: 1156 | resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} 1157 | engines: {node: '>= 0.10.0'} 1158 | 1159 | mime@1.6.0: 1160 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 1161 | engines: {node: '>=4'} 1162 | hasBin: true 1163 | 1164 | minimatch@10.0.1: 1165 | resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} 1166 | engines: {node: 20 || >=22} 1167 | 1168 | minimatch@3.1.2: 1169 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 1170 | 1171 | minipass@3.3.6: 1172 | resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} 1173 | engines: {node: '>=8'} 1174 | 1175 | minipass@5.0.0: 1176 | resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} 1177 | engines: {node: '>=8'} 1178 | 1179 | minipass@7.1.2: 1180 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 1181 | engines: {node: '>=16 || 14 >=14.17'} 1182 | 1183 | minizlib@2.1.2: 1184 | resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} 1185 | engines: {node: '>= 8'} 1186 | 1187 | mkdirp@1.0.4: 1188 | resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} 1189 | engines: {node: '>=10'} 1190 | hasBin: true 1191 | 1192 | ms@2.1.3: 1193 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1194 | 1195 | natural-compare@1.4.0: 1196 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 1197 | 1198 | needle@3.3.1: 1199 | resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} 1200 | engines: {node: '>= 4.4.x'} 1201 | hasBin: true 1202 | 1203 | nice-try@1.0.5: 1204 | resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} 1205 | 1206 | node-fetch@2.7.0: 1207 | resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} 1208 | engines: {node: 4.x || >=6.0.0} 1209 | peerDependencies: 1210 | encoding: ^0.1.0 1211 | peerDependenciesMeta: 1212 | encoding: 1213 | optional: true 1214 | 1215 | normalize-package-data@2.5.0: 1216 | resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} 1217 | 1218 | npm-run-all@4.1.5: 1219 | resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} 1220 | engines: {node: '>= 4'} 1221 | hasBin: true 1222 | 1223 | object-inspect@1.13.3: 1224 | resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} 1225 | engines: {node: '>= 0.4'} 1226 | 1227 | object-keys@1.1.1: 1228 | resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 1229 | engines: {node: '>= 0.4'} 1230 | 1231 | object.assign@4.1.5: 1232 | resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} 1233 | engines: {node: '>= 0.4'} 1234 | 1235 | optionator@0.9.4: 1236 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 1237 | engines: {node: '>= 0.8.0'} 1238 | 1239 | p-limit@3.1.0: 1240 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1241 | engines: {node: '>=10'} 1242 | 1243 | p-locate@5.0.0: 1244 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1245 | engines: {node: '>=10'} 1246 | 1247 | package-json-from-dist@1.0.1: 1248 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 1249 | 1250 | parent-module@1.0.1: 1251 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1252 | engines: {node: '>=6'} 1253 | 1254 | parse-json@4.0.0: 1255 | resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} 1256 | engines: {node: '>=4'} 1257 | 1258 | parse-node-version@1.0.1: 1259 | resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} 1260 | engines: {node: '>= 0.10'} 1261 | 1262 | path-exists@4.0.0: 1263 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1264 | engines: {node: '>=8'} 1265 | 1266 | path-key@2.0.1: 1267 | resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} 1268 | engines: {node: '>=4'} 1269 | 1270 | path-key@3.1.1: 1271 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1272 | engines: {node: '>=8'} 1273 | 1274 | path-parse@1.0.7: 1275 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1276 | 1277 | path-scurry@2.0.0: 1278 | resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} 1279 | engines: {node: 20 || >=22} 1280 | 1281 | path-type@3.0.0: 1282 | resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} 1283 | engines: {node: '>=4'} 1284 | 1285 | pidtree@0.3.1: 1286 | resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} 1287 | engines: {node: '>=0.10'} 1288 | hasBin: true 1289 | 1290 | pify@3.0.0: 1291 | resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} 1292 | engines: {node: '>=4'} 1293 | 1294 | pify@4.0.1: 1295 | resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} 1296 | engines: {node: '>=6'} 1297 | 1298 | possible-typed-array-names@1.0.0: 1299 | resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} 1300 | engines: {node: '>= 0.4'} 1301 | 1302 | prelude-ls@1.2.1: 1303 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 1304 | engines: {node: '>= 0.8.0'} 1305 | 1306 | prettier@3.5.3: 1307 | resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} 1308 | engines: {node: '>=14'} 1309 | hasBin: true 1310 | 1311 | prr@1.0.1: 1312 | resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} 1313 | 1314 | punycode@2.3.1: 1315 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1316 | engines: {node: '>=6'} 1317 | 1318 | read-pkg@3.0.0: 1319 | resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} 1320 | engines: {node: '>=4'} 1321 | 1322 | regexp.prototype.flags@1.5.3: 1323 | resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} 1324 | engines: {node: '>= 0.4'} 1325 | 1326 | regular-table@0.6.6: 1327 | resolution: {integrity: sha512-v2JieNm/fhUICKA4PZ6mcGqKpjKVvYF/3p2X0tmuFzxrLA+wvDySiDtEkN82AGrjIz57ThWjxjpkJbkbNMA95g==} 1328 | engines: {node: '>=16'} 1329 | 1330 | resolve-from@4.0.0: 1331 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1332 | engines: {node: '>=4'} 1333 | 1334 | resolve@1.22.8: 1335 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} 1336 | hasBin: true 1337 | 1338 | rimraf@6.0.1: 1339 | resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} 1340 | engines: {node: 20 || >=22} 1341 | hasBin: true 1342 | 1343 | robust-predicates@3.0.2: 1344 | resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} 1345 | 1346 | rw@1.3.3: 1347 | resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} 1348 | 1349 | safe-array-concat@1.1.2: 1350 | resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} 1351 | engines: {node: '>=0.4'} 1352 | 1353 | safe-regex-test@1.0.3: 1354 | resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} 1355 | engines: {node: '>= 0.4'} 1356 | 1357 | safer-buffer@2.1.2: 1358 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 1359 | 1360 | sax@1.4.1: 1361 | resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} 1362 | 1363 | semver@5.7.2: 1364 | resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} 1365 | hasBin: true 1366 | 1367 | set-function-length@1.2.2: 1368 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 1369 | engines: {node: '>= 0.4'} 1370 | 1371 | set-function-name@2.0.2: 1372 | resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} 1373 | engines: {node: '>= 0.4'} 1374 | 1375 | shebang-command@1.2.0: 1376 | resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} 1377 | engines: {node: '>=0.10.0'} 1378 | 1379 | shebang-command@2.0.0: 1380 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1381 | engines: {node: '>=8'} 1382 | 1383 | shebang-regex@1.0.0: 1384 | resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} 1385 | engines: {node: '>=0.10.0'} 1386 | 1387 | shebang-regex@3.0.0: 1388 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1389 | engines: {node: '>=8'} 1390 | 1391 | shell-quote@1.8.1: 1392 | resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} 1393 | 1394 | side-channel@1.0.6: 1395 | resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} 1396 | engines: {node: '>= 0.4'} 1397 | 1398 | signal-exit@4.1.0: 1399 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 1400 | engines: {node: '>=14'} 1401 | 1402 | source-map@0.6.1: 1403 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 1404 | engines: {node: '>=0.10.0'} 1405 | 1406 | spdx-correct@3.2.0: 1407 | resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} 1408 | 1409 | spdx-exceptions@2.5.0: 1410 | resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} 1411 | 1412 | spdx-expression-parse@3.0.1: 1413 | resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} 1414 | 1415 | spdx-license-ids@3.0.20: 1416 | resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} 1417 | 1418 | stoppable@1.1.0: 1419 | resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} 1420 | engines: {node: '>=4', npm: '>=6'} 1421 | 1422 | string-width@4.2.3: 1423 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1424 | engines: {node: '>=8'} 1425 | 1426 | string-width@5.1.2: 1427 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 1428 | engines: {node: '>=12'} 1429 | 1430 | string.prototype.padend@3.1.6: 1431 | resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} 1432 | engines: {node: '>= 0.4'} 1433 | 1434 | string.prototype.trim@1.2.9: 1435 | resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} 1436 | engines: {node: '>= 0.4'} 1437 | 1438 | string.prototype.trimend@1.0.8: 1439 | resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} 1440 | 1441 | string.prototype.trimstart@1.0.8: 1442 | resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} 1443 | engines: {node: '>= 0.4'} 1444 | 1445 | strip-ansi@6.0.1: 1446 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1447 | engines: {node: '>=8'} 1448 | 1449 | strip-ansi@7.1.0: 1450 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1451 | engines: {node: '>=12'} 1452 | 1453 | strip-bom@3.0.0: 1454 | resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 1455 | engines: {node: '>=4'} 1456 | 1457 | strip-json-comments@3.1.1: 1458 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1459 | engines: {node: '>=8'} 1460 | 1461 | supports-color@5.5.0: 1462 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 1463 | engines: {node: '>=4'} 1464 | 1465 | supports-color@7.2.0: 1466 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1467 | engines: {node: '>=8'} 1468 | 1469 | supports-preserve-symlinks-flag@1.0.0: 1470 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1471 | engines: {node: '>= 0.4'} 1472 | 1473 | tar@6.2.1: 1474 | resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} 1475 | engines: {node: '>=10'} 1476 | 1477 | tr46@0.0.3: 1478 | resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} 1479 | 1480 | tslib@2.8.1: 1481 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1482 | 1483 | type-check@0.4.0: 1484 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1485 | engines: {node: '>= 0.8.0'} 1486 | 1487 | typed-array-buffer@1.0.2: 1488 | resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} 1489 | engines: {node: '>= 0.4'} 1490 | 1491 | typed-array-byte-length@1.0.1: 1492 | resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} 1493 | engines: {node: '>= 0.4'} 1494 | 1495 | typed-array-byte-offset@1.0.2: 1496 | resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} 1497 | engines: {node: '>= 0.4'} 1498 | 1499 | typed-array-length@1.0.6: 1500 | resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} 1501 | engines: {node: '>= 0.4'} 1502 | 1503 | unbox-primitive@1.0.2: 1504 | resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} 1505 | 1506 | uri-js@4.4.1: 1507 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1508 | 1509 | validate-npm-package-license@3.0.4: 1510 | resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} 1511 | 1512 | webidl-conversions@3.0.1: 1513 | resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} 1514 | 1515 | whatwg-url@5.0.0: 1516 | resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} 1517 | 1518 | which-boxed-primitive@1.0.2: 1519 | resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} 1520 | 1521 | which-typed-array@1.1.15: 1522 | resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} 1523 | engines: {node: '>= 0.4'} 1524 | 1525 | which@1.3.1: 1526 | resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} 1527 | hasBin: true 1528 | 1529 | which@2.0.2: 1530 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1531 | engines: {node: '>= 8'} 1532 | hasBin: true 1533 | 1534 | word-wrap@1.2.5: 1535 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 1536 | engines: {node: '>=0.10.0'} 1537 | 1538 | wrap-ansi@7.0.0: 1539 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1540 | engines: {node: '>=10'} 1541 | 1542 | wrap-ansi@8.1.0: 1543 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1544 | engines: {node: '>=12'} 1545 | 1546 | ws@8.18.1: 1547 | resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} 1548 | engines: {node: '>=10.0.0'} 1549 | peerDependencies: 1550 | bufferutil: ^4.0.1 1551 | utf-8-validate: '>=5.0.2' 1552 | peerDependenciesMeta: 1553 | bufferutil: 1554 | optional: true 1555 | utf-8-validate: 1556 | optional: true 1557 | 1558 | yallist@4.0.0: 1559 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 1560 | 1561 | yocto-queue@0.1.0: 1562 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1563 | engines: {node: '>=10'} 1564 | 1565 | snapshots: 1566 | 1567 | '@d3fc/d3fc-annotation@3.0.16(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0)': 1568 | dependencies: 1569 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 1570 | '@d3fc/d3fc-rebind': 6.0.1 1571 | '@d3fc/d3fc-series': 6.1.3(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 1572 | '@d3fc/d3fc-shape': 6.0.1(d3-path@3.1.0) 1573 | d3-scale: 4.0.2 1574 | d3-selection: 3.0.0 1575 | transitivePeerDependencies: 1576 | - d3-array 1577 | - d3-path 1578 | - d3-scale-chromatic 1579 | - d3-shape 1580 | 1581 | '@d3fc/d3fc-axis@3.0.7(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0)': 1582 | dependencies: 1583 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 1584 | '@d3fc/d3fc-rebind': 6.0.1 1585 | d3-scale: 4.0.2 1586 | d3-selection: 3.0.0 1587 | d3-shape: 3.2.0 1588 | 1589 | '@d3fc/d3fc-brush@3.0.3(d3-brush@3.0.0)(d3-dispatch@3.0.1)(d3-scale@4.0.2)(d3-selection@3.0.0)': 1590 | dependencies: 1591 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 1592 | '@d3fc/d3fc-rebind': 6.0.1 1593 | d3-brush: 3.0.0 1594 | d3-dispatch: 3.0.1 1595 | d3-scale: 4.0.2 1596 | d3-selection: 3.0.0 1597 | 1598 | '@d3fc/d3fc-chart@5.1.9(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0)': 1599 | dependencies: 1600 | '@d3fc/d3fc-axis': 3.0.7(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 1601 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 1602 | '@d3fc/d3fc-element': 6.2.0 1603 | '@d3fc/d3fc-rebind': 6.0.1 1604 | '@d3fc/d3fc-series': 6.1.3(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 1605 | d3-scale: 4.0.2 1606 | d3-selection: 3.0.0 1607 | transitivePeerDependencies: 1608 | - d3-array 1609 | - d3-path 1610 | - d3-scale-chromatic 1611 | - d3-shape 1612 | 1613 | '@d3fc/d3fc-data-join@6.0.3(d3-selection@3.0.0)': 1614 | dependencies: 1615 | d3-selection: 3.0.0 1616 | 1617 | '@d3fc/d3fc-discontinuous-scale@4.1.1(d3-scale@4.0.2)(d3-time@3.1.0)': 1618 | dependencies: 1619 | '@d3fc/d3fc-rebind': 6.0.1 1620 | d3-scale: 4.0.2 1621 | d3-time: 3.1.0 1622 | 1623 | '@d3fc/d3fc-element@6.2.0': {} 1624 | 1625 | '@d3fc/d3fc-extent@4.0.2(d3-array@3.2.4)': 1626 | dependencies: 1627 | d3-array: 3.2.4 1628 | 1629 | '@d3fc/d3fc-financial-feed@7.1.0(d3-fetch@3.0.1)': 1630 | dependencies: 1631 | d3-fetch: 3.0.1 1632 | 1633 | '@d3fc/d3fc-group@3.0.1': {} 1634 | 1635 | '@d3fc/d3fc-label-layout@7.0.4(d3-array@3.2.4)(d3-scale@4.0.2)(d3-selection@3.0.0)': 1636 | dependencies: 1637 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 1638 | '@d3fc/d3fc-rebind': 6.0.1 1639 | d3-array: 3.2.4 1640 | d3-scale: 4.0.2 1641 | d3-selection: 3.0.0 1642 | 1643 | '@d3fc/d3fc-pointer@3.0.3(d3-dispatch@3.0.1)(d3-selection@3.0.0)': 1644 | dependencies: 1645 | '@d3fc/d3fc-rebind': 6.0.1 1646 | d3-dispatch: 3.0.1 1647 | d3-selection: 3.0.0 1648 | 1649 | '@d3fc/d3fc-random-data@4.0.2(d3-random@3.0.1)(d3-time@3.1.0)': 1650 | dependencies: 1651 | '@d3fc/d3fc-rebind': 6.0.1 1652 | d3-random: 3.0.1 1653 | d3-time: 3.1.0 1654 | 1655 | '@d3fc/d3fc-rebind@6.0.1': {} 1656 | 1657 | '@d3fc/d3fc-sample@5.0.2(d3-array@3.2.4)': 1658 | dependencies: 1659 | '@d3fc/d3fc-rebind': 6.0.1 1660 | d3-array: 3.2.4 1661 | 1662 | '@d3fc/d3fc-series@6.1.3(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0)': 1663 | dependencies: 1664 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 1665 | '@d3fc/d3fc-rebind': 6.0.1 1666 | '@d3fc/d3fc-shape': 6.0.1(d3-path@3.1.0) 1667 | '@d3fc/d3fc-webgl': 3.2.1(d3-scale@4.0.2)(d3-shape@3.2.0) 1668 | d3-array: 3.2.4 1669 | d3-scale: 4.0.2 1670 | d3-scale-chromatic: 3.1.0 1671 | d3-selection: 3.0.0 1672 | d3-shape: 3.2.0 1673 | transitivePeerDependencies: 1674 | - d3-path 1675 | 1676 | '@d3fc/d3fc-shape@6.0.1(d3-path@3.1.0)': 1677 | dependencies: 1678 | d3-path: 3.1.0 1679 | 1680 | '@d3fc/d3fc-technical-indicator@8.1.1(d3-array@3.2.4)': 1681 | dependencies: 1682 | '@d3fc/d3fc-rebind': 6.0.1 1683 | d3-array: 3.2.4 1684 | 1685 | '@d3fc/d3fc-webgl@3.2.1(d3-scale@4.0.2)(d3-shape@3.2.0)': 1686 | dependencies: 1687 | '@d3fc/d3fc-rebind': 6.0.1 1688 | d3-scale: 4.0.2 1689 | d3-shape: 3.2.0 1690 | 1691 | '@d3fc/d3fc-zoom@1.2.0(d3-dispatch@3.0.1)(d3-selection@3.0.0)(d3-zoom@3.0.0)': 1692 | dependencies: 1693 | '@d3fc/d3fc-rebind': 6.0.1 1694 | d3-dispatch: 3.0.1 1695 | d3-selection: 3.0.0 1696 | d3-zoom: 3.0.0 1697 | 1698 | '@esbuild/aix-ppc64@0.25.2': 1699 | optional: true 1700 | 1701 | '@esbuild/android-arm64@0.25.2': 1702 | optional: true 1703 | 1704 | '@esbuild/android-arm@0.25.2': 1705 | optional: true 1706 | 1707 | '@esbuild/android-x64@0.25.2': 1708 | optional: true 1709 | 1710 | '@esbuild/darwin-arm64@0.25.2': 1711 | optional: true 1712 | 1713 | '@esbuild/darwin-x64@0.25.2': 1714 | optional: true 1715 | 1716 | '@esbuild/freebsd-arm64@0.25.2': 1717 | optional: true 1718 | 1719 | '@esbuild/freebsd-x64@0.25.2': 1720 | optional: true 1721 | 1722 | '@esbuild/linux-arm64@0.25.2': 1723 | optional: true 1724 | 1725 | '@esbuild/linux-arm@0.25.2': 1726 | optional: true 1727 | 1728 | '@esbuild/linux-ia32@0.25.2': 1729 | optional: true 1730 | 1731 | '@esbuild/linux-loong64@0.25.2': 1732 | optional: true 1733 | 1734 | '@esbuild/linux-mips64el@0.25.2': 1735 | optional: true 1736 | 1737 | '@esbuild/linux-ppc64@0.25.2': 1738 | optional: true 1739 | 1740 | '@esbuild/linux-riscv64@0.25.2': 1741 | optional: true 1742 | 1743 | '@esbuild/linux-s390x@0.25.2': 1744 | optional: true 1745 | 1746 | '@esbuild/linux-x64@0.25.2': 1747 | optional: true 1748 | 1749 | '@esbuild/netbsd-arm64@0.25.2': 1750 | optional: true 1751 | 1752 | '@esbuild/netbsd-x64@0.25.2': 1753 | optional: true 1754 | 1755 | '@esbuild/openbsd-arm64@0.25.2': 1756 | optional: true 1757 | 1758 | '@esbuild/openbsd-x64@0.25.2': 1759 | optional: true 1760 | 1761 | '@esbuild/sunos-x64@0.25.2': 1762 | optional: true 1763 | 1764 | '@esbuild/win32-arm64@0.25.2': 1765 | optional: true 1766 | 1767 | '@esbuild/win32-ia32@0.25.2': 1768 | optional: true 1769 | 1770 | '@esbuild/win32-x64@0.25.2': 1771 | optional: true 1772 | 1773 | '@eslint-community/eslint-utils@4.5.1(eslint@9.24.0)': 1774 | dependencies: 1775 | eslint: 9.24.0 1776 | eslint-visitor-keys: 3.4.3 1777 | 1778 | '@eslint-community/regexpp@4.12.1': {} 1779 | 1780 | '@eslint/config-array@0.20.0': 1781 | dependencies: 1782 | '@eslint/object-schema': 2.1.6 1783 | debug: 4.4.0 1784 | minimatch: 3.1.2 1785 | transitivePeerDependencies: 1786 | - supports-color 1787 | 1788 | '@eslint/config-helpers@0.2.1': {} 1789 | 1790 | '@eslint/core@0.12.0': 1791 | dependencies: 1792 | '@types/json-schema': 7.0.15 1793 | 1794 | '@eslint/core@0.13.0': 1795 | dependencies: 1796 | '@types/json-schema': 7.0.15 1797 | 1798 | '@eslint/eslintrc@3.3.1': 1799 | dependencies: 1800 | ajv: 6.12.6 1801 | debug: 4.4.0 1802 | espree: 10.3.0 1803 | globals: 14.0.0 1804 | ignore: 5.3.2 1805 | import-fresh: 3.3.1 1806 | js-yaml: 4.1.0 1807 | minimatch: 3.1.2 1808 | strip-json-comments: 3.1.1 1809 | transitivePeerDependencies: 1810 | - supports-color 1811 | 1812 | '@eslint/js@9.24.0': {} 1813 | 1814 | '@eslint/object-schema@2.1.6': {} 1815 | 1816 | '@eslint/plugin-kit@0.2.8': 1817 | dependencies: 1818 | '@eslint/core': 0.13.0 1819 | levn: 0.4.1 1820 | 1821 | '@finos/perspective-esbuild-plugin@3.2.1': 1822 | dependencies: 1823 | node-fetch: 2.7.0 1824 | tar: 6.2.1 1825 | transitivePeerDependencies: 1826 | - encoding 1827 | 1828 | '@finos/perspective-viewer-d3fc@3.4.3(d3-brush@3.0.0)(d3-dispatch@3.0.1)(d3-fetch@3.0.1)(d3-path@3.1.0)(d3-random@3.0.1)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-shape@3.2.0)(d3-time@3.1.0)(d3-zoom@3.0.0)': 1829 | dependencies: 1830 | '@finos/perspective': 3.4.3 1831 | '@finos/perspective-viewer': 3.4.3 1832 | chroma-js: 1.4.1 1833 | d3: 7.9.0 1834 | d3-array: 3.2.4 1835 | d3-selection: 3.0.0 1836 | d3-svg-legend: 2.25.6 1837 | d3fc: 15.2.13(d3-array@3.2.4)(d3-brush@3.0.0)(d3-dispatch@3.0.1)(d3-fetch@3.0.1)(d3-path@3.1.0)(d3-random@3.0.1)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0)(d3-time@3.1.0)(d3-zoom@3.0.0) 1838 | gradient-parser: 1.0.2 1839 | transitivePeerDependencies: 1840 | - bufferutil 1841 | - d3-brush 1842 | - d3-dispatch 1843 | - d3-fetch 1844 | - d3-path 1845 | - d3-random 1846 | - d3-scale 1847 | - d3-scale-chromatic 1848 | - d3-shape 1849 | - d3-time 1850 | - d3-zoom 1851 | - utf-8-validate 1852 | 1853 | '@finos/perspective-viewer-datagrid@3.4.3': 1854 | dependencies: 1855 | '@finos/perspective': 3.4.3 1856 | '@finos/perspective-viewer': 3.4.3 1857 | chroma-js: 1.4.1 1858 | regular-table: 0.6.6 1859 | transitivePeerDependencies: 1860 | - bufferutil 1861 | - utf-8-validate 1862 | 1863 | '@finos/perspective-viewer@3.4.3': 1864 | dependencies: 1865 | '@finos/perspective': 3.4.3 1866 | transitivePeerDependencies: 1867 | - bufferutil 1868 | - utf-8-validate 1869 | 1870 | '@finos/perspective-workspace@3.4.3': 1871 | dependencies: 1872 | '@finos/perspective': 3.4.3 1873 | '@finos/perspective-viewer': 3.4.3 1874 | '@lumino/algorithm': 2.0.3 1875 | '@lumino/commands': 2.3.2 1876 | '@lumino/coreutils': 2.2.1 1877 | '@lumino/domutils': 2.0.3 1878 | '@lumino/messaging': 2.0.3 1879 | '@lumino/signaling': 2.1.4 1880 | '@lumino/virtualdom': 2.0.3 1881 | '@lumino/widgets': 2.7.0 1882 | lodash: 4.17.21 1883 | transitivePeerDependencies: 1884 | - bufferutil 1885 | - utf-8-validate 1886 | 1887 | '@finos/perspective@3.4.3': 1888 | dependencies: 1889 | stoppable: 1.1.0 1890 | ws: 8.18.1 1891 | transitivePeerDependencies: 1892 | - bufferutil 1893 | - utf-8-validate 1894 | 1895 | '@humanfs/core@0.19.1': {} 1896 | 1897 | '@humanfs/node@0.16.6': 1898 | dependencies: 1899 | '@humanfs/core': 0.19.1 1900 | '@humanwhocodes/retry': 0.3.1 1901 | 1902 | '@humanwhocodes/module-importer@1.0.1': {} 1903 | 1904 | '@humanwhocodes/retry@0.3.1': {} 1905 | 1906 | '@humanwhocodes/retry@0.4.2': {} 1907 | 1908 | '@isaacs/cliui@8.0.2': 1909 | dependencies: 1910 | string-width: 5.1.2 1911 | string-width-cjs: string-width@4.2.3 1912 | strip-ansi: 7.1.0 1913 | strip-ansi-cjs: strip-ansi@6.0.1 1914 | wrap-ansi: 8.1.0 1915 | wrap-ansi-cjs: wrap-ansi@7.0.0 1916 | 1917 | '@lumino/algorithm@2.0.3': {} 1918 | 1919 | '@lumino/collections@2.0.3': 1920 | dependencies: 1921 | '@lumino/algorithm': 2.0.3 1922 | 1923 | '@lumino/commands@2.3.2': 1924 | dependencies: 1925 | '@lumino/algorithm': 2.0.3 1926 | '@lumino/coreutils': 2.2.1 1927 | '@lumino/disposable': 2.1.4 1928 | '@lumino/domutils': 2.0.3 1929 | '@lumino/keyboard': 2.0.3 1930 | '@lumino/signaling': 2.1.4 1931 | '@lumino/virtualdom': 2.0.3 1932 | 1933 | '@lumino/coreutils@2.2.1': 1934 | dependencies: 1935 | '@lumino/algorithm': 2.0.3 1936 | 1937 | '@lumino/disposable@2.1.4': 1938 | dependencies: 1939 | '@lumino/signaling': 2.1.4 1940 | 1941 | '@lumino/domutils@2.0.3': {} 1942 | 1943 | '@lumino/dragdrop@2.1.6': 1944 | dependencies: 1945 | '@lumino/coreutils': 2.2.1 1946 | '@lumino/disposable': 2.1.4 1947 | 1948 | '@lumino/keyboard@2.0.3': {} 1949 | 1950 | '@lumino/messaging@2.0.3': 1951 | dependencies: 1952 | '@lumino/algorithm': 2.0.3 1953 | '@lumino/collections': 2.0.3 1954 | 1955 | '@lumino/properties@2.0.3': {} 1956 | 1957 | '@lumino/signaling@2.1.4': 1958 | dependencies: 1959 | '@lumino/algorithm': 2.0.3 1960 | '@lumino/coreutils': 2.2.1 1961 | 1962 | '@lumino/virtualdom@2.0.3': 1963 | dependencies: 1964 | '@lumino/algorithm': 2.0.3 1965 | 1966 | '@lumino/widgets@2.7.0': 1967 | dependencies: 1968 | '@lumino/algorithm': 2.0.3 1969 | '@lumino/commands': 2.3.2 1970 | '@lumino/coreutils': 2.2.1 1971 | '@lumino/disposable': 2.1.4 1972 | '@lumino/domutils': 2.0.3 1973 | '@lumino/dragdrop': 2.1.6 1974 | '@lumino/keyboard': 2.0.3 1975 | '@lumino/messaging': 2.0.3 1976 | '@lumino/properties': 2.0.3 1977 | '@lumino/signaling': 2.1.4 1978 | '@lumino/virtualdom': 2.0.3 1979 | 1980 | '@prospective.co/procss@0.1.16': {} 1981 | 1982 | '@types/d3-selection@1.0.10': {} 1983 | 1984 | '@types/estree@1.0.7': {} 1985 | 1986 | '@types/json-schema@7.0.15': {} 1987 | 1988 | '@types/less@3.0.8': {} 1989 | 1990 | acorn-jsx@5.3.2(acorn@8.14.1): 1991 | dependencies: 1992 | acorn: 8.14.1 1993 | 1994 | acorn@8.14.1: {} 1995 | 1996 | ajv@6.12.6: 1997 | dependencies: 1998 | fast-deep-equal: 3.1.3 1999 | fast-json-stable-stringify: 2.1.0 2000 | json-schema-traverse: 0.4.1 2001 | uri-js: 4.4.1 2002 | 2003 | ansi-regex@5.0.1: {} 2004 | 2005 | ansi-regex@6.1.0: {} 2006 | 2007 | ansi-styles@3.2.1: 2008 | dependencies: 2009 | color-convert: 1.9.3 2010 | 2011 | ansi-styles@4.3.0: 2012 | dependencies: 2013 | color-convert: 2.0.1 2014 | 2015 | ansi-styles@6.2.1: {} 2016 | 2017 | argparse@2.0.1: {} 2018 | 2019 | array-buffer-byte-length@1.0.1: 2020 | dependencies: 2021 | call-bind: 1.0.7 2022 | is-array-buffer: 3.0.4 2023 | 2024 | arraybuffer.prototype.slice@1.0.3: 2025 | dependencies: 2026 | array-buffer-byte-length: 1.0.1 2027 | call-bind: 1.0.7 2028 | define-properties: 1.2.1 2029 | es-abstract: 1.23.5 2030 | es-errors: 1.3.0 2031 | get-intrinsic: 1.2.4 2032 | is-array-buffer: 3.0.4 2033 | is-shared-array-buffer: 1.0.3 2034 | 2035 | available-typed-arrays@1.0.7: 2036 | dependencies: 2037 | possible-typed-array-names: 1.0.0 2038 | 2039 | balanced-match@1.0.2: {} 2040 | 2041 | brace-expansion@1.1.11: 2042 | dependencies: 2043 | balanced-match: 1.0.2 2044 | concat-map: 0.0.1 2045 | 2046 | brace-expansion@2.0.1: 2047 | dependencies: 2048 | balanced-match: 1.0.2 2049 | 2050 | call-bind@1.0.7: 2051 | dependencies: 2052 | es-define-property: 1.0.0 2053 | es-errors: 1.3.0 2054 | function-bind: 1.1.2 2055 | get-intrinsic: 1.2.4 2056 | set-function-length: 1.2.2 2057 | 2058 | callsites@3.1.0: {} 2059 | 2060 | chalk@2.4.2: 2061 | dependencies: 2062 | ansi-styles: 3.2.1 2063 | escape-string-regexp: 1.0.5 2064 | supports-color: 5.5.0 2065 | 2066 | chalk@4.1.2: 2067 | dependencies: 2068 | ansi-styles: 4.3.0 2069 | supports-color: 7.2.0 2070 | 2071 | chownr@2.0.0: {} 2072 | 2073 | chroma-js@1.4.1: {} 2074 | 2075 | color-convert@1.9.3: 2076 | dependencies: 2077 | color-name: 1.1.3 2078 | 2079 | color-convert@2.0.1: 2080 | dependencies: 2081 | color-name: 1.1.4 2082 | 2083 | color-name@1.1.3: {} 2084 | 2085 | color-name@1.1.4: {} 2086 | 2087 | commander@7.2.0: {} 2088 | 2089 | concat-map@0.0.1: {} 2090 | 2091 | copy-anything@2.0.6: 2092 | dependencies: 2093 | is-what: 3.14.1 2094 | 2095 | cross-spawn@6.0.6: 2096 | dependencies: 2097 | nice-try: 1.0.5 2098 | path-key: 2.0.1 2099 | semver: 5.7.2 2100 | shebang-command: 1.2.0 2101 | which: 1.3.1 2102 | 2103 | cross-spawn@7.0.5: 2104 | dependencies: 2105 | path-key: 3.1.1 2106 | shebang-command: 2.0.0 2107 | which: 2.0.2 2108 | 2109 | cross-spawn@7.0.6: 2110 | dependencies: 2111 | path-key: 3.1.1 2112 | shebang-command: 2.0.0 2113 | which: 2.0.2 2114 | 2115 | d3-array@1.0.1: {} 2116 | 2117 | d3-array@3.2.4: 2118 | dependencies: 2119 | internmap: 2.0.3 2120 | 2121 | d3-axis@3.0.0: {} 2122 | 2123 | d3-brush@3.0.0: 2124 | dependencies: 2125 | d3-dispatch: 3.0.1 2126 | d3-drag: 3.0.0 2127 | d3-interpolate: 3.0.1 2128 | d3-selection: 3.0.0 2129 | d3-transition: 3.0.1(d3-selection@3.0.0) 2130 | 2131 | d3-chord@3.0.1: 2132 | dependencies: 2133 | d3-path: 3.1.0 2134 | 2135 | d3-collection@1.0.7: {} 2136 | 2137 | d3-color@1.4.1: {} 2138 | 2139 | d3-color@3.1.0: {} 2140 | 2141 | d3-contour@4.0.2: 2142 | dependencies: 2143 | d3-array: 3.2.4 2144 | 2145 | d3-delaunay@6.0.4: 2146 | dependencies: 2147 | delaunator: 5.0.1 2148 | 2149 | d3-dispatch@1.0.1: {} 2150 | 2151 | d3-dispatch@3.0.1: {} 2152 | 2153 | d3-drag@3.0.0: 2154 | dependencies: 2155 | d3-dispatch: 3.0.1 2156 | d3-selection: 3.0.0 2157 | 2158 | d3-dsv@3.0.1: 2159 | dependencies: 2160 | commander: 7.2.0 2161 | iconv-lite: 0.6.3 2162 | rw: 1.3.3 2163 | 2164 | d3-ease@1.0.7: {} 2165 | 2166 | d3-ease@3.0.1: {} 2167 | 2168 | d3-fetch@3.0.1: 2169 | dependencies: 2170 | d3-dsv: 3.0.1 2171 | 2172 | d3-force@3.0.0: 2173 | dependencies: 2174 | d3-dispatch: 3.0.1 2175 | d3-quadtree: 3.0.1 2176 | d3-timer: 3.0.1 2177 | 2178 | d3-format@1.0.2: {} 2179 | 2180 | d3-format@3.1.0: {} 2181 | 2182 | d3-geo@3.1.1: 2183 | dependencies: 2184 | d3-array: 3.2.4 2185 | 2186 | d3-hierarchy@3.1.2: {} 2187 | 2188 | d3-interpolate@1.4.0: 2189 | dependencies: 2190 | d3-color: 1.4.1 2191 | 2192 | d3-interpolate@3.0.1: 2193 | dependencies: 2194 | d3-color: 3.1.0 2195 | 2196 | d3-path@3.1.0: {} 2197 | 2198 | d3-polygon@3.0.1: {} 2199 | 2200 | d3-quadtree@3.0.1: {} 2201 | 2202 | d3-random@3.0.1: {} 2203 | 2204 | d3-scale-chromatic@3.1.0: 2205 | dependencies: 2206 | d3-color: 3.1.0 2207 | d3-interpolate: 3.0.1 2208 | 2209 | d3-scale@1.0.3: 2210 | dependencies: 2211 | d3-array: 1.0.1 2212 | d3-collection: 1.0.7 2213 | d3-color: 1.4.1 2214 | d3-format: 1.0.2 2215 | d3-interpolate: 1.4.0 2216 | d3-time: 1.1.0 2217 | d3-time-format: 2.3.0 2218 | 2219 | d3-scale@4.0.2: 2220 | dependencies: 2221 | d3-array: 3.2.4 2222 | d3-format: 3.1.0 2223 | d3-interpolate: 3.0.1 2224 | d3-time: 3.1.0 2225 | d3-time-format: 4.1.0 2226 | 2227 | d3-selection@1.0.2: {} 2228 | 2229 | d3-selection@3.0.0: {} 2230 | 2231 | d3-shape@3.2.0: 2232 | dependencies: 2233 | d3-path: 3.1.0 2234 | 2235 | d3-svg-legend@2.25.6: 2236 | dependencies: 2237 | '@types/d3-selection': 1.0.10 2238 | d3-array: 1.0.1 2239 | d3-dispatch: 1.0.1 2240 | d3-format: 1.0.2 2241 | d3-scale: 1.0.3 2242 | d3-selection: 1.0.2 2243 | d3-transition: 1.0.3 2244 | 2245 | d3-time-format@2.3.0: 2246 | dependencies: 2247 | d3-time: 1.1.0 2248 | 2249 | d3-time-format@4.1.0: 2250 | dependencies: 2251 | d3-time: 3.1.0 2252 | 2253 | d3-time@1.1.0: {} 2254 | 2255 | d3-time@3.1.0: 2256 | dependencies: 2257 | d3-array: 3.2.4 2258 | 2259 | d3-timer@1.0.10: {} 2260 | 2261 | d3-timer@3.0.1: {} 2262 | 2263 | d3-transition@1.0.3: 2264 | dependencies: 2265 | d3-color: 1.4.1 2266 | d3-dispatch: 1.0.1 2267 | d3-ease: 1.0.7 2268 | d3-interpolate: 1.4.0 2269 | d3-selection: 1.0.2 2270 | d3-timer: 1.0.10 2271 | 2272 | d3-transition@3.0.1(d3-selection@3.0.0): 2273 | dependencies: 2274 | d3-color: 3.1.0 2275 | d3-dispatch: 3.0.1 2276 | d3-ease: 3.0.1 2277 | d3-interpolate: 3.0.1 2278 | d3-selection: 3.0.0 2279 | d3-timer: 3.0.1 2280 | 2281 | d3-zoom@3.0.0: 2282 | dependencies: 2283 | d3-dispatch: 3.0.1 2284 | d3-drag: 3.0.0 2285 | d3-interpolate: 3.0.1 2286 | d3-selection: 3.0.0 2287 | d3-transition: 3.0.1(d3-selection@3.0.0) 2288 | 2289 | d3@7.9.0: 2290 | dependencies: 2291 | d3-array: 3.2.4 2292 | d3-axis: 3.0.0 2293 | d3-brush: 3.0.0 2294 | d3-chord: 3.0.1 2295 | d3-color: 3.1.0 2296 | d3-contour: 4.0.2 2297 | d3-delaunay: 6.0.4 2298 | d3-dispatch: 3.0.1 2299 | d3-drag: 3.0.0 2300 | d3-dsv: 3.0.1 2301 | d3-ease: 3.0.1 2302 | d3-fetch: 3.0.1 2303 | d3-force: 3.0.0 2304 | d3-format: 3.1.0 2305 | d3-geo: 3.1.1 2306 | d3-hierarchy: 3.1.2 2307 | d3-interpolate: 3.0.1 2308 | d3-path: 3.1.0 2309 | d3-polygon: 3.0.1 2310 | d3-quadtree: 3.0.1 2311 | d3-random: 3.0.1 2312 | d3-scale: 4.0.2 2313 | d3-scale-chromatic: 3.1.0 2314 | d3-selection: 3.0.0 2315 | d3-shape: 3.2.0 2316 | d3-time: 3.1.0 2317 | d3-time-format: 4.1.0 2318 | d3-timer: 3.0.1 2319 | d3-transition: 3.0.1(d3-selection@3.0.0) 2320 | d3-zoom: 3.0.0 2321 | 2322 | d3fc@15.2.13(d3-array@3.2.4)(d3-brush@3.0.0)(d3-dispatch@3.0.1)(d3-fetch@3.0.1)(d3-path@3.1.0)(d3-random@3.0.1)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0)(d3-time@3.1.0)(d3-zoom@3.0.0): 2323 | dependencies: 2324 | '@d3fc/d3fc-annotation': 3.0.16(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 2325 | '@d3fc/d3fc-axis': 3.0.7(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 2326 | '@d3fc/d3fc-brush': 3.0.3(d3-brush@3.0.0)(d3-dispatch@3.0.1)(d3-scale@4.0.2)(d3-selection@3.0.0) 2327 | '@d3fc/d3fc-chart': 5.1.9(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 2328 | '@d3fc/d3fc-data-join': 6.0.3(d3-selection@3.0.0) 2329 | '@d3fc/d3fc-discontinuous-scale': 4.1.1(d3-scale@4.0.2)(d3-time@3.1.0) 2330 | '@d3fc/d3fc-element': 6.2.0 2331 | '@d3fc/d3fc-extent': 4.0.2(d3-array@3.2.4) 2332 | '@d3fc/d3fc-financial-feed': 7.1.0(d3-fetch@3.0.1) 2333 | '@d3fc/d3fc-group': 3.0.1 2334 | '@d3fc/d3fc-label-layout': 7.0.4(d3-array@3.2.4)(d3-scale@4.0.2)(d3-selection@3.0.0) 2335 | '@d3fc/d3fc-pointer': 3.0.3(d3-dispatch@3.0.1)(d3-selection@3.0.0) 2336 | '@d3fc/d3fc-random-data': 4.0.2(d3-random@3.0.1)(d3-time@3.1.0) 2337 | '@d3fc/d3fc-rebind': 6.0.1 2338 | '@d3fc/d3fc-sample': 5.0.2(d3-array@3.2.4) 2339 | '@d3fc/d3fc-series': 6.1.3(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) 2340 | '@d3fc/d3fc-shape': 6.0.1(d3-path@3.1.0) 2341 | '@d3fc/d3fc-technical-indicator': 8.1.1(d3-array@3.2.4) 2342 | '@d3fc/d3fc-webgl': 3.2.1(d3-scale@4.0.2)(d3-shape@3.2.0) 2343 | '@d3fc/d3fc-zoom': 1.2.0(d3-dispatch@3.0.1)(d3-selection@3.0.0)(d3-zoom@3.0.0) 2344 | transitivePeerDependencies: 2345 | - d3-array 2346 | - d3-brush 2347 | - d3-dispatch 2348 | - d3-fetch 2349 | - d3-path 2350 | - d3-random 2351 | - d3-scale 2352 | - d3-scale-chromatic 2353 | - d3-selection 2354 | - d3-shape 2355 | - d3-time 2356 | - d3-zoom 2357 | 2358 | data-view-buffer@1.0.1: 2359 | dependencies: 2360 | call-bind: 1.0.7 2361 | es-errors: 1.3.0 2362 | is-data-view: 1.0.1 2363 | 2364 | data-view-byte-length@1.0.1: 2365 | dependencies: 2366 | call-bind: 1.0.7 2367 | es-errors: 1.3.0 2368 | is-data-view: 1.0.1 2369 | 2370 | data-view-byte-offset@1.0.0: 2371 | dependencies: 2372 | call-bind: 1.0.7 2373 | es-errors: 1.3.0 2374 | is-data-view: 1.0.1 2375 | 2376 | debug@4.4.0: 2377 | dependencies: 2378 | ms: 2.1.3 2379 | 2380 | deep-is@0.1.4: {} 2381 | 2382 | define-data-property@1.1.4: 2383 | dependencies: 2384 | es-define-property: 1.0.0 2385 | es-errors: 1.3.0 2386 | gopd: 1.0.1 2387 | 2388 | define-properties@1.2.1: 2389 | dependencies: 2390 | define-data-property: 1.1.4 2391 | has-property-descriptors: 1.0.2 2392 | object-keys: 1.1.1 2393 | 2394 | delaunator@5.0.1: 2395 | dependencies: 2396 | robust-predicates: 3.0.2 2397 | 2398 | eastasianwidth@0.2.0: {} 2399 | 2400 | emoji-regex@8.0.0: {} 2401 | 2402 | emoji-regex@9.2.2: {} 2403 | 2404 | errno@0.1.8: 2405 | dependencies: 2406 | prr: 1.0.1 2407 | optional: true 2408 | 2409 | error-ex@1.3.2: 2410 | dependencies: 2411 | is-arrayish: 0.2.1 2412 | 2413 | es-abstract@1.23.5: 2414 | dependencies: 2415 | array-buffer-byte-length: 1.0.1 2416 | arraybuffer.prototype.slice: 1.0.3 2417 | available-typed-arrays: 1.0.7 2418 | call-bind: 1.0.7 2419 | data-view-buffer: 1.0.1 2420 | data-view-byte-length: 1.0.1 2421 | data-view-byte-offset: 1.0.0 2422 | es-define-property: 1.0.0 2423 | es-errors: 1.3.0 2424 | es-object-atoms: 1.0.0 2425 | es-set-tostringtag: 2.0.3 2426 | es-to-primitive: 1.2.1 2427 | function.prototype.name: 1.1.6 2428 | get-intrinsic: 1.2.4 2429 | get-symbol-description: 1.0.2 2430 | globalthis: 1.0.4 2431 | gopd: 1.0.1 2432 | has-property-descriptors: 1.0.2 2433 | has-proto: 1.0.3 2434 | has-symbols: 1.0.3 2435 | hasown: 2.0.2 2436 | internal-slot: 1.0.7 2437 | is-array-buffer: 3.0.4 2438 | is-callable: 1.2.7 2439 | is-data-view: 1.0.1 2440 | is-negative-zero: 2.0.3 2441 | is-regex: 1.1.4 2442 | is-shared-array-buffer: 1.0.3 2443 | is-string: 1.0.7 2444 | is-typed-array: 1.1.13 2445 | is-weakref: 1.0.2 2446 | object-inspect: 1.13.3 2447 | object-keys: 1.1.1 2448 | object.assign: 4.1.5 2449 | regexp.prototype.flags: 1.5.3 2450 | safe-array-concat: 1.1.2 2451 | safe-regex-test: 1.0.3 2452 | string.prototype.trim: 1.2.9 2453 | string.prototype.trimend: 1.0.8 2454 | string.prototype.trimstart: 1.0.8 2455 | typed-array-buffer: 1.0.2 2456 | typed-array-byte-length: 1.0.1 2457 | typed-array-byte-offset: 1.0.2 2458 | typed-array-length: 1.0.6 2459 | unbox-primitive: 1.0.2 2460 | which-typed-array: 1.1.15 2461 | 2462 | es-define-property@1.0.0: 2463 | dependencies: 2464 | get-intrinsic: 1.2.4 2465 | 2466 | es-errors@1.3.0: {} 2467 | 2468 | es-object-atoms@1.0.0: 2469 | dependencies: 2470 | es-errors: 1.3.0 2471 | 2472 | es-set-tostringtag@2.0.3: 2473 | dependencies: 2474 | get-intrinsic: 1.2.4 2475 | has-tostringtag: 1.0.2 2476 | hasown: 2.0.2 2477 | 2478 | es-to-primitive@1.2.1: 2479 | dependencies: 2480 | is-callable: 1.2.7 2481 | is-date-object: 1.0.5 2482 | is-symbol: 1.0.4 2483 | 2484 | esbuild-plugin-less@1.3.20(esbuild@0.25.2): 2485 | dependencies: 2486 | '@types/less': 3.0.8 2487 | esbuild: 0.25.2 2488 | less: 4.3.0 2489 | 2490 | esbuild@0.25.2: 2491 | optionalDependencies: 2492 | '@esbuild/aix-ppc64': 0.25.2 2493 | '@esbuild/android-arm': 0.25.2 2494 | '@esbuild/android-arm64': 0.25.2 2495 | '@esbuild/android-x64': 0.25.2 2496 | '@esbuild/darwin-arm64': 0.25.2 2497 | '@esbuild/darwin-x64': 0.25.2 2498 | '@esbuild/freebsd-arm64': 0.25.2 2499 | '@esbuild/freebsd-x64': 0.25.2 2500 | '@esbuild/linux-arm': 0.25.2 2501 | '@esbuild/linux-arm64': 0.25.2 2502 | '@esbuild/linux-ia32': 0.25.2 2503 | '@esbuild/linux-loong64': 0.25.2 2504 | '@esbuild/linux-mips64el': 0.25.2 2505 | '@esbuild/linux-ppc64': 0.25.2 2506 | '@esbuild/linux-riscv64': 0.25.2 2507 | '@esbuild/linux-s390x': 0.25.2 2508 | '@esbuild/linux-x64': 0.25.2 2509 | '@esbuild/netbsd-arm64': 0.25.2 2510 | '@esbuild/netbsd-x64': 0.25.2 2511 | '@esbuild/openbsd-arm64': 0.25.2 2512 | '@esbuild/openbsd-x64': 0.25.2 2513 | '@esbuild/sunos-x64': 0.25.2 2514 | '@esbuild/win32-arm64': 0.25.2 2515 | '@esbuild/win32-ia32': 0.25.2 2516 | '@esbuild/win32-x64': 0.25.2 2517 | 2518 | escape-string-regexp@1.0.5: {} 2519 | 2520 | escape-string-regexp@4.0.0: {} 2521 | 2522 | eslint-scope@8.3.0: 2523 | dependencies: 2524 | esrecurse: 4.3.0 2525 | estraverse: 5.3.0 2526 | 2527 | eslint-visitor-keys@3.4.3: {} 2528 | 2529 | eslint-visitor-keys@4.2.0: {} 2530 | 2531 | eslint@9.24.0: 2532 | dependencies: 2533 | '@eslint-community/eslint-utils': 4.5.1(eslint@9.24.0) 2534 | '@eslint-community/regexpp': 4.12.1 2535 | '@eslint/config-array': 0.20.0 2536 | '@eslint/config-helpers': 0.2.1 2537 | '@eslint/core': 0.12.0 2538 | '@eslint/eslintrc': 3.3.1 2539 | '@eslint/js': 9.24.0 2540 | '@eslint/plugin-kit': 0.2.8 2541 | '@humanfs/node': 0.16.6 2542 | '@humanwhocodes/module-importer': 1.0.1 2543 | '@humanwhocodes/retry': 0.4.2 2544 | '@types/estree': 1.0.7 2545 | '@types/json-schema': 7.0.15 2546 | ajv: 6.12.6 2547 | chalk: 4.1.2 2548 | cross-spawn: 7.0.6 2549 | debug: 4.4.0 2550 | escape-string-regexp: 4.0.0 2551 | eslint-scope: 8.3.0 2552 | eslint-visitor-keys: 4.2.0 2553 | espree: 10.3.0 2554 | esquery: 1.6.0 2555 | esutils: 2.0.3 2556 | fast-deep-equal: 3.1.3 2557 | file-entry-cache: 8.0.0 2558 | find-up: 5.0.0 2559 | glob-parent: 6.0.2 2560 | ignore: 5.3.2 2561 | imurmurhash: 0.1.4 2562 | is-glob: 4.0.3 2563 | json-stable-stringify-without-jsonify: 1.0.1 2564 | lodash.merge: 4.6.2 2565 | minimatch: 3.1.2 2566 | natural-compare: 1.4.0 2567 | optionator: 0.9.4 2568 | transitivePeerDependencies: 2569 | - supports-color 2570 | 2571 | espree@10.3.0: 2572 | dependencies: 2573 | acorn: 8.14.1 2574 | acorn-jsx: 5.3.2(acorn@8.14.1) 2575 | eslint-visitor-keys: 4.2.0 2576 | 2577 | esquery@1.6.0: 2578 | dependencies: 2579 | estraverse: 5.3.0 2580 | 2581 | esrecurse@4.3.0: 2582 | dependencies: 2583 | estraverse: 5.3.0 2584 | 2585 | estraverse@5.3.0: {} 2586 | 2587 | esutils@2.0.3: {} 2588 | 2589 | fast-deep-equal@3.1.3: {} 2590 | 2591 | fast-json-stable-stringify@2.1.0: {} 2592 | 2593 | fast-levenshtein@2.0.6: {} 2594 | 2595 | file-entry-cache@8.0.0: 2596 | dependencies: 2597 | flat-cache: 4.0.1 2598 | 2599 | find-up@5.0.0: 2600 | dependencies: 2601 | locate-path: 6.0.0 2602 | path-exists: 4.0.0 2603 | 2604 | flat-cache@4.0.1: 2605 | dependencies: 2606 | flatted: 3.3.3 2607 | keyv: 4.5.4 2608 | 2609 | flatted@3.3.3: {} 2610 | 2611 | for-each@0.3.3: 2612 | dependencies: 2613 | is-callable: 1.2.7 2614 | 2615 | foreground-child@3.3.0: 2616 | dependencies: 2617 | cross-spawn: 7.0.5 2618 | signal-exit: 4.1.0 2619 | 2620 | fs-minipass@2.1.0: 2621 | dependencies: 2622 | minipass: 3.3.6 2623 | 2624 | function-bind@1.1.2: {} 2625 | 2626 | function.prototype.name@1.1.6: 2627 | dependencies: 2628 | call-bind: 1.0.7 2629 | define-properties: 1.2.1 2630 | es-abstract: 1.23.5 2631 | functions-have-names: 1.2.3 2632 | 2633 | functions-have-names@1.2.3: {} 2634 | 2635 | get-intrinsic@1.2.4: 2636 | dependencies: 2637 | es-errors: 1.3.0 2638 | function-bind: 1.1.2 2639 | has-proto: 1.0.3 2640 | has-symbols: 1.0.3 2641 | hasown: 2.0.2 2642 | 2643 | get-symbol-description@1.0.2: 2644 | dependencies: 2645 | call-bind: 1.0.7 2646 | es-errors: 1.3.0 2647 | get-intrinsic: 1.2.4 2648 | 2649 | glob-parent@6.0.2: 2650 | dependencies: 2651 | is-glob: 4.0.3 2652 | 2653 | glob@11.0.0: 2654 | dependencies: 2655 | foreground-child: 3.3.0 2656 | jackspeak: 4.0.2 2657 | minimatch: 10.0.1 2658 | minipass: 7.1.2 2659 | package-json-from-dist: 1.0.1 2660 | path-scurry: 2.0.0 2661 | 2662 | globals@14.0.0: {} 2663 | 2664 | globalthis@1.0.4: 2665 | dependencies: 2666 | define-properties: 1.2.1 2667 | gopd: 1.0.1 2668 | 2669 | gopd@1.0.1: 2670 | dependencies: 2671 | get-intrinsic: 1.2.4 2672 | 2673 | graceful-fs@4.2.11: {} 2674 | 2675 | gradient-parser@1.0.2: {} 2676 | 2677 | has-bigints@1.0.2: {} 2678 | 2679 | has-flag@3.0.0: {} 2680 | 2681 | has-flag@4.0.0: {} 2682 | 2683 | has-property-descriptors@1.0.2: 2684 | dependencies: 2685 | es-define-property: 1.0.0 2686 | 2687 | has-proto@1.0.3: {} 2688 | 2689 | has-symbols@1.0.3: {} 2690 | 2691 | has-tostringtag@1.0.2: 2692 | dependencies: 2693 | has-symbols: 1.0.3 2694 | 2695 | hasown@2.0.2: 2696 | dependencies: 2697 | function-bind: 1.1.2 2698 | 2699 | hosted-git-info@2.8.9: {} 2700 | 2701 | iconv-lite@0.6.3: 2702 | dependencies: 2703 | safer-buffer: 2.1.2 2704 | 2705 | ignore@5.3.2: {} 2706 | 2707 | image-size@0.5.5: 2708 | optional: true 2709 | 2710 | import-fresh@3.3.1: 2711 | dependencies: 2712 | parent-module: 1.0.1 2713 | resolve-from: 4.0.0 2714 | 2715 | imurmurhash@0.1.4: {} 2716 | 2717 | internal-slot@1.0.7: 2718 | dependencies: 2719 | es-errors: 1.3.0 2720 | hasown: 2.0.2 2721 | side-channel: 1.0.6 2722 | 2723 | internmap@2.0.3: {} 2724 | 2725 | is-array-buffer@3.0.4: 2726 | dependencies: 2727 | call-bind: 1.0.7 2728 | get-intrinsic: 1.2.4 2729 | 2730 | is-arrayish@0.2.1: {} 2731 | 2732 | is-bigint@1.0.4: 2733 | dependencies: 2734 | has-bigints: 1.0.2 2735 | 2736 | is-boolean-object@1.1.2: 2737 | dependencies: 2738 | call-bind: 1.0.7 2739 | has-tostringtag: 1.0.2 2740 | 2741 | is-callable@1.2.7: {} 2742 | 2743 | is-core-module@2.15.1: 2744 | dependencies: 2745 | hasown: 2.0.2 2746 | 2747 | is-data-view@1.0.1: 2748 | dependencies: 2749 | is-typed-array: 1.1.13 2750 | 2751 | is-date-object@1.0.5: 2752 | dependencies: 2753 | has-tostringtag: 1.0.2 2754 | 2755 | is-extglob@2.1.1: {} 2756 | 2757 | is-fullwidth-code-point@3.0.0: {} 2758 | 2759 | is-glob@4.0.3: 2760 | dependencies: 2761 | is-extglob: 2.1.1 2762 | 2763 | is-negative-zero@2.0.3: {} 2764 | 2765 | is-number-object@1.0.7: 2766 | dependencies: 2767 | has-tostringtag: 1.0.2 2768 | 2769 | is-regex@1.1.4: 2770 | dependencies: 2771 | call-bind: 1.0.7 2772 | has-tostringtag: 1.0.2 2773 | 2774 | is-shared-array-buffer@1.0.3: 2775 | dependencies: 2776 | call-bind: 1.0.7 2777 | 2778 | is-string@1.0.7: 2779 | dependencies: 2780 | has-tostringtag: 1.0.2 2781 | 2782 | is-symbol@1.0.4: 2783 | dependencies: 2784 | has-symbols: 1.0.3 2785 | 2786 | is-typed-array@1.1.13: 2787 | dependencies: 2788 | which-typed-array: 1.1.15 2789 | 2790 | is-weakref@1.0.2: 2791 | dependencies: 2792 | call-bind: 1.0.7 2793 | 2794 | is-what@3.14.1: {} 2795 | 2796 | isarray@2.0.5: {} 2797 | 2798 | isexe@2.0.0: {} 2799 | 2800 | jackspeak@4.0.2: 2801 | dependencies: 2802 | '@isaacs/cliui': 8.0.2 2803 | 2804 | js-yaml@4.1.0: 2805 | dependencies: 2806 | argparse: 2.0.1 2807 | 2808 | json-buffer@3.0.1: {} 2809 | 2810 | json-parse-better-errors@1.0.2: {} 2811 | 2812 | json-schema-traverse@0.4.1: {} 2813 | 2814 | json-stable-stringify-without-jsonify@1.0.1: {} 2815 | 2816 | keyv@4.5.4: 2817 | dependencies: 2818 | json-buffer: 3.0.1 2819 | 2820 | less@4.3.0: 2821 | dependencies: 2822 | copy-anything: 2.0.6 2823 | parse-node-version: 1.0.1 2824 | tslib: 2.8.1 2825 | optionalDependencies: 2826 | errno: 0.1.8 2827 | graceful-fs: 4.2.11 2828 | image-size: 0.5.5 2829 | make-dir: 2.1.0 2830 | mime: 1.6.0 2831 | needle: 3.3.1 2832 | source-map: 0.6.1 2833 | 2834 | levn@0.4.1: 2835 | dependencies: 2836 | prelude-ls: 1.2.1 2837 | type-check: 0.4.0 2838 | 2839 | load-json-file@4.0.0: 2840 | dependencies: 2841 | graceful-fs: 4.2.11 2842 | parse-json: 4.0.0 2843 | pify: 3.0.0 2844 | strip-bom: 3.0.0 2845 | 2846 | locate-path@6.0.0: 2847 | dependencies: 2848 | p-locate: 5.0.0 2849 | 2850 | lodash.merge@4.6.2: {} 2851 | 2852 | lodash@4.17.21: {} 2853 | 2854 | lru-cache@11.0.2: {} 2855 | 2856 | make-dir@2.1.0: 2857 | dependencies: 2858 | pify: 4.0.1 2859 | semver: 5.7.2 2860 | optional: true 2861 | 2862 | memorystream@0.3.1: {} 2863 | 2864 | mime@1.6.0: 2865 | optional: true 2866 | 2867 | minimatch@10.0.1: 2868 | dependencies: 2869 | brace-expansion: 2.0.1 2870 | 2871 | minimatch@3.1.2: 2872 | dependencies: 2873 | brace-expansion: 1.1.11 2874 | 2875 | minipass@3.3.6: 2876 | dependencies: 2877 | yallist: 4.0.0 2878 | 2879 | minipass@5.0.0: {} 2880 | 2881 | minipass@7.1.2: {} 2882 | 2883 | minizlib@2.1.2: 2884 | dependencies: 2885 | minipass: 3.3.6 2886 | yallist: 4.0.0 2887 | 2888 | mkdirp@1.0.4: {} 2889 | 2890 | ms@2.1.3: {} 2891 | 2892 | natural-compare@1.4.0: {} 2893 | 2894 | needle@3.3.1: 2895 | dependencies: 2896 | iconv-lite: 0.6.3 2897 | sax: 1.4.1 2898 | optional: true 2899 | 2900 | nice-try@1.0.5: {} 2901 | 2902 | node-fetch@2.7.0: 2903 | dependencies: 2904 | whatwg-url: 5.0.0 2905 | 2906 | normalize-package-data@2.5.0: 2907 | dependencies: 2908 | hosted-git-info: 2.8.9 2909 | resolve: 1.22.8 2910 | semver: 5.7.2 2911 | validate-npm-package-license: 3.0.4 2912 | 2913 | npm-run-all@4.1.5: 2914 | dependencies: 2915 | ansi-styles: 3.2.1 2916 | chalk: 2.4.2 2917 | cross-spawn: 6.0.6 2918 | memorystream: 0.3.1 2919 | minimatch: 3.1.2 2920 | pidtree: 0.3.1 2921 | read-pkg: 3.0.0 2922 | shell-quote: 1.8.1 2923 | string.prototype.padend: 3.1.6 2924 | 2925 | object-inspect@1.13.3: {} 2926 | 2927 | object-keys@1.1.1: {} 2928 | 2929 | object.assign@4.1.5: 2930 | dependencies: 2931 | call-bind: 1.0.7 2932 | define-properties: 1.2.1 2933 | has-symbols: 1.0.3 2934 | object-keys: 1.1.1 2935 | 2936 | optionator@0.9.4: 2937 | dependencies: 2938 | deep-is: 0.1.4 2939 | fast-levenshtein: 2.0.6 2940 | levn: 0.4.1 2941 | prelude-ls: 1.2.1 2942 | type-check: 0.4.0 2943 | word-wrap: 1.2.5 2944 | 2945 | p-limit@3.1.0: 2946 | dependencies: 2947 | yocto-queue: 0.1.0 2948 | 2949 | p-locate@5.0.0: 2950 | dependencies: 2951 | p-limit: 3.1.0 2952 | 2953 | package-json-from-dist@1.0.1: {} 2954 | 2955 | parent-module@1.0.1: 2956 | dependencies: 2957 | callsites: 3.1.0 2958 | 2959 | parse-json@4.0.0: 2960 | dependencies: 2961 | error-ex: 1.3.2 2962 | json-parse-better-errors: 1.0.2 2963 | 2964 | parse-node-version@1.0.1: {} 2965 | 2966 | path-exists@4.0.0: {} 2967 | 2968 | path-key@2.0.1: {} 2969 | 2970 | path-key@3.1.1: {} 2971 | 2972 | path-parse@1.0.7: {} 2973 | 2974 | path-scurry@2.0.0: 2975 | dependencies: 2976 | lru-cache: 11.0.2 2977 | minipass: 7.1.2 2978 | 2979 | path-type@3.0.0: 2980 | dependencies: 2981 | pify: 3.0.0 2982 | 2983 | pidtree@0.3.1: {} 2984 | 2985 | pify@3.0.0: {} 2986 | 2987 | pify@4.0.1: 2988 | optional: true 2989 | 2990 | possible-typed-array-names@1.0.0: {} 2991 | 2992 | prelude-ls@1.2.1: {} 2993 | 2994 | prettier@3.5.3: {} 2995 | 2996 | prr@1.0.1: 2997 | optional: true 2998 | 2999 | punycode@2.3.1: {} 3000 | 3001 | read-pkg@3.0.0: 3002 | dependencies: 3003 | load-json-file: 4.0.0 3004 | normalize-package-data: 2.5.0 3005 | path-type: 3.0.0 3006 | 3007 | regexp.prototype.flags@1.5.3: 3008 | dependencies: 3009 | call-bind: 1.0.7 3010 | define-properties: 1.2.1 3011 | es-errors: 1.3.0 3012 | set-function-name: 2.0.2 3013 | 3014 | regular-table@0.6.6: {} 3015 | 3016 | resolve-from@4.0.0: {} 3017 | 3018 | resolve@1.22.8: 3019 | dependencies: 3020 | is-core-module: 2.15.1 3021 | path-parse: 1.0.7 3022 | supports-preserve-symlinks-flag: 1.0.0 3023 | 3024 | rimraf@6.0.1: 3025 | dependencies: 3026 | glob: 11.0.0 3027 | package-json-from-dist: 1.0.1 3028 | 3029 | robust-predicates@3.0.2: {} 3030 | 3031 | rw@1.3.3: {} 3032 | 3033 | safe-array-concat@1.1.2: 3034 | dependencies: 3035 | call-bind: 1.0.7 3036 | get-intrinsic: 1.2.4 3037 | has-symbols: 1.0.3 3038 | isarray: 2.0.5 3039 | 3040 | safe-regex-test@1.0.3: 3041 | dependencies: 3042 | call-bind: 1.0.7 3043 | es-errors: 1.3.0 3044 | is-regex: 1.1.4 3045 | 3046 | safer-buffer@2.1.2: {} 3047 | 3048 | sax@1.4.1: 3049 | optional: true 3050 | 3051 | semver@5.7.2: {} 3052 | 3053 | set-function-length@1.2.2: 3054 | dependencies: 3055 | define-data-property: 1.1.4 3056 | es-errors: 1.3.0 3057 | function-bind: 1.1.2 3058 | get-intrinsic: 1.2.4 3059 | gopd: 1.0.1 3060 | has-property-descriptors: 1.0.2 3061 | 3062 | set-function-name@2.0.2: 3063 | dependencies: 3064 | define-data-property: 1.1.4 3065 | es-errors: 1.3.0 3066 | functions-have-names: 1.2.3 3067 | has-property-descriptors: 1.0.2 3068 | 3069 | shebang-command@1.2.0: 3070 | dependencies: 3071 | shebang-regex: 1.0.0 3072 | 3073 | shebang-command@2.0.0: 3074 | dependencies: 3075 | shebang-regex: 3.0.0 3076 | 3077 | shebang-regex@1.0.0: {} 3078 | 3079 | shebang-regex@3.0.0: {} 3080 | 3081 | shell-quote@1.8.1: {} 3082 | 3083 | side-channel@1.0.6: 3084 | dependencies: 3085 | call-bind: 1.0.7 3086 | es-errors: 1.3.0 3087 | get-intrinsic: 1.2.4 3088 | object-inspect: 1.13.3 3089 | 3090 | signal-exit@4.1.0: {} 3091 | 3092 | source-map@0.6.1: 3093 | optional: true 3094 | 3095 | spdx-correct@3.2.0: 3096 | dependencies: 3097 | spdx-expression-parse: 3.0.1 3098 | spdx-license-ids: 3.0.20 3099 | 3100 | spdx-exceptions@2.5.0: {} 3101 | 3102 | spdx-expression-parse@3.0.1: 3103 | dependencies: 3104 | spdx-exceptions: 2.5.0 3105 | spdx-license-ids: 3.0.20 3106 | 3107 | spdx-license-ids@3.0.20: {} 3108 | 3109 | stoppable@1.1.0: {} 3110 | 3111 | string-width@4.2.3: 3112 | dependencies: 3113 | emoji-regex: 8.0.0 3114 | is-fullwidth-code-point: 3.0.0 3115 | strip-ansi: 6.0.1 3116 | 3117 | string-width@5.1.2: 3118 | dependencies: 3119 | eastasianwidth: 0.2.0 3120 | emoji-regex: 9.2.2 3121 | strip-ansi: 7.1.0 3122 | 3123 | string.prototype.padend@3.1.6: 3124 | dependencies: 3125 | call-bind: 1.0.7 3126 | define-properties: 1.2.1 3127 | es-abstract: 1.23.5 3128 | es-object-atoms: 1.0.0 3129 | 3130 | string.prototype.trim@1.2.9: 3131 | dependencies: 3132 | call-bind: 1.0.7 3133 | define-properties: 1.2.1 3134 | es-abstract: 1.23.5 3135 | es-object-atoms: 1.0.0 3136 | 3137 | string.prototype.trimend@1.0.8: 3138 | dependencies: 3139 | call-bind: 1.0.7 3140 | define-properties: 1.2.1 3141 | es-object-atoms: 1.0.0 3142 | 3143 | string.prototype.trimstart@1.0.8: 3144 | dependencies: 3145 | call-bind: 1.0.7 3146 | define-properties: 1.2.1 3147 | es-object-atoms: 1.0.0 3148 | 3149 | strip-ansi@6.0.1: 3150 | dependencies: 3151 | ansi-regex: 5.0.1 3152 | 3153 | strip-ansi@7.1.0: 3154 | dependencies: 3155 | ansi-regex: 6.1.0 3156 | 3157 | strip-bom@3.0.0: {} 3158 | 3159 | strip-json-comments@3.1.1: {} 3160 | 3161 | supports-color@5.5.0: 3162 | dependencies: 3163 | has-flag: 3.0.0 3164 | 3165 | supports-color@7.2.0: 3166 | dependencies: 3167 | has-flag: 4.0.0 3168 | 3169 | supports-preserve-symlinks-flag@1.0.0: {} 3170 | 3171 | tar@6.2.1: 3172 | dependencies: 3173 | chownr: 2.0.0 3174 | fs-minipass: 2.1.0 3175 | minipass: 5.0.0 3176 | minizlib: 2.1.2 3177 | mkdirp: 1.0.4 3178 | yallist: 4.0.0 3179 | 3180 | tr46@0.0.3: {} 3181 | 3182 | tslib@2.8.1: {} 3183 | 3184 | type-check@0.4.0: 3185 | dependencies: 3186 | prelude-ls: 1.2.1 3187 | 3188 | typed-array-buffer@1.0.2: 3189 | dependencies: 3190 | call-bind: 1.0.7 3191 | es-errors: 1.3.0 3192 | is-typed-array: 1.1.13 3193 | 3194 | typed-array-byte-length@1.0.1: 3195 | dependencies: 3196 | call-bind: 1.0.7 3197 | for-each: 0.3.3 3198 | gopd: 1.0.1 3199 | has-proto: 1.0.3 3200 | is-typed-array: 1.1.13 3201 | 3202 | typed-array-byte-offset@1.0.2: 3203 | dependencies: 3204 | available-typed-arrays: 1.0.7 3205 | call-bind: 1.0.7 3206 | for-each: 0.3.3 3207 | gopd: 1.0.1 3208 | has-proto: 1.0.3 3209 | is-typed-array: 1.1.13 3210 | 3211 | typed-array-length@1.0.6: 3212 | dependencies: 3213 | call-bind: 1.0.7 3214 | for-each: 0.3.3 3215 | gopd: 1.0.1 3216 | has-proto: 1.0.3 3217 | is-typed-array: 1.1.13 3218 | possible-typed-array-names: 1.0.0 3219 | 3220 | unbox-primitive@1.0.2: 3221 | dependencies: 3222 | call-bind: 1.0.7 3223 | has-bigints: 1.0.2 3224 | has-symbols: 1.0.3 3225 | which-boxed-primitive: 1.0.2 3226 | 3227 | uri-js@4.4.1: 3228 | dependencies: 3229 | punycode: 2.3.1 3230 | 3231 | validate-npm-package-license@3.0.4: 3232 | dependencies: 3233 | spdx-correct: 3.2.0 3234 | spdx-expression-parse: 3.0.1 3235 | 3236 | webidl-conversions@3.0.1: {} 3237 | 3238 | whatwg-url@5.0.0: 3239 | dependencies: 3240 | tr46: 0.0.3 3241 | webidl-conversions: 3.0.1 3242 | 3243 | which-boxed-primitive@1.0.2: 3244 | dependencies: 3245 | is-bigint: 1.0.4 3246 | is-boolean-object: 1.1.2 3247 | is-number-object: 1.0.7 3248 | is-string: 1.0.7 3249 | is-symbol: 1.0.4 3250 | 3251 | which-typed-array@1.1.15: 3252 | dependencies: 3253 | available-typed-arrays: 1.0.7 3254 | call-bind: 1.0.7 3255 | for-each: 0.3.3 3256 | gopd: 1.0.1 3257 | has-tostringtag: 1.0.2 3258 | 3259 | which@1.3.1: 3260 | dependencies: 3261 | isexe: 2.0.0 3262 | 3263 | which@2.0.2: 3264 | dependencies: 3265 | isexe: 2.0.0 3266 | 3267 | word-wrap@1.2.5: {} 3268 | 3269 | wrap-ansi@7.0.0: 3270 | dependencies: 3271 | ansi-styles: 4.3.0 3272 | string-width: 4.2.3 3273 | strip-ansi: 6.0.1 3274 | 3275 | wrap-ansi@8.1.0: 3276 | dependencies: 3277 | ansi-styles: 6.2.1 3278 | string-width: 5.1.2 3279 | strip-ansi: 7.1.0 3280 | 3281 | ws@8.18.1: {} 3282 | 3283 | yallist@4.0.0: {} 3284 | 3285 | yocto-queue@0.1.0: {} 3286 | -------------------------------------------------------------------------------- /js/src/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Material+Icons"); 2 | @import url("https://fonts.googleapis.com/css?family=Roboto+Mono"); 3 | 4 | html { 5 | font-family: "Roboto Mono"; 6 | } 7 | 8 | perspective-workspace { 9 | position: absolute; 10 | top: 40px; 11 | bottom: 0; 12 | left: 0; 13 | right: 0; 14 | } 15 | 16 | .title-bar { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | right: 0; 21 | height: 40px; 22 | display: flex; 23 | justify-content: space-between; 24 | align-items: center; 25 | padding: 0 10px; 26 | z-index: 1000; 27 | border-bottom: 1px solid #ccc; 28 | } 29 | 30 | .title { 31 | font-size: 18px; 32 | font-weight: bold; 33 | } 34 | .save-button { 35 | padding: 5px 10px; 36 | border: none; 37 | cursor: pointer; 38 | } 39 | -------------------------------------------------------------------------------- /js/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | raydar 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | Raydar v{{ version }} 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /js/src/index.js: -------------------------------------------------------------------------------- 1 | import perspective from "@finos/perspective"; 2 | import "@finos/perspective-viewer"; 3 | import "@finos/perspective-viewer-datagrid"; 4 | import "@finos/perspective-viewer-d3fc"; 5 | import "@finos/perspective-workspace/dist/css/pro.css"; 6 | import "@finos/perspective-viewer/dist/css/themes.css"; 7 | import "@finos/perspective-workspace"; 8 | import "./index.css"; 9 | 10 | function removeTrailingSlash(url) { 11 | return url.replace(/\/$/, ""); 12 | } 13 | 14 | async function load() { 15 | const workspace = document.querySelector("perspective-workspace"); 16 | const saveButton = document.getElementById("save-layout-button"); 17 | const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; 18 | const websocket = await perspective.websocket( 19 | `${protocol}//${window.location.host}${removeTrailingSlash( 20 | window.location.pathname, 21 | )}/ws`, 22 | ); 23 | const registeredTables = new Set(); 24 | const updateTables = async () => { 25 | const response = await fetch( 26 | `${removeTrailingSlash(window.location.href)}/tables`, 27 | ); 28 | const tables = await response.json(); 29 | 30 | tables.map(async (tableName) => { 31 | if (registeredTables.has(tableName)) return; 32 | registeredTables.add(tableName); 33 | workspace.addTable(tableName, await websocket.open_table(tableName)); 34 | }); 35 | }; 36 | 37 | document.body.addEventListener("dragover", (e) => { 38 | e.preventDefault(); 39 | }); 40 | 41 | document.body.addEventListener("drop", (e) => { 42 | e.preventDefault(); 43 | 44 | const file = e.dataTransfer.files[0]; 45 | if (file) { 46 | const reader = new FileReader(); 47 | reader.onload = (event) => { 48 | try { 49 | const json = JSON.parse(event.target.result); 50 | workspace.restore(json); 51 | console.log("File contents:", json); 52 | } catch (error) { 53 | console.error("Error parsing JSON:", error); 54 | } 55 | }; 56 | reader.readAsText(file); 57 | } 58 | }); 59 | 60 | saveButton.addEventListener("click", function () { 61 | let workspace = document.querySelector("perspective-workspace"); 62 | 63 | workspace.save().then((config) => { 64 | // Convert the configuration object to a JSON string 65 | let json = JSON.stringify(config); 66 | 67 | // Create a Blob object from the JSON string 68 | let blob = new Blob([json], { type: "application/json" }); 69 | 70 | // Create a download link 71 | let link = document.createElement("a"); 72 | link.href = URL.createObjectURL(blob); 73 | link.download = "workspace.json"; 74 | 75 | // Append the link to the document body and click it to start the download 76 | document.body.appendChild(link); 77 | link.click(); 78 | document.body.removeChild(link); 79 | }); 80 | }); 81 | 82 | const layouts = await fetch("static/layouts/default.json"); 83 | const layoutData = await layouts.json(); 84 | console.log("Loading layout from static/layouts/default.json..."); 85 | workspace.restore(layoutData); 86 | 87 | await updateTables(); 88 | 89 | // update tables every 5s 90 | setInterval(updateTables, 5000); 91 | } 92 | 93 | load(); 94 | -------------------------------------------------------------------------------- /js/src/layouts/default.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling>=1.22.4,<1.28", 4 | "pkginfo>=1.10,<1.13", 5 | "hatch-jupyter-builder", 6 | ] 7 | build-backend = "hatchling.build" 8 | 9 | [project] 10 | name = "raydar" 11 | description = "A perspective powered, user editable ray dashboard via ray serve" 12 | version = "0.2.4" 13 | readme = "README.md" 14 | license = { file = "LICENSE" } 15 | requires-python = ">=3.8" 16 | authors = [ 17 | { name = "Point72", email = "OpenSource@Point72.com" }, 18 | ] 19 | keywords = [ 20 | "perspective", 21 | "ray", 22 | "dashboard", 23 | "dataviz", 24 | "data visualization", 25 | "cluster", 26 | "cluster visualization", 27 | ] 28 | classifiers = [ 29 | "Development Status :: 4 - Beta", 30 | "Framework :: Jupyter", 31 | "Framework :: Jupyter :: JupyterLab", 32 | "Programming Language :: Python", 33 | "Programming Language :: Python :: 3", 34 | "Programming Language :: Python :: 3.8", 35 | "Programming Language :: Python :: 3.9", 36 | "Programming Language :: Python :: 3.10", 37 | "Programming Language :: Python :: 3.11", 38 | "Programming Language :: Python :: 3.12", 39 | "License :: OSI Approved :: Apache Software License", 40 | ] 41 | 42 | dependencies = [ 43 | "coolname", 44 | "fastapi", 45 | "jinja2", 46 | "packaging", 47 | "perspective-python>=3,<4", 48 | "polars", 49 | "pyarrow", 50 | "pydantic", 51 | "ray[serve]>=2.8", 52 | ] 53 | 54 | [project.optional-dependencies] 55 | develop = [ 56 | "build", 57 | "bump-my-version", 58 | "check-manifest", 59 | "hatchling", 60 | "hatch-jupyter-builder", 61 | "ruff>=0.5.0,<0.12", 62 | "toml", 63 | "twine", 64 | "wheel", 65 | # Test deps 66 | "pandas", 67 | "pytest", 68 | "pytest-asyncio", 69 | "pytest-cov", 70 | "pytest-sugar", 71 | ] 72 | 73 | test = [ 74 | "pytest", 75 | "pytest-asyncio", 76 | "pytest-cov", 77 | "pytest-sugar", 78 | ] 79 | 80 | [project.urls] 81 | Repository = "https://github.com/point72/raydar" 82 | Homepage = "https://github.com/point72/raydar" 83 | 84 | [tool.bumpversion] 85 | current_version = "0.2.4" 86 | commit = true 87 | tag = false 88 | commit_args = "-s" 89 | 90 | [[tool.bumpversion.files]] 91 | filename = "pyproject.toml" 92 | search = 'version = "{current_version}"' 93 | replace = 'version = "{new_version}"' 94 | 95 | [[tool.bumpversion.files]] 96 | filename = "raydar/__init__.py" 97 | search = '__version__ = "{current_version}"' 98 | replace = '__version__ = "{new_version}"' 99 | 100 | [[tool.bumpversion.files]] 101 | filename = "js/package.json" 102 | search = '"version": "{current_version}"' 103 | replace = '"version": "{new_version}"' 104 | 105 | [[tool.bumpversion.files]] 106 | filename = "js/src/index.html" 107 | search = 'Raydar v{current_version}' 108 | replace = 'Raydar v{new_version}' 109 | 110 | [tool.check-manifest] 111 | ignore = [ 112 | "raydar/dashboard/static/*", 113 | "js/**", 114 | ] 115 | 116 | [tool.hatch.build] 117 | artifacts = [ 118 | "raydar/dashboard/static" 119 | ] 120 | 121 | [tool.hatch.build.sources] 122 | src = "/" 123 | 124 | [tool.hatch.build.targets.sdist] 125 | include = [ 126 | "/raydar", 127 | "/js", 128 | "LICENSE", 129 | "README.md", 130 | ] 131 | exclude = [ 132 | "/.github", 133 | "/.gitignore", 134 | "/.mypy_cache", 135 | "/.ruff_cache", 136 | "/dist", 137 | "/docs", 138 | "/js/node_modules", 139 | ] 140 | 141 | [tool.hatch.build.targets.wheel] 142 | include = [ 143 | "/raydar", 144 | ] 145 | exclude = [ 146 | "/.github", 147 | "/.gitignore", 148 | "/.mypy_cache", 149 | "/.ruff_cache", 150 | "/pyproject.toml", 151 | "/dist", 152 | "/docs", 153 | "/js" 154 | ] 155 | 156 | [tool.hatch.build.hooks.jupyter-builder] 157 | build-function = "hatch_jupyter_builder.npm_builder" 158 | ensured-targets = [ 159 | "raydar/dashboard/static/index.js", 160 | "raydar/dashboard/static/index.html", 161 | "raydar/dashboard/static/index.css", 162 | ] 163 | skip-if-exists = [ 164 | "raydar/dashboard/static/index.js", 165 | ] 166 | dependencies = [ 167 | "hatch-jupyter-builder>=0.5.0", 168 | ] 169 | 170 | [tool.hatch.build.hooks.jupyter-builder.build-kwargs] 171 | path = "js" 172 | build_cmd = "build" 173 | npm = "pnpm" 174 | 175 | [tool.ruff] 176 | line-length = 150 177 | 178 | [tool.ruff.lint] 179 | extend-select = ["I"] 180 | 181 | [tool.ruff.lint.isort] 182 | combine-as-imports = true 183 | default-section = "third-party" 184 | known-first-party = ["raydar"] 185 | section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] 186 | 187 | [tool.ruff.lint.per-file-ignores] 188 | "__init__.py" = ["F401", "F403"] 189 | -------------------------------------------------------------------------------- /raydar/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.2.4" 2 | 3 | # import this first, might need to monkeypatch ray 4 | # https://github.com/ray-project/ray/issues/42654 5 | from .dashboard import * 6 | from .task_tracker import * 7 | -------------------------------------------------------------------------------- /raydar/dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | from .server import PerspectiveProxyRayServer, PerspectiveRayServer 2 | -------------------------------------------------------------------------------- /raydar/dashboard/demo.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import time 4 | 5 | import ray 6 | 7 | from .server import PerspectiveProxyRayServer, PerspectiveRayServer 8 | 9 | 10 | @ray.remote 11 | def test_job(backoff, tablename, proxy): 12 | start = time.time() 13 | time.sleep(backoff) 14 | end = time.time() 15 | runtime = end - start 16 | data = dict(start=start, end=end, runtime=runtime, backoff=backoff, random=random.random()) 17 | return proxy.remote("update", tablename, data) 18 | 19 | 20 | if __name__ == "__main__": 21 | os.environ["RAY_SERVE_ENABLE_EXPERIMENTAL_STREAMING"] = "1" 22 | 23 | host = "127.0.0.1" # NOTE: change if you run on another machine 24 | port = 8989 25 | ray.init(dashboard_host=host, dashboard_port=port) 26 | ray.serve.start(http_options={"host": host, "port": port + 1}) 27 | 28 | webserver = ray.serve.run(PerspectiveRayServer.bind(), name="webserver", route_prefix="/") 29 | proxy_server = ray.serve.run(PerspectiveProxyRayServer.bind(webserver), name="proxy", route_prefix="/proxy") 30 | 31 | # setup perspective table 32 | proxy_server.remote( 33 | "new", 34 | "data", 35 | { 36 | "start": "datetime", 37 | "end": "datetime", 38 | "runtime": "float", 39 | "backoff": "float", 40 | "random": "float", 41 | }, 42 | ) 43 | 44 | # launch jobs 45 | while True: 46 | test_job.remote(backoff=random.random(), tablename="data", proxy=proxy_server) 47 | time.sleep(0.5) 48 | -------------------------------------------------------------------------------- /raydar/dashboard/server.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from os import environ 3 | from os.path import abspath, dirname, join 4 | from threading import Thread 5 | from traceback import format_exc 6 | 7 | import perspective 8 | from fastapi import FastAPI, HTTPException, Request, Response, WebSocket, WebSocketDisconnect 9 | from fastapi.staticfiles import StaticFiles 10 | from fastapi.templating import Jinja2Templates 11 | from perspective.handlers.starlette import PerspectiveStarletteHandler 12 | from pydantic import BaseModel, Field 13 | from ray.serve import Application, deployment, ingress 14 | 15 | from .. import __version__ 16 | 17 | 18 | class PerspectiveRayServerArgs(BaseModel): 19 | name: str = Field(default="Perspective") 20 | 21 | 22 | def perspective_thread(): 23 | from asyncio import new_event_loop 24 | 25 | psp_loop = new_event_loop() 26 | perspective.GLOBAL_CLIENT.set_loop_callback(psp_loop.call_soon_threadsafe) 27 | psp_loop.run_forever() 28 | 29 | 30 | app = FastAPI() 31 | logger = logging.getLogger("ray.serve") 32 | static_files_dir = join(abspath(dirname(__file__)), "static") 33 | templates = Jinja2Templates(static_files_dir) 34 | app.mount("/static", StaticFiles(directory=static_files_dir, check_dir=False, html=True)) 35 | 36 | 37 | @deployment(name="Perspective_Web_Server", num_replicas=1) 38 | @ingress(app) 39 | class PerspectiveRayServer: 40 | def __init__(self, args: PerspectiveRayServerArgs = None): 41 | logger.setLevel(logging.ERROR) 42 | args = args or PerspectiveRayServerArgs() 43 | self._schemas = {} 44 | self._tables = {} 45 | self._psp_thread = Thread(target=perspective_thread, daemon=True) 46 | self._psp_thread.start() 47 | 48 | def new_table(self, tablename: str, schema) -> None: 49 | if tablename in self._schemas: 50 | return self._schemas[tablename] 51 | self._schemas[tablename] = schema 52 | self._tables[tablename] = perspective.table(schema, name=tablename) 53 | 54 | def clear_table(self, tablename: str, schema) -> None: 55 | if tablename in self._tables: 56 | self._tables[tablename].clear() 57 | 58 | def update(self, tablename: str, data): 59 | if isinstance(data, dict): 60 | data = [data] 61 | self._tables[tablename].update(data) 62 | 63 | @app.websocket("/ws") 64 | async def ws(self, ws: WebSocket): 65 | handler = PerspectiveStarletteHandler(websocket=ws) 66 | try: 67 | await handler.run() 68 | except WebSocketDisconnect: 69 | ... 70 | 71 | @app.get("/") 72 | async def site(self, request: Request): 73 | return templates.TemplateResponse(request=request, name="index.html", context={"version": __version__, "javascript": "index.js"}) 74 | 75 | @app.get("/version") 76 | async def version(self): 77 | return __version__ 78 | 79 | @app.get("/tables") 80 | async def tables(self): 81 | return list(self._schemas.keys()) 82 | 83 | @app.post("/new/{tablename}") 84 | async def new_table_rest(self, tablename: str, request: Request) -> Response: 85 | if tablename in self._schemas: 86 | raise HTTPException(501, "Table already exists, replace not yet supported") 87 | try: 88 | schema = await request.json() 89 | except BaseException as exception: 90 | raise HTTPException(503, "Exception during schema parsing") from exception 91 | 92 | try: 93 | self.new_table(tablename, schema) 94 | return Response(content=schema) 95 | except BaseException as exception: 96 | raise HTTPException( 97 | 503, 98 | f"Exception during table creation: {schema} / {tablename} / {format_exc()}", 99 | ) from exception 100 | 101 | @app.get("/get/{tablename}") 102 | async def get_table_rest(self, tablename: str): 103 | if tablename not in self._schemas: 104 | raise HTTPException(404, "Table does not exist: {tablename}") 105 | return Response(content=self._schemas[tablename]) 106 | 107 | @app.post("/update/{tablename}") 108 | async def update_table_rest(self, tablename: str, request: Request) -> Response: 109 | if tablename not in self._schemas: 110 | # just create table for now 111 | self.new_table(tablename, []) 112 | try: 113 | data = await request.json() 114 | except BaseException as exception: 115 | raise HTTPException(503, "Exception during data parsing") from exception 116 | 117 | try: 118 | self.update(tablename, data) 119 | except BaseException as exception: 120 | raise HTTPException(503, f"Exception during data ingestion: {tablename} / {format_exc()}") from exception 121 | 122 | 123 | @deployment(name="Perspective_Proxy_Server") 124 | class PerspectiveProxyRayServer: 125 | def __init__(self, psp_handle): 126 | logger.setLevel(logging.ERROR) 127 | self._psp_handle = psp_handle 128 | 129 | def __call__(self, op, tablename, data_or_schema): 130 | if op == "new": 131 | if tablename and data_or_schema: 132 | self._psp_handle.new_table.remote(tablename, data_or_schema) 133 | if op == "update": 134 | if tablename and data_or_schema: 135 | self._psp_handle.update.remote(tablename, data_or_schema) 136 | if op == "clear": 137 | if tablename: 138 | self._psp_handle.clear_table.remote(tablename, data_or_schema) 139 | 140 | 141 | def main(args: PerspectiveRayServerArgs = None) -> Application: 142 | args = args or PerspectiveRayServerArgs() 143 | if environ.get("RAY_SERVE_ENABLE_EXPERIMENTAL_STREAMING") is not None: 144 | return PerspectiveProxyRayServer.bind(PerspectiveRayServer.bind(args)) 145 | raise Exception("Perspective server requires websockets, rerun with RAY_SERVE_ENABLE_EXPERIMENTAL_STREAMING=1") 146 | -------------------------------------------------------------------------------- /raydar/task_tracker/__init__.py: -------------------------------------------------------------------------------- 1 | from .task_tracker import * 2 | -------------------------------------------------------------------------------- /raydar/task_tracker/schema.py: -------------------------------------------------------------------------------- 1 | import polars as pl 2 | 3 | schema = { 4 | "task_id": pl.Utf8, 5 | "attempt_number": pl.Int64, 6 | "name": pl.Utf8, 7 | "state": pl.Utf8, 8 | "job_id": pl.Utf8, 9 | "actor_id": pl.Float32, 10 | "type": pl.Utf8, 11 | "func_or_class_name": pl.Utf8, 12 | "parent_task_id": pl.Utf8, 13 | "node_id": pl.Utf8, 14 | "worker_id": pl.Utf8, 15 | "error_type": pl.Utf8, 16 | "language": pl.Utf8, 17 | "required_resources": pl.Struct([pl.Field("CPU", pl.Float64)]), 18 | "runtime_env_info": pl.Struct( 19 | [ 20 | pl.Field("serialized_runtime_env", pl.Utf8), 21 | pl.Field( 22 | "runtime_env_config", 23 | pl.Struct( 24 | [ 25 | pl.Field("setup_timeout_seconds", pl.Int64), 26 | pl.Field("eager_install", pl.Boolean), 27 | ] 28 | ), 29 | ), 30 | ] 31 | ), 32 | "placement_group_id": pl.Float32, 33 | "events": pl.List(pl.Struct([pl.Field("state", pl.Utf8), pl.Field("created_ms", pl.Float64)])), 34 | "profiling_data": pl.Struct( 35 | [ 36 | pl.Field("component_type", pl.Utf8), 37 | pl.Field("component_id", pl.Utf8), 38 | pl.Field("node_ip_address", pl.Utf8), 39 | pl.Field( 40 | "events", 41 | pl.List( 42 | pl.Struct( 43 | [ 44 | pl.Field( 45 | "start_time", 46 | pl.Datetime(time_unit="ms", time_zone="America/New_York"), 47 | ), 48 | pl.Field( 49 | "end_time", 50 | pl.Datetime(time_unit="ms", time_zone="America/New_York"), 51 | ), 52 | pl.Field( 53 | "extra_data", 54 | pl.Struct( 55 | [ 56 | pl.Field("name", pl.Utf8), 57 | pl.Field("task_id", pl.Utf8), 58 | ] 59 | ), 60 | ), 61 | pl.Field("event_name", pl.Utf8), 62 | ] 63 | ) 64 | ), 65 | ), 66 | ] 67 | ), 68 | "creation_time_ms": pl.Datetime(time_unit="ms", time_zone="America/New_York"), 69 | "start_time_ms": pl.Datetime(time_unit="ms", time_zone="America/New_York"), 70 | "end_time_ms": pl.Datetime(time_unit="ms", time_zone="America/New_York"), 71 | "task_log_info": pl.Struct( 72 | [ 73 | pl.Field("stdout_file", pl.Utf8), 74 | pl.Field("stderr_file", pl.Utf8), 75 | pl.Field("stdout_start", pl.Int64), 76 | pl.Field("stdout_end", pl.Int64), 77 | pl.Field("stderr_start", pl.Int64), 78 | pl.Field("stderr_end", pl.Int64), 79 | ] 80 | ), 81 | "error_message": pl.Utf8, 82 | } 83 | -------------------------------------------------------------------------------- /raydar/task_tracker/task_tracker.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import itertools 3 | import logging 4 | import os 5 | from collections.abc import Iterable 6 | from typing import Dict, List, Optional 7 | 8 | import coolname 9 | import pandas as pd 10 | import polars as pl 11 | import ray 12 | from packaging.version import Version 13 | from ray.serve import shutdown 14 | from ray.serve.handle import DeploymentHandle 15 | 16 | from .schema import schema as default_schema 17 | 18 | logger = logging.getLogger(__name__) 19 | 20 | __all__ = ("AsyncMetadataTracker", "RayTaskTracker", "setup_proxy_server") 21 | 22 | 23 | def get_callback_actor_name(name: str) -> str: 24 | return f"{name}_callback_actor" 25 | 26 | 27 | def setup_proxy_server(proxy_server_name="proxy", proxy_server_route_prefix="/proxy", **kwargs) -> DeploymentHandle: 28 | """Construct a webserver, and bind it to a PerspectiveProxyRayServer. 29 | 30 | Args: 31 | proxy_server_name: the name passed to ray.serve.run for the PerspectiveProxyRayServer 32 | proxy_server_route_prefix: the route_prefix passed to ray.serve.run for the PerspectiveProxyRayServer 33 | **kwargs: arguments forwarded to ray.serve.run() for the webserver 34 | 35 | Returns: A DeploymentHandle for the PerspectiveProxyRayServer 36 | """ 37 | from raydar.dashboard.server import PerspectiveProxyRayServer 38 | 39 | webserver = ray.serve.run(**kwargs) 40 | proxy_server = ray.serve.run( 41 | PerspectiveProxyRayServer.bind(webserver), 42 | name=proxy_server_name, 43 | route_prefix=proxy_server_route_prefix, 44 | ) 45 | return proxy_server 46 | 47 | 48 | @ray.remote(resources={"node:__internal_head__": 0.1}, num_cpus=0) 49 | class AsyncMetadataTrackerCallback: 50 | """ 51 | Intended to be constructed from an AsyncMetadataTracker actor, owning an attribute pointing 52 | back to that actor. 53 | """ 54 | 55 | def __init__(self, name: str, namespace: str): 56 | self.actor = ray.get_actor(name, namespace) 57 | 58 | def process(self, obj_refs: Iterable[ray.ObjectRef]) -> None: 59 | """Processes an interable collection of ray.ObjectRefs. 60 | 61 | Iterates through the collection, finds completed references, and returns those references to the 62 | self.actor attribute via its .callback remote function. 63 | 64 | Args: 65 | obj_refs: An iterable collection of (possibly) in-progress ray object references 66 | """ 67 | active_tasks = set(obj_refs) 68 | while len(active_tasks) > 0: 69 | finished_tasks = [] 70 | for obj_ref in obj_refs: 71 | if obj_ref in active_tasks: 72 | done, _ = ray.wait([obj_ref], timeout=0.0, fetch_local=False) 73 | if done: 74 | active_tasks.remove(done[0]) 75 | finished_tasks.append(done[0]) 76 | if len(finished_tasks) > 0: 77 | self.actor.callback.remote(finished_tasks) 78 | return 79 | 80 | def exit(self) -> None: 81 | """Terminate this actor""" 82 | ray.actor.exit_actor() 83 | 84 | 85 | @ray.remote(resources={"node:__internal_head__": 0.1}, num_cpus=0) 86 | class AsyncMetadataTracker: 87 | def __init__( 88 | self, 89 | name: str, 90 | namespace: str, 91 | path: Optional[str] = None, 92 | enable_perspective_dashboard: bool = False, 93 | ): 94 | """An async Ray Actor Class to track task level metadata. 95 | 96 | This class constructs a AsyncMetadataTrackerCallback actor, which points back to this actor. Its process(...) 97 | method sends lists of object references to its AsyncMetadataTrackerCallback, which performs blocking ray.wait(...) 98 | calls on those object references, and calls this actor's callback(...) method as those tasks complete. 99 | 100 | Args: 101 | name: Ray actor name, used to construct its AsyncMetadataTrackerCallback actor attribute. 102 | namespace: Ray Namespace 103 | path: A Cloudpathlib.AnyPath, used for saving its internal polars DataFrame object. 104 | enable_perspective_dashboard: To enable an experimental perspective dashboard. 105 | 106 | """ 107 | logger.info(f"Initializing an AsyncMetadataTracker in namespace {namespace} with name {name}.") 108 | # Passing 'self' to the AsyncMetadataTrackerCallback converts this actor class to a 109 | # modify_Class..Class object. So for now, we pass the name and 110 | # namespace used to construct this actor to its AsyncMetadataTrackerCallback. 111 | self.processor = AsyncMetadataTrackerCallback.options( 112 | name=get_callback_actor_name(name), 113 | namespace=namespace, 114 | lifetime="detached", 115 | get_if_exists=True, 116 | ).remote(name, namespace) 117 | self.path = path 118 | self.df = None 119 | self.finished_tasks = {} 120 | self.user_defined_metadata = {} 121 | self.perspective_dashboard_enabled = enable_perspective_dashboard 122 | self.pending_tasks = [] 123 | self.perspective_table_name = f"{name}_data" 124 | 125 | # WARNING: Do not move this import. Importing these modules elsewhere can cause 126 | # difficult to diagnose, "There is no current event loop in thread 'ray_client_server_" errors. 127 | asyncio.set_event_loop(asyncio.new_event_loop()) 128 | from ray.util.state.api import StateApiClient 129 | 130 | self.client = StateApiClient(address=ray.get_runtime_context().gcs_address) 131 | 132 | if self.perspective_dashboard_enabled: 133 | from raydar.dashboard.server import PerspectiveRayServer 134 | 135 | kwargs = dict( 136 | target=PerspectiveRayServer.bind(), 137 | name="webserver", 138 | route_prefix="/", 139 | ) 140 | 141 | if Version(ray.__version__) < Version("2.10"): 142 | kwargs["port"] = os.environ.get("RAYDAR_PORT", 8000) 143 | 144 | self.proxy_server = setup_proxy_server(**kwargs) 145 | self.proxy_server.remote( 146 | "new", 147 | self.perspective_table_name, 148 | { 149 | "task_id": "str", 150 | "user_defined_metadata": "str", 151 | "attempt_number": "int", 152 | "name": "str", 153 | "state": "str", 154 | "job_id": "str", 155 | "actor_id": "float", 156 | "type": "str", 157 | "func_or_class_name": "str", 158 | "parent_task_id": "str", 159 | "node_id": "str", 160 | "worker_id": "str", 161 | "error_type": "str", 162 | "language": "str", 163 | "placement_group_id": "float", 164 | "creation_time_ms": "datetime", 165 | "start_time_ms": "datetime", 166 | "end_time_ms": "datetime", 167 | "error_message": "str", 168 | }, 169 | ) 170 | 171 | def callback(self, tasks: Iterable[ray.ObjectRef]) -> None: 172 | """A remote function used by this actor's processor actor attribute. Will be called by a separate actor 173 | with a collection of ray object references once those ObjectReferences are not in the "RUNNING" or 174 | "PENDING" state. 175 | """ 176 | # WARNING: Do not move this import. Importing these modules elsewhere can cause 177 | # difficult to diagnose, "There is no current event loop in thread 'ray_client_server_" errors. 178 | asyncio.set_event_loop(asyncio.new_event_loop()) 179 | from ray.util.state.common import GetApiOptions, StateResource 180 | 181 | def metadata_filter(task) -> bool: 182 | return task is not None and task.state not in { 183 | "NIL", 184 | "PENDING_ARGS_AVAIL", 185 | "PENDING_NODE_ASSIGNMENT", 186 | "PENDING_OBJ_STORE_MEM_AVAIL", 187 | "PENDING_ARGS_FETCH", 188 | "SUBMITTED_TO_WORKER", 189 | "RUNNING", 190 | "RUNNING_IN_RAY_GET", 191 | "RUNNING_IN_RAY_WAIT", 192 | } 193 | 194 | all_tasks = itertools.chain(tasks, self.pending_tasks) 195 | task_metadata = [ 196 | ( 197 | task, 198 | self.client.get( 199 | resource=StateResource.TASKS, 200 | id=task.task_id().hex(), 201 | options=GetApiOptions(), 202 | ), 203 | ) 204 | for task in all_tasks 205 | ] 206 | delayed_tasks = [task for task, metadata in task_metadata if not metadata_filter(metadata)] 207 | self.pending_tasks = delayed_tasks 208 | completed_tasks = [(task, metadata) for task, metadata in task_metadata if metadata_filter(metadata)] 209 | 210 | for task, metadata in completed_tasks: 211 | self.finished_tasks[task.task_id().hex()] = metadata 212 | 213 | if self.perspective_dashboard_enabled: 214 | self.update_perspective_dashboard(completed_tasks) 215 | 216 | def update_perspective_dashboard(self, completed_tasks) -> None: 217 | """A helper function, which updates this actor's proxy_server attribute with processed data. 218 | 219 | That proxy_server serves perspective tables which anticipate the data formats we provide. 220 | 221 | Args: 222 | completed_tasks: A list of tuples of the form (ObjectReference, TaskMetadata), where the ObjectReferences are neither Running nor Pending Assignment. 223 | """ 224 | data = [ 225 | dict( 226 | task_id=metadata.task_id, 227 | attempt_number=metadata.attempt_number, 228 | name=metadata.name, 229 | state=metadata.state, 230 | job_id=metadata.job_id, 231 | actor_id=metadata.actor_id, 232 | type=metadata.type, 233 | func_or_class_name=metadata.func_or_class_name, 234 | parent_task_id=metadata.parent_task_id, 235 | node_id=metadata.node_id, 236 | worker_id=metadata.worker_id, 237 | error_type=metadata.error_type, 238 | language=metadata.language, 239 | placement_group_id=metadata.placement_group_id, 240 | creation_time_ms=metadata.creation_time_ms, 241 | start_time_ms=metadata.start_time_ms, 242 | end_time_ms=metadata.end_time_ms, 243 | error_message=metadata.error_message, 244 | user_defined_metadata=self.user_defined_metadata.get(task.task_id().hex()), 245 | ) 246 | for task, metadata in completed_tasks 247 | ] 248 | self.proxy_server.remote("update", self.perspective_table_name, data) 249 | 250 | async def process(self, obj_refs: Iterable[ray.ObjectRef], metadata: Optional[Iterable[str]] = None, chunk_size: int = 25_000) -> None: 251 | """An asynchronous function to process a collection of Ray object references. 252 | 253 | Sends sub-collections of object references of size chunk_size to its AsyncMetadataTrackerCallback actor. 254 | 255 | Args: 256 | obj_refs: A List of Ray object references. 257 | metadata: An optional list of equal size, of json-strings for each object reference. 258 | chunk_size: The maximum number of tasks to pass to its AsyncMetadataTrackerCallback at a time. 259 | """ 260 | if metadata: 261 | for obj, info in zip(obj_refs, metadata): 262 | self.user_defined_metadata[obj.task_id().hex()] = info 263 | for i in range(0, len(obj_refs), chunk_size): 264 | self.processor.process.remote(obj_refs[i : i + chunk_size]) 265 | 266 | def get_df(self) -> pl.DataFrame: 267 | """Retrieves an internally maintained dataframe of task related information pulled from the ray GCS""" 268 | self.df = pl.DataFrame( 269 | data={ 270 | # fmt: off 271 | "task_id": [task.task_id for task in self.finished_tasks.values()], 272 | "user_defined_metadata": [self.user_defined_metadata.get(task.task_id) for task in self.finished_tasks.values()], 273 | "attempt_number": [task.attempt_number for task in self.finished_tasks.values()], 274 | "name": [task.name for task in self.finished_tasks.values()], 275 | "state": [task.state for task in self.finished_tasks.values()], 276 | "job_id": [task.job_id for task in self.finished_tasks.values()], 277 | "actor_id": [task.actor_id for task in self.finished_tasks.values()], 278 | "type": [task.type for task in self.finished_tasks.values()], 279 | "func_or_class_name": [task.func_or_class_name for task in self.finished_tasks.values()], 280 | "parent_task_id": [task.parent_task_id for task in self.finished_tasks.values()], 281 | "node_id": [task.node_id for task in self.finished_tasks.values()], 282 | "worker_id": [task.worker_id for task in self.finished_tasks.values()], 283 | "error_type": [task.error_type for task in self.finished_tasks.values()], 284 | "language": [task.language for task in self.finished_tasks.values()], 285 | "required_resources": [task.required_resources for task in self.finished_tasks.values()], 286 | "runtime_env_info": [task.runtime_env_info for task in self.finished_tasks.values()], 287 | "placement_group_id": [task.placement_group_id for task in self.finished_tasks.values()], 288 | "events": [task.events for task in self.finished_tasks.values()], 289 | "profiling_data": [task.profiling_data for task in self.finished_tasks.values()], 290 | "creation_time_ms": [task.creation_time_ms for task in self.finished_tasks.values()], 291 | "start_time_ms": [task.start_time_ms for task in self.finished_tasks.values()], 292 | "end_time_ms": [task.end_time_ms for task in self.finished_tasks.values()], 293 | "task_log_info": [task.task_log_info for task in self.finished_tasks.values()], 294 | "error_message": [task.error_message for task in self.finished_tasks.values()], 295 | # fmt: on 296 | }, 297 | schema_overrides=default_schema, 298 | ) 299 | return self.df 300 | 301 | def get_proxy_server(self) -> ray.serve.handle.DeploymentHandle: 302 | """A getter for this actors proxy server attribute. Can be used to create custom perspective visuals. 303 | Returns: this actors proxy_server attribute 304 | """ 305 | if self.proxy_server: 306 | return self.proxy_server 307 | raise Exception("This task_tracker has no active proxy_server.") 308 | 309 | def save_df(self) -> None: 310 | """Saves the internally maintained dataframe of task related information from the ray GCS""" 311 | self.get_df() 312 | if self.path is not None and self.df is not None: 313 | logger.info(f"Writing DataFrame to {self.path}") 314 | self.df.write_parquet(self.path) 315 | return True 316 | return False 317 | 318 | def clear_df(self) -> None: 319 | """Clears the internally maintained dataframe of task related information from the ray GCS""" 320 | self.df = None 321 | self.finished_tasks = {} 322 | if self.perspective_dashboard_enabled: 323 | self.proxy_server.remote("clear", self.perspective_table_name, None) 324 | 325 | 326 | class RayTaskTracker: 327 | def __init__(self, name: str = "task_tracker", namespace: str = None, **kwargs): 328 | """A utility to construct AsyncMetadataTracker actors. 329 | 330 | Wraps several remote AsyncMetadataTracker functions in a ray.get() call for convenience. 331 | 332 | Args: 333 | Optional[name]: The named used to construct a AsyncMetadataTracker, also used to form the name of its AsyncMetadataTrackerCallback. 334 | Optional[namespace]: Ray namespace for the AsyncMetadataTracker and its AsyncMetadataTrackerCallback. 335 | """ 336 | if namespace is None: 337 | namespace = coolname.generate_slug(2) 338 | logger.critical(f'No namespace provided, using namespace "{namespace}"') 339 | 340 | self.name = name 341 | self.namespace = namespace 342 | self.tracker = AsyncMetadataTracker.options( 343 | lifetime="detached", 344 | name=name, 345 | namespace=namespace, 346 | get_if_exists=True, 347 | ).remote( 348 | name=name, 349 | namespace=namespace, 350 | **kwargs, 351 | ) 352 | 353 | def process(self, object_refs: Iterable[ray.ObjectRef], metadata: Optional[Iterable[str]] = None, chunk_size: int = 25_000) -> None: 354 | """A helper function, to send this object's AsyncMetadataTracker actor a collection of object references to track""" 355 | self.tracker.process.remote(object_refs, metadata=metadata, chunk_size=chunk_size) 356 | 357 | def get_df(self, process_user_metadata_column=False) -> pl.DataFrame: 358 | """Fetches this object's AsyncMetadataTracker's internal dataframe object""" 359 | df = ray.get(self.tracker.get_df.remote()) 360 | if process_user_metadata_column: 361 | user_metadata_frame = pl.from_pandas(pd.json_normalize(df["user_defined_metadata"].to_pandas())) 362 | df_with_user_metadata = pl.concat([df, user_metadata_frame], how="horizontal") 363 | return df_with_user_metadata 364 | return df 365 | 366 | def save_df(self) -> None: 367 | """Save the dataframe used by this object's AsyncMetadataTracker actor""" 368 | return ray.get(self.tracker.save_df.remote()) 369 | 370 | def clear(self) -> None: 371 | """Clear the dataframe used by this object's AsyncMetadataTracker actor""" 372 | return ray.get(self.tracker.clear_df.remote()) 373 | 374 | def create_table(self, table_name: str, table_schema: Dict[str, str]) -> None: 375 | """Create a new perspective table using the proxy server used by the RayTaskTracker's AsyncMetadataTracker actor""" 376 | proxy_server = self.proxy_server() 377 | return proxy_server.remote("new", table_name, table_schema) 378 | 379 | def update_table(self, table_name: str, data: List[Dict]) -> None: 380 | """Update rows of perspective table held by the proxy server used by the RayTaskTracker's AsyncMetadataTracker actor""" 381 | proxy_server = self.proxy_server() 382 | return proxy_server.remote("update", table_name, data) 383 | 384 | def proxy_server(self) -> ray.serve.handle.DeploymentHandle: 385 | """Fetch the proxy server used by this object's AsyncMetadataTracker actor""" 386 | return ray.get(self.tracker.get_proxy_server.remote()) 387 | 388 | def exit(self) -> None: 389 | """Perform cleanup tasks, kill associated actors, and shutdown.""" 390 | ray.kill(ray.get_actor(name=self.name, namespace=self.namespace)) 391 | ray.kill(ray.get_actor(name=get_callback_actor_name(self.name), namespace=self.namespace)) 392 | shutdown() 393 | -------------------------------------------------------------------------------- /raydar/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Point72/raydar/7137824716788cd59c783c80fff652742d027ee5/raydar/tests/__init__.py -------------------------------------------------------------------------------- /raydar/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import tempfile 4 | 5 | import pytest 6 | import ray 7 | 8 | 9 | class RayFixture(object): 10 | def __init__(self, config): 11 | self.config = config 12 | 13 | def __enter__(self): 14 | if ray.is_initialized(): 15 | ray.shutdown() 16 | print( 17 | "Launching a Ray Cluster with config:\n", 18 | json.dumps(self.config, indent=4, default=lambda i: i.__name__), 19 | ) 20 | 21 | context = ray.init(**self.config) 22 | if hasattr(context, "address_info"): 23 | os.environ["RAY_ADDRESS"] = context.address_info["address"] 24 | 25 | def __exit__(self, exc_type, exc_val, exc_tb): 26 | print("Shutting down Ray cluster!") 27 | if "RAY_ADDRESS" in os.environ: 28 | del os.environ["RAY_ADDRESS"] 29 | ray.shutdown() 30 | 31 | 32 | @pytest.fixture(scope="module") 33 | def unittest_ray_config(): 34 | # Unit tests run on Jenkins, which may require special configuration 35 | # With the wrong configuration, it could mysteriously freeze 36 | config = dict( 37 | num_cpus=5, 38 | include_dashboard=True, 39 | ) 40 | return config 41 | 42 | 43 | @pytest.fixture(scope="module") 44 | def unittest_ray_cluster(unittest_ray_config): 45 | with tempfile.TemporaryDirectory() as tmpdir: 46 | unittest_ray_config["storage"] = tmpdir 47 | with RayFixture(unittest_ray_config) as _: 48 | yield None 49 | -------------------------------------------------------------------------------- /raydar/tests/test_task_tracker.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import pytest 4 | import ray 5 | import requests 6 | 7 | from raydar import RayTaskTracker, setup_proxy_server 8 | 9 | 10 | @ray.remote 11 | def do_some_work(): 12 | time.sleep(0.1) 13 | return True 14 | 15 | 16 | @pytest.mark.usefixtures("unittest_ray_cluster") 17 | class TestRayTaskTracker: 18 | def test_construction_and_dataframe(self): 19 | task_tracker = RayTaskTracker(enable_perspective_dashboard=True) 20 | assert len(task_tracker.namespace.split("-")) == 2 21 | refs = [do_some_work.remote() for _ in range(10)] 22 | task_tracker.process(refs) 23 | time.sleep(30) 24 | df = task_tracker.get_df() 25 | assert df[["name", "state"]].row(0) == ("do_some_work", "FINISHED") 26 | 27 | def test_get_proxy_server(self): 28 | from raydar.dashboard.server import PerspectiveRayServer 29 | 30 | kwargs = dict( 31 | target=PerspectiveRayServer.bind(), 32 | name="webserver", 33 | route_prefix="/", 34 | ) 35 | server = setup_proxy_server(**kwargs) 36 | server.remote("new", "test_table", dict(a="str", b="int", c="float", d="datetime")) 37 | time.sleep(2) 38 | server.remote("update", "test_table", [dict(a="foo", b=1, c=1.0, d=time.time())]) 39 | time.sleep(2) 40 | response = requests.get("http://localhost:8000/tables") 41 | assert eval(response.text) == ["test_table"] 42 | --------------------------------------------------------------------------------