diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8bd0e39 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ + +# This is NOT the Next.js you know + +This version has breaking changes โ€” APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0914b29 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md new file mode 100644 index 0000000..d6fe758 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md @@ -0,0 +1,19 @@ +--- +name: CI/CD +about: Report issues with CI/CD pipeline or suggest improvements +title: "[CI/CD]" +labels: ci-cd +assignees: "" +--- + +**Describe the CI/CD issue** +A clear and concise description of the CI/CD problem. + +**Current pipeline behavior** +Describe what's happening in the current pipeline. + +**Expected pipeline behavior** +Describe what should happen in the pipeline. + +**Additional context** +Add any other context about the CI/CD issue here. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md new file mode 100644 index 0000000..2004556 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md @@ -0,0 +1,55 @@ +--- +name: Code of Conduct Violation +about: Report a violation of our Code of Conduct +title: "[CONDUCT]" +labels: ["CONDUCT", "community"] +assignees: "" +--- + +## Code of Conduct Violation Report + +**IMPORTANT**: This issue will be handled privately and with respect for all parties involved. + +### Type of Violation + +- [ ] Harassment or bullying +- [ ] Inappropriate language or behavior +- [ ] Privacy violation (sharing personal information) +- [ ] Spam or trolling +- [ ] Discrimination +- [ ] Other (please describe) + +### Description + + +### When did this occur? + + +### Where did this occur? + +- [ ] GitHub Issues +- [ ] GitHub Discussions +- [ ] GitHub Pull Requests +- [ ] GitHub Comments +- [ ] External communication (Discord, email, etc.) +- [ ] Other (please specify) + +### People Involved + + +### Evidence + + + +### Impact + + +### Additional Context + + +### Preferred Resolution + + +--- + +**Note**: This report will be reviewed by the team and handled according to our [Code of Conduct](https://github.com/RFS-ADRENO/zca-js/blob/main/CODE_OF_CONDUCT.md). We are committed to maintaining a safe and welcoming community for everyone. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..a8d1480 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEAT]" +labels: enhancement +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md new file mode 100644 index 0000000..e9b137f --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md @@ -0,0 +1,16 @@ +--- +name: Help Wanted request +about: Ask for help with something +title: "[HELP]" +labels: help wanted +assignees: "" +--- + +**Is your help wanted request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I need help with a problem [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the help wanted request here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/performance.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/performance.md new file mode 100644 index 0000000..0eb5360 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/performance.md @@ -0,0 +1,19 @@ +--- +name: Performance +about: Report performance issues or suggest optimizations +title: "[PERF]" +labels: performance +assignees: "" +--- + +**Describe the performance issue** +A clear and concise description of the performance problem. + +**Current behavior** +Describe the current performance metrics or behavior. + +**Expected behavior** +Describe what performance you expect to see. + +**Additional context** +Add any other context about the performance issue here. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/question.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..16ac159 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,16 @@ +--- +name: Question +about: Questions arising during use. +title: "[Question] Some question..." +labels: question +assignees: "" +--- + +**Ask the questions you want!** +A clear and concise question about what. Ex. When to use API [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to + +**Additional context** +Add any other context or screenshots about the question here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/security.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/security.md new file mode 100644 index 0000000..f14d23a --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/security.md @@ -0,0 +1,74 @@ +--- +name: Security Vulnerability Report +about: Report a security vulnerability +title: "[SECURITY]" +labels: security +assignees: "" +--- + +## Security Vulnerability Report + +**IMPORTANT**: This issue will be handled privately and with the highest priority. Please do not disclose details publicly until the issue is resolved. + +### Vulnerability Type + +- [ ] Authentication bypass +- [ ] Data exposure/information disclosure +- [ ] Code injection +- [ ] Cross-site scripting (XSS) +- [ ] Cross-site request forgery (CSRF) +- [ ] Denial of service (DoS) +- [ ] Privilege escalation +- [ ] Dependency vulnerability +- [ ] Other (please describe) + +### Severity Level + +- [ ] Critical (immediate action required) +- [ ] High (action required within 24 hours) +- [ ] Medium (action required within 1 week) +- [ ] Low (action required within 1 month) + +### Description + + +### Steps to Reproduce + +1. +2. +3. + +### Expected Behavior + + +### Actual Behavior + + +### Environment + +- **zca-js Version**: +- **Node.js Version**: +- **Operating System**: +- **Browser** (if applicable): + +### Impact Assessment + +- **Data at Risk**: +- **Users Affected**: +- **Business Impact**: + +### Suggested Fix + + +### Additional Information + + + +### Disclosure Timeline + + +--- + +**Note**: This report will be reviewed by the security team and handled according to our [Security Policy](https://github.com/RFS-ADRENO/zca-js/blob/main/SECURITY.md). We appreciate your responsible disclosure and will work to resolve this issue promptly. + +**โš ๏ธ Warning**: This is an unofficial API library. Please ensure your testing does not violate Zalo's terms of service or put any accounts at risk. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/testing.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/testing.md new file mode 100644 index 0000000..dcf7f64 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/testing.md @@ -0,0 +1,19 @@ +--- +name: Testing +about: Report testing issues or suggest test improvements +title: "[TEST]" +labels: testing +assignees: "" +--- + +**Describe the testing issue** +A clear and concise description of the testing problem. + +**Test case** +Describe the test case that's failing or needs improvement. + +**Expected test result** +Describe what the test should verify. + +**Additional context** +Add any other context about the testing issue here. \ No newline at end of file diff --git a/_libs/zca-js/.github/pull_request_template.md b/_libs/zca-js/.github/pull_request_template.md new file mode 100644 index 0000000..0fe9a9e --- /dev/null +++ b/_libs/zca-js/.github/pull_request_template.md @@ -0,0 +1,18 @@ +## What does this PR do? + + +## Reason for this PR + + +## How did you verify your code works? +- [ ] All related features have been fully tested +- [ ] Unit tests have been updated/created if needed +- [ ] No lint/format errors +- [ ] Documentation has been updated if needed +- [ ] Code changes + +## Related Issue (if any) + + +## Testing Instructions + \ No newline at end of file diff --git a/_libs/zca-js/.github/workflows/lint.yml b/_libs/zca-js/.github/workflows/lint.yml new file mode 100644 index 0000000..ce5938d --- /dev/null +++ b/_libs/zca-js/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: lint + +on: + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "18.x" + - run: npm install + - run: npm run build:clean && npm run build:esm && npm run build:cjs + - run: npm run lint diff --git a/_libs/zca-js/.gitignore b/_libs/zca-js/.gitignore new file mode 100644 index 0000000..67d4ad2 --- /dev/null +++ b/_libs/zca-js/.gitignore @@ -0,0 +1,136 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +bun.lockb +cookies.json +credentials.json +/.dev +test/**/*.test.* diff --git a/_libs/zca-js/.husky/pre-commit b/_libs/zca-js/.husky/pre-commit new file mode 100644 index 0000000..ea5a55b --- /dev/null +++ b/_libs/zca-js/.husky/pre-commit @@ -0,0 +1 @@ +bunx lint-staged diff --git a/_libs/zca-js/.npmignore b/_libs/zca-js/.npmignore new file mode 100644 index 0000000..bae4ecb --- /dev/null +++ b/_libs/zca-js/.npmignore @@ -0,0 +1,15 @@ +.dev +examples +src +test + +bun.lockb +bun.lock +package-lock.json +tsconfig.json + +.husky +.github +.vscode + +scripts diff --git a/_libs/zca-js/.prettierrc b/_libs/zca-js/.prettierrc new file mode 100644 index 0000000..86ef8dc --- /dev/null +++ b/_libs/zca-js/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 4, + "printWidth": 120, + "semi": true +} diff --git a/_libs/zca-js/.vscode/settings.json b/_libs/zca-js/.vscode/settings.json new file mode 100644 index 0000000..910a2a5 --- /dev/null +++ b/_libs/zca-js/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules\\typescript\\lib" +} diff --git a/_libs/zca-js/CODE_OF_CONDUCT.md b/_libs/zca-js/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..158f87c --- /dev/null +++ b/_libs/zca-js/CODE_OF_CONDUCT.md @@ -0,0 +1,163 @@ +# Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement through: +- **GitHub Issues**: Create a private issue with the `[CONDUCT]` label +- **GitHub Discussions**: Use the "Community" category for general concerns +- **Direct Contact**: Reach out to any team member through GitHub +- **Team Members**: + - [@RFS-ADRENO](https://github.com/RFS-ADRENO) + - [@truong9c2208](https://github.com/truong9c2208) + - [@JustKemForFun](https://github.com/JustKemForFun) + +All complaints will be reviewed and investigated promptly and fairly by the team. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. + +## Special Considerations for zca-js + +Given that zca-js is an unofficial API library for Zalo, we have additional +guidelines specific to our project: + +### Responsible Usage + +* **Respect Zalo's Terms of Service**: While this is an unofficial library, + users should be aware of and respect Zalo's terms of service +* **Account Safety**: Do not encourage or provide guidance on circumventing + Zalo's security measures +* **Privacy**: Respect user privacy and do not share personal information + obtained through the API +* **Rate Limiting**: Encourage responsible usage that doesn't overload Zalo's + servers + +### Technical Discussions + +* **Security**: When discussing security-related topics, use appropriate + channels and follow responsible disclosure practices +* **Bugs and Issues**: Report bugs and issues through proper channels +* **Documentation**: Help improve documentation and examples for the community + +--- + +**Note**: This Code of Conduct is a living document and may be updated as our +community grows and evolves. \ No newline at end of file diff --git a/_libs/zca-js/CONTRIBUTING.md b/_libs/zca-js/CONTRIBUTING.md new file mode 100644 index 0000000..03bd6ee --- /dev/null +++ b/_libs/zca-js/CONTRIBUTING.md @@ -0,0 +1,470 @@ +# Contributing to zca-js + +Thank you for your interest in contributing to zca-js! This project is maintained by the community and we welcome all contributions. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Development Setup](#development-setup) +- [How to Contribute](#how-to-contribute) +- [Pull Request Process](#pull-request-process) +- [Code Style Guidelines](#code-style-guidelines) +- [Testing Guidelines](#testing-guidelines) +- [Documentation Guidelines](#documentation-guidelines) +- [Security Guidelines](#security-guidelines) +- [Release Process](#release-process) +- [Getting Help](#getting-help) + +## Code of Conduct + +This project adheres to our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you agree to abide by these rules. + +## Getting Started + +### Prerequisites + +- Node.js >= 18.0.0 +- Bun (recommended) or npm +- Git + +### Fork and Clone + +1. Fork this repository +2. Clone your fork: + ```bash + git clone https://github.com/YOUR_USERNAME/zca-js.git + cd zca-js + ``` +3. Add upstream remote: + ```bash + git remote add upstream https://github.com/RFS-ADRENO/zca-js.git + ``` + +## Development Setup + +### Install Dependencies + +```bash +# Using Bun (recommended) +bun install + +# Or using npm +npm install +``` + +### Build Project + +```bash +# Build both ESM and CJS +bun run build + +# Build ESM only +bun run build:esm + +# Build CJS only +bun run build:cjs +``` + +### Run Tests + +```bash +# Run feature tests +bun run test:feat + +# Run specific test file +bun run test/test.ts +``` + +### Code Formatting + +```bash +# Format code with Prettier +bun run prettier +``` + +## How to Contribute + +### Types of Contributions + +We welcome the following types of contributions: + +- ๐Ÿ› **Bug Reports**: Report bugs and issues +- โœจ **Feature Requests**: Suggest new features +- ๐Ÿ”ง **Code Contributions**: Fix bugs and add features +- ๐Ÿ“š **Documentation**: Improve docs and examples +- ๐Ÿงช **Tests**: Add or improve tests +- ๐Ÿ”’ **Security**: Report security vulnerabilities +- ๐ŸŒ **Translations**: Translate docs to other languages + +### Before You Start + +1. **Check existing issues**: Search for existing issues before creating new ones +2. **Discuss major changes**: Create an issue to discuss major changes +3. **Follow the roadmap**: Check the current roadmap and priorities + +## Pull Request Process + +### Creating a Pull Request + +1. **Create a feature branch**: + ```bash + git checkout -b feature/your-feature-name + # or + git checkout -b fix/your-bug-fix + ``` + +2. **Make your changes**: + - Follow code style guidelines + - Add tests for new functionality + - Update documentation if needed + +3. **Test your changes**: + ```bash + bun run build + bun run test:feat + ``` + +4. **Commit your changes**: + ```bash + git add . + git commit -m "feat: add new API method for group management" + ``` + +5. **Push to your fork**: + ```bash + git push origin feature/your-feature-name + ``` + +6. **Create a Pull Request**: + - Use the provided PR template + - Link related issues + - Provide clear description of changes + +### PR Review Process + +1. **Automated checks** must pass +2. **Code review** by at least one maintainer +3. **Security review** for security-related changes +4. **Documentation review** for API changes +5. **Final approval** before merge + +## Code Style Guidelines + +### TypeScript Guidelines + +- Use TypeScript strict mode +- Prefer interfaces over types for object shapes +- Use meaningful variable and function names +- Add JSDoc comments for public APIs +- Use async/await over Promises when possible + +### Code Organization + +The project follows a modular structure for better maintainability and organization: + +``` +zca-js/ +โ”œโ”€โ”€ src/ # Source code directory +โ”‚ โ”œโ”€โ”€ apis/ # API methods (100+ files) +โ”‚ โ”‚ โ”œโ”€โ”€ sendMessage.ts # Core messaging functionality +โ”‚ โ”‚ โ”œโ”€โ”€ login.ts # Authentication methods +โ”‚ โ”‚ โ”œโ”€โ”€ loginQR.ts # QR code login +โ”‚ โ”‚ โ”œโ”€โ”€ listen.ts # Event listening +โ”‚ โ”‚ โ”œโ”€โ”€ sendVideo.ts # Video sending +โ”‚ โ”‚ โ”œโ”€โ”€ sendVoice.ts # Voice sending +โ”‚ โ”‚ โ”œโ”€โ”€ sendSticker.ts # Sticker sending +โ”‚ โ”‚ โ”œโ”€โ”€ createGroup.ts # Group management +โ”‚ โ”‚ โ”œโ”€โ”€ addReaction.ts # Message reactions +โ”‚ โ”‚ โ”œโ”€โ”€ uploadAttachment.ts # File uploads +โ”‚ โ”‚ โ””โ”€โ”€ ... # 90+ other API methods +โ”‚ โ”œโ”€โ”€ models/ # TypeScript interfaces and types +โ”‚ โ”‚ โ”œโ”€โ”€ Message.ts # Message interface +โ”‚ โ”‚ โ”œโ”€โ”€ Attachment.ts # File attachment types +โ”‚ โ”‚ โ”œโ”€โ”€ Reaction.ts # Reaction types +โ”‚ โ”‚ โ”œโ”€โ”€ FriendEvent.ts # Friend event types +โ”‚ โ”‚ โ”œโ”€โ”€ GroupEvent.ts # Group event types +โ”‚ โ”‚ โ”œโ”€โ”€ Typing.ts # Typing indicators +โ”‚ โ”‚ โ”œโ”€โ”€ SeenMessage.ts # Message seen events +โ”‚ โ”‚ โ”œโ”€โ”€ DeliveredMessage.ts # Message delivery events +โ”‚ โ”‚ โ”œโ”€โ”€ Undo.ts # Undo functionality +โ”‚ โ”‚ โ”œโ”€โ”€ Enum.ts # Enumerations +โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Model exports +โ”‚ โ”œโ”€โ”€ Errors/ # Error handling +โ”‚ โ”‚ โ”œโ”€โ”€ ZaloApiError.ts # Custom API error class +โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Error exports +โ”‚ โ”œโ”€โ”€ context.ts # Context management and state +โ”‚ โ”œโ”€โ”€ utils.ts # Utility functions and helpers +โ”‚ โ”œโ”€โ”€ zalo.ts # Main Zalo class implementation +โ”‚ โ”œโ”€โ”€ update.ts # Update checking functionality +โ”‚ โ””โ”€โ”€ index.ts # Public API exports +โ”œโ”€โ”€ examples/ # Usage examples +โ”‚ โ””โ”€โ”€ echobot.ts # Echo bot example +โ”œโ”€โ”€ test/ # Test files +โ”‚ โ”œโ”€โ”€ feat.ts # Feature tests +โ”‚ โ”œโ”€โ”€ feat.test.ts # Feature test suite +โ”‚ โ”œโ”€โ”€ test.ts # General tests +โ”‚ โ””โ”€โ”€ a.png # Test assets +โ”œโ”€โ”€ .github/ # GitHub configuration +โ”‚ โ””โ”€โ”€ ISSUE_TEMPLATE/ # Issue templates +โ”œโ”€โ”€ .dev/ # Development tools (developer generated) +โ”œโ”€โ”€ dist/ # Build output (generated) +โ”œโ”€โ”€ node_modules/ # Dependencies (generated) +โ”œโ”€โ”€ package.json # Project configuration +โ”œโ”€โ”€ tsconfig.json # TypeScript configuration +โ”œโ”€โ”€ rollup.config.js # Build configuration +โ”œโ”€โ”€ README.md # Project documentation +โ”œโ”€โ”€ CONTRIBUTING.md # Contribution guidelines +โ”œโ”€โ”€ SECURITY.md # Security policy +โ”œโ”€โ”€ CODE_OF_CONDUCT.md # Community guidelines +โ””โ”€โ”€ LICENSE # MIT License +``` + +### Key Directories Explained + +- **`src/apis/`**: Contains all API method implementations (~100 files) + +- **`src/models/`**: TypeScript interfaces and type definitions + - Core data structures for messages, events, and API responses + - Ensures type safety across the application + +- **`src/Errors/`**: Custom error handling + - `ZaloApiError.ts`: Handles API-specific errors + - Provides consistent error handling across the library + +- **`examples/`**: Usage examples and demonstrations + - `echobot.ts`: Complete example of a Zalo bot implementation + +- **`test/`**: Test suites and test assets + - Feature tests for core functionality + - Integration tests for API methods + +### Naming Conventions + +- **Files**: camelCase (e.g., `sendMessage.ts`) +- **Classes**: PascalCase (e.g., `ZaloApiError`) +- **Functions**: camelCase (e.g., `sendMessage`) +- **Constants**: UPPER_SNAKE_CASE (e.g., `API_BASE_URL`) +- **Interfaces**: PascalCase with `I` prefix (e.g., `IMessage`) + +### Error Handling + +```typescript +// Good +try { + const result = await api.sendMessage(message); + return result; +} catch (error) { + if (error instanceof ZaloApiError) { + throw error; + } + throw new ZaloApiError(`Failed to send message: ${error.message}`); +} + +// Bad +try { + const result = await api.sendMessage(message); + return result; +} catch (error) { + console.error(error); + return null; +} +``` + +## Testing Guidelines + +### Test Structure + +```typescript +describe('API Method', () => { + beforeEach(() => { + // Setup + }); + + afterEach(() => { + // Cleanup + }); + + it('should handle success case', async () => { + // Test implementation + }); + + it('should handle error case', async () => { + // Test error handling + }); +}); +``` + +### Testing Best Practices + +- Test both success and failure scenarios +- Mock external dependencies +- Use descriptive test names +- Keep tests independent +- Test edge cases and error conditions + +### Running Tests + +```bash +# Run all tests +bun run test:feat + +# Run specific test +bun run test/test.ts + +# Run with coverage (if available) +bun run test:coverage +``` + +## Documentation Guidelines + +### API Documentation + +- Document all public methods with JSDoc +- Include parameter types and descriptions +- Provide usage examples +- Document error conditions + +```typescript +/** + * Sends a message to a specific thread + * @param message - The message object containing content and metadata + * @param threadId - The ID of the thread to send the message to + * @param threadType - The type of thread (User or Group) + * @returns Promise - The sent message object + * @throws {ZaloApiError} When the API request fails + * @example + * ```typescript + * const message = await api.sendMessage( + * { msg: "Hello, world!" }, + * "123456789", + * ThreadType.User + * ); + * ``` + */ +async sendMessage(message: IMessage, threadId: string, threadType: ThreadType): Promise +``` + +### README Updates + +- Update README.md for new features +- Add examples for new APIs +- Update installation instructions if needed +- Keep the table of contents updated + +## Security Guidelines + +### Security Best Practices + +- Never commit sensitive data (tokens, passwords, etc.) +- Use environment variables for configuration +- Validate all user inputs +- Follow the principle of least privilege +- Report security issues privately + +### Security Reporting + +If you discover a security vulnerability: + +1. **DO NOT** create a public issue +2. Use the [SECURITY.md](SECURITY.md) reporting process +3. Create a private issue with `[SECURITY]` label +4. Contact team members directly for urgent issues + +### Code Security + +```typescript +// Good - Validate inputs +function sendMessage(content: string, threadId: string) { + if (!content || typeof content !== 'string') { + throw new ZaloApiError('Content must be a non-empty string'); + } + if (!threadId || typeof threadId !== 'string') { + throw new ZaloApiError('ThreadId must be a non-empty string'); + } + // Implementation +} + +// Bad - No validation +function sendMessage(content: any, threadId: any) { + // Implementation without validation +} +``` + +## Release Process + +### Versioning + +We follow [Semantic Versioning](https://semver.org/): + +- **MAJOR**: Breaking changes +- **MINOR**: New features (backward compatible) +- **PATCH**: Bug fixes (backward compatible) + +### Release Checklist + +- [ ] All tests pass +- [ ] Documentation is updated +- [ ] Version is bumped in package.json +- [ ] Build is successful +- [ ] Release notes are prepared + +### Publishing + +```bash +# Build the project +bun run build + +# Run tests +bun run test:feat + +# Publish to npm +npm publish +``` + +## Getting Help + +### Communication Channels + +- **GitHub Issues**: For bug reports and feature requests +- **GitHub Discussions**: For questions and general discussion +- **Pull Requests**: For code contributions +- **Security Issues**: Use `[SECURITY]` label + +### Team Members + +- [@RFS-ADRENO](https://github.com/RFS-ADRENO) +- [@truong9c2208](https://github.com/truong9c2208) +- [@JustKemForFun](https://github.com/JustKemForFun) + +### Resources + +- [API Documentation](https://zca-js.tdung.com) +- [Examples](examples/) +- [Security Policy](SECURITY.md) +- [Code of Conduct](CODE_OF_CONDUCT.md) + +## Special Considerations for zca-js + +### Unofficial API Library + +> [!IMPORTANT] +> โš ๏ธ zca-js is an unofficial API library for Zalo. Please be aware of: + +- **Account Risk**: Using this API may result in account suspension +- **Terms of Service**: Respect Zalo's ToS in your contributions +- **Rate Limiting**: Be mindful of API usage limits +- **Privacy**: Protect user privacy and data + +### Responsible Development + +- Test changes thoroughly before submitting +- Avoid introducing features that could harm users +- Consider the impact on Zalo's infrastructure +- Document any risks or limitations + +--- + +**Thank you for contributing to zca-js!** ๐Ÿš€ + +Your contributions help make this library better for the entire community. diff --git a/_libs/zca-js/LICENSE b/_libs/zca-js/LICENSE new file mode 100644 index 0000000..75a1138 --- /dev/null +++ b/_libs/zca-js/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 - 2025 RFS-ADRENO, truong9c2208, JustKemForFun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/_libs/zca-js/README.md b/_libs/zca-js/README.md new file mode 100644 index 0000000..5cabdd3 --- /dev/null +++ b/_libs/zca-js/README.md @@ -0,0 +1,225 @@ +# ZCA-JS + +> [!NOTE] +> This is an unofficial Zalo API for personal account. It work by simulating the browser to interact with Zalo Web. + +> [!WARNING] +> Using this API could get your account locked or banned. We are not responsible for any issues that may happen. Use it at your own risk. + +--- + +## Table of Contents + +- [Installation](#installation) + - [Migrate to V2](#migrate-to-v2) +- [Documentation](#documentation) +- [Basic Usages](#basic-usages) + - [Login](#login) + - [Listen for new messages](#listen-for-new-messages) + - [Send a message](#send-a-message) + - [Get/Send a sticker](#getsend-a-sticker) +- [Example](#example) +- [Projects & Useful Resources](#projects--useful-resources) +- [Contributing](#contributing) +- [License](#license) +- [Support Us](#support-us) + +## Installation + +```bash +bun add zca-js # or npm install zca-js +``` + +### Migrate to V2 + +Since official version 2.0.0, `zca-js` has removed sharp dependency for image metadata extraction. It now requires users to provide their own `imageMetadataGetter` function when initializing the `Zalo` class if they want to send images/gifs by file path. + +Example of custom `imageMetadataGetter` using `sharp`: + +```bash +bun add sharp # or npm install sharp +``` + +```javascript +import { Zalo } from "zca-js"; +import sharp from "sharp"; +import fs from "node:fs"; + +async function imageMetadataGetter(filePath) { + const data = await fs.promises.readFile(filePath); + const metadata = await sharp(data).metadata(); + return { + height: metadata.height, + width: metadata.width, + size: metadata.size || data.length, + }; +} + +const zalo = new Zalo({ + imageMetadataGetter, +}); +``` + +--- + +## Documentation + +See [API Documentation](https://zca-js.tdung.com) for more details. + +--- + +## Basic Usages + +### Login + +```javascript +import { Zalo } from "zca-js"; + +const zalo = new Zalo(); +const api = await zalo.loginQR(); +``` + +### Listen for new messages + +```javascript +import { Zalo, ThreadType } from "zca-js"; + +const zalo = new Zalo(); +const api = await zalo.loginQR(); + +api.listener.on("message", (message) => { + const isPlainText = typeof message.data.content === "string"; + + switch (message.type) { + case ThreadType.User: { + if (isPlainText) { + // received plain text direct message + } + break; + } + case ThreadType.Group: { + if (isPlainText) { + // received plain text group message + } + break; + } + } +}); + +api.listener.start(); +``` + +> [!IMPORTANT] +> Only one web listener can run per account at a time. If you open Zalo in the browser while the listener is active, the listener will be automatically stopped. + +### Send a message + +```javascript +import { Zalo, ThreadType } from "zca-js"; + +const zalo = new Zalo(); +const api = await zalo.loginQR(); + +// Echo bot +api.listener.on("message", (message) => { + const isPlainText = typeof message.data.content === "string"; + if (message.isSelf || !isPlainText) return; + + switch (message.type) { + case ThreadType.User: { + api.sendMessage( + { + msg: "echo: " + message.data.content, + quote: message.data, // the message to reply to (optional) + }, + message.threadId, + message.type, // ThreadType.User + ); + break; + } + case ThreadType.Group: { + api.sendMessage( + { + msg: "echo: " + message.data.content, + quote: message.data, // the message to reply to (optional) + }, + message.threadId, + message.type, // ThreadType.Group + ); + break; + } + } +}); + +api.listener.start(); +``` + +### Get/Send a sticker + +```javascript +api.getStickers("hello").then(async (stickerIds) => { + // Get the first sticker + const stickerObject = await api.getStickersDetail(stickerIds[0]); + api.sendMessageSticker( + stickerObject, + message.threadId, + message.type, // ThreadType.User or ThreadType.Group + ); +}); +``` + +--- + +## Example + +See [examples](examples) folder for more details. + +--- + +## Projects & Useful Resources + +
+ +| Repository | Description | +| :--- | :--- | +| [**ZaloDataExtractor**](https://github.com/JustKemForFun/ZaloDataExtractor) | A browser `Extension` to extract IMEI, cookies, and user agent from Zalo Web. | +| [**MultiZlogin**](https://github.com/ChickenAI/multizlogin) | A multi-account Zalo management system that lets you log in to and manage multiple accounts simultaneously, with proxy and webhook integration. | +| [**n8n-nodes-zalo-tools**](https://github.com/ChickenAI/zalo-node) | N8N node for personal Zalo account. | +| [**Zalo-F12**](https://github.com/ElectroHeavenVN/Zalo-F12) | A collection of JavaScript code snippets to paste into DevTools to change how Zalo Web/PC works. | +| [**Zalo-F12-Tools**](https://github.com/JustKemForFun/Zalo-F12-Tools) | Toggle hidden modes for Zalo Web. | + +
+ +--- + +## Contributing + +We welcome contributions from the community! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to: + +- ๐Ÿ› Report bugs and issues +- โœจ Suggest new features +- ๐Ÿ”ง Submit code contributions +- ๐Ÿ“š Improve documentation +- ๐Ÿงช Add or improve tests +- ๐Ÿ”’ Report security vulnerabilities + +For more information, please read our [Code of Conduct](CODE_OF_CONDUCT.md) and [Security Policy](SECURITY.md) before participating. + +--- + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- + +## **Support Us** + +- โญ **Star our repositories** if you find them useful! +- ๐Ÿ”„ **Share** with your network to help us grow +- ๐Ÿ’ก **Contribute** your ideas and code +- โ˜• **A coffee**: + - [Buy Me a Coffee](https://ko-fi.com/grosse) + - [Paypal](https://www.paypal.com/paypalme/dungto213) + - [VietQR](https://github.com/user-attachments/assets/e1f319d6-9d11-4082-8248-55b55e645caa) + - [Momo](https://me.momo.vn/gMIMulsaUqsbf6iAiXt3) diff --git a/_libs/zca-js/SECURITY.md b/_libs/zca-js/SECURITY.md new file mode 100644 index 0000000..6180eda --- /dev/null +++ b/_libs/zca-js/SECURITY.md @@ -0,0 +1,83 @@ +# Security Policy + +## Supported Versions + +We actively maintain and provide security updates for the following versions: + +| Version | Supported | +| ------- | ------------------ | +| 2.0.x | :white_check_mark: | +| 1.x.x | :x: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +We take security vulnerabilities seriously. If you discover a security vulnerability in zca-js, please follow these steps: + +### 1. **DO NOT** create a public GitHub issue +Security vulnerabilities should be reported privately to prevent potential exploitation. + +### 2. Report the vulnerability +Send an email to the maintainers with the following information: +- **Subject**: `[SECURITY] zca-js vulnerability report` +- **Description**: Detailed description of the vulnerability +- **Steps to reproduce**: Clear steps to reproduce the issue +- **Impact assessment**: Potential impact of the vulnerability +- **Suggested fix** (if available): Any suggestions for fixing the issue + +### 3. Response timeline +- **Initial response**: Within 48 hours +- **Status update**: Within 1 week +- **Resolution**: As soon as possible, typically within 30 days + +## Security Considerations + +### Important Warnings + +โš ๏ธ **This is an unofficial API library** that simulates browser interactions with Zalo Web. Please be aware of the following security considerations: + +1. **Account Risk**: Using this API may result in your Zalo account being locked or banned. Use at your own risk. + +2. **Authentication**: The library handles sensitive authentication data. Ensure proper security measures when storing or transmitting this information. + +3. **Rate Limiting**: Respect Zalo's rate limits to avoid triggering security measures. + +4. **Data Privacy**: Be mindful of user privacy and comply with relevant data protection regulations. + +### Best Practices + +1. **Environment Variables**: Store sensitive configuration in environment variables, not in code +2. **HTTPS Only**: Always use HTTPS when transmitting data +3. **Input Validation**: Validate all inputs before processing +4. **Error Handling**: Implement proper error handling to avoid information disclosure +5. **Regular Updates**: Keep the library updated to the latest version + +### Known Limitations + +- This library is not officially supported by Zalo +- API endpoints and behavior may change without notice +- No guarantee of service availability or stability +- May break with Zalo Web updates + +## Security Updates + +Security updates will be released as patch versions (e.g., 2.0.1, 2.0.2) and will be clearly marked in the changelog. + +## Contact Information + +For security-related issues, please contact: +- **GitHub Issues**: Create a private issue with the `[SECURITY]` label +- **GitHub Discussions**: Use the "Security" category for general security questions +- **Team Members**: + - [@RFS-ADRENO](https://github.com/RFS-ADRENO) + - [@truong9c2208](https://github.com/truong9c2208) + - [@JustKemForFun](https://github.com/JustKemForFun) +- **Alternative**: Contact any team member through GitHub for urgent security matters + +## Acknowledgments + +We appreciate security researchers and community members who responsibly disclose vulnerabilities. Contributors will be acknowledged in our security advisories unless they prefer to remain anonymous. + +--- + +**Note**: This security policy is subject to change. Please check back regularly for updates. \ No newline at end of file diff --git a/_libs/zca-js/bun.lock b/_libs/zca-js/bun.lock new file mode 100644 index 0000000..b1c9140 --- /dev/null +++ b/_libs/zca-js/bun.lock @@ -0,0 +1,546 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "zca-js", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0", + }, + "devDependencies": { + "@eslint/js": "^9.34.0", + "@rollup/plugin-typescript": "^12.1.0", + "@types/bun": "^1.1.14", + "@types/crypto-js": "^4.2.2", + "@types/json-bigint": "^1.0.4", + "@types/node": "^20.14.10", + "@types/pako": "^2.0.3", + "@types/semver": "^7.5.8", + "@types/spark-md5": "^3.0.4", + "@types/ws": "^8.5.11", + "eslint": "^9.34.0", + "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup": "^4.24.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.41.0", + }, + }, + }, + "packages": { + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="], + + "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "@eslint/js": ["@eslint/js@9.34.0", "", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@rollup/plugin-typescript": ["@rollup/plugin-typescript@12.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.14.0||^3.0.0||^4.0.0", "tslib": "*", "typescript": ">=3.7.0" } }, "sha512-Kzs8KGJofe7cfTRODsnG1jNGxSvU8gVoNNd7Z/QaY25AYwe2LSSUpx/kPxqF38NYkpR8de3m51r9uwJpDlz6dg=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.1.2", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^2.3.1" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.24.0", "", { "os": "android", "cpu": "arm" }, "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.24.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.24.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.24.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.24.0", "", { "os": "linux", "cpu": "arm" }, "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.24.0", "", { "os": "linux", "cpu": "arm" }, "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw=="], + + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.24.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.24.0", "", { "os": "linux", "cpu": "none" }, "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.24.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.24.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.24.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.24.0", "", { "os": "win32", "cpu": "x64" }, "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw=="], + + "@types/bun": ["@types/bun@1.1.14", "", { "dependencies": { "bun-types": "1.1.37" } }, "sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA=="], + + "@types/crypto-js": ["@types/crypto-js@4.2.2", "", {}, "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ=="], + + "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], + + "@types/json-bigint": ["@types/json-bigint@1.0.4", "", {}, "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@20.14.10", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ=="], + + "@types/pako": ["@types/pako@2.0.3", "", {}, "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q=="], + + "@types/semver": ["@types/semver@7.5.8", "", {}, "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="], + + "@types/spark-md5": ["@types/spark-md5@3.0.4", "", {}, "sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q=="], + + "@types/ws": ["@types/ws@8.5.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.41.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/type-utils": "8.41.0", "@typescript-eslint/utils": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.41.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.41.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.41.0", "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0" } }, "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.41.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.41.0", "", {}, "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.41.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.41.0", "@typescript-eslint/tsconfig-utils": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.41.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], + + "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "bun-types": ["bun-types@1.1.37", "", { "dependencies": { "@types/node": "~20.12.8", "@types/ws": "~8.5.10" } }, "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "crypto-js": ["crypto-js@4.2.0", "", {}, "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.34.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "is-core-module": ["is-core-module@2.15.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lint-staged": ["lint-staged@16.1.5", "", { "dependencies": { "chalk": "^5.5.0", "commander": "^14.0.0", "debug": "^4.4.1", "lilconfig": "^3.1.3", "listr2": "^9.0.1", "micromatch": "^4.0.8", "nano-spawn": "^1.0.2", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A=="], + + "listr2": ["listr2@9.0.2", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-VVd7cS6W+vLJu2wmq4QmfVj14Iep7cz4r/OWNk36Aq5ZOY7G8/BfCrQFexcwB1OIxB3yERiePfE/REBjEFulag=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nano-spawn": ["nano-spawn@1.0.2", "", {}, "sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.3.3", "", { "bin": "bin/prettier.cjs" }, "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": "dist/esm/bin.mjs" }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], + + "rollup": ["rollup@4.24.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.24.0", "@rollup/rollup-android-arm64": "4.24.0", "@rollup/rollup-darwin-arm64": "4.24.0", "@rollup/rollup-darwin-x64": "4.24.0", "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", "@rollup/rollup-linux-arm-musleabihf": "4.24.0", "@rollup/rollup-linux-arm64-gnu": "4.24.0", "@rollup/rollup-linux-arm64-musl": "4.24.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", "@rollup/rollup-linux-riscv64-gnu": "4.24.0", "@rollup/rollup-linux-s390x-gnu": "4.24.0", "@rollup/rollup-linux-x64-gnu": "4.24.0", "@rollup/rollup-linux-x64-musl": "4.24.0", "@rollup/rollup-win32-arm64-msvc": "4.24.0", "@rollup/rollup-win32-ia32-msvc": "4.24.0", "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "semver": ["semver@7.6.3", "", { "bin": "bin/semver.js" }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "spark-md5": ["spark-md5@3.0.2", "", {}, "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tldts": ["tldts@6.1.64", "", { "dependencies": { "tldts-core": "^6.1.64" }, "bin": "bin/cli.js" }, "sha512-ph4AE5BXWIOsSy9stpoeo7bYe/Cy7VfpciIH4RhVZUPItCJmhqWCN0EVzxd8BOHiyNb42vuJc6NWTjJkg91Tuw=="], + + "tldts-core": ["tldts-core@6.1.64", "", {}, "sha512-uqnl8vGV16KsyflHOzqrYjjArjfXaU6rMPXYy2/ZWoRKCkXtghgB4VwTDXUG+t0OTGeSewNAG31/x1gCTfLt+Q=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "tough-cookie": ["tough-cookie@5.0.0", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "tslib": ["tslib@2.6.3", "", {}, "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.5.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ=="], + + "typescript-eslint": ["typescript-eslint@8.41.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.41.0", "@typescript-eslint/parser": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw=="], + + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "lint-staged/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "^1.0.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/_libs/zca-js/eslint.config.js b/_libs/zca-js/eslint.config.js new file mode 100644 index 0000000..491ef6a --- /dev/null +++ b/_libs/zca-js/eslint.config.js @@ -0,0 +1,21 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import { defineConfig, globalIgnores } from "eslint/config"; + +export default defineConfig([ + { + files: ["**/*.{js,ts}"], + plugins: { js }, + extends: ["js/recommended"], + languageOptions: { globals: globals.node }, + }, + tseslint.configs.recommended, + { + rules: { + "@typescript-eslint/consistent-type-imports": "error", + "no-extra-boolean-cast": "off", + }, + }, + globalIgnores(["dist/*"]), +]); diff --git a/_libs/zca-js/examples/echobot.ts b/_libs/zca-js/examples/echobot.ts new file mode 100644 index 0000000..4620b4a --- /dev/null +++ b/_libs/zca-js/examples/echobot.ts @@ -0,0 +1,56 @@ +import fs from "node:fs"; +import type { API, Credentials} from "../src/index.js"; +import { ThreadType, Zalo } from "../src/index.js"; + +const zalo = new Zalo(); + +const credentials: Partial = { + cookie: JSON.parse(fs.readFileSync("./examples/cookies.json", "utf-8")), + imei: "", + userAgent: "", // Same userAgent is recommended +}; + +const isValid = (credentials: Partial): credentials is Credentials => { + return !!credentials.cookie && !!credentials.imei && !!credentials.userAgent; +}; + +let api: API | undefined; + +if (isValid(credentials)) { + api = await zalo.login(credentials); +} else { + api = await zalo.loginQR({ qrPath: "./qr.png", userAgent: "" }, () => { + console.log("QR Code has been generated"); + }); + const context = api.getContext(); + + // Save credentials for later use + credentials.imei = context.imei; + credentials.userAgent = context.userAgent; + credentials.cookie = context.cookie.toJSON()?.cookies; +} + +const { listener } = api; + +listener.on("message", (message) => { + const isDirectMessage = message.type === ThreadType.User; + + if (isDirectMessage && !message.isSelf && typeof message.data.content === "string") { + console.log("Message:", message.data.content, message.threadId); + api.sendMessage(message.data.content, message.threadId, message.type); + } +}); + +listener.onConnected(() => { + console.log("Connected"); +}); + +listener.onClosed(() => { + console.log("Closed"); +}); + +listener.onError((error) => { + console.error("Error:", error); +}); + +listener.start(); diff --git a/_libs/zca-js/examples/login.ts b/_libs/zca-js/examples/login.ts new file mode 100644 index 0000000..4c5b4b8 --- /dev/null +++ b/_libs/zca-js/examples/login.ts @@ -0,0 +1,40 @@ +import type { Credentials } from "../src"; + +import { Zalo } from "../src"; +import fs from "node:fs"; + +const zalo = new Zalo(); +const credentialsPath = "./examples/credentials.json"; + +function validateCredentials(credentials: Partial): credentials is Credentials { + return !!credentials.cookie && !!credentials.imei && !!credentials.userAgent; +} + +function getCredentials() { + if (!fs.existsSync(credentialsPath)) { + return null; + } + + const raw = fs.readFileSync(credentialsPath, "utf-8") || "{}"; + const credentials = JSON.parse(raw); + return validateCredentials(credentials) ? credentials : null; +} + +const credentials = getCredentials(); + +const isValidCredentials = credentials !== null; +const api = isValidCredentials ? await zalo.login(credentials) : await zalo.loginQR(); + +if (!isValidCredentials) { + const apiContext = api.getContext(); + const credentials: Credentials = { + cookie: apiContext.cookie.toJSON()?.cookies || [], + imei: apiContext.imei, + userAgent: apiContext.userAgent, + }; + + fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), "utf-8"); +} + +const myProfile = await api.fetchAccountInfo(); +console.log("My profile:", myProfile); diff --git a/_libs/zca-js/index.d.ts b/_libs/zca-js/index.d.ts new file mode 100644 index 0000000..964ed8c --- /dev/null +++ b/_libs/zca-js/index.d.ts @@ -0,0 +1,2 @@ +export * from "./dist"; +export as namespace Zalo; diff --git a/_libs/zca-js/package-lock.json b/_libs/zca-js/package-lock.json new file mode 100644 index 0000000..9f029a6 --- /dev/null +++ b/_libs/zca-js/package-lock.json @@ -0,0 +1,3505 @@ +{ + "name": "zca-js", + "version": "2.1.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zca-js", + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@eslint/js": "^9.34.0", + "@rollup/plugin-typescript": "^12.1.0", + "@types/bun": "^1.1.14", + "@types/crypto-js": "^4.2.2", + "@types/json-bigint": "^1.0.4", + "@types/node": "^20.14.10", + "@types/pako": "^2.0.3", + "@types/semver": "^7.5.8", + "@types/spark-md5": "^3.0.4", + "@types/ws": "^8.5.11", + "eslint": "^9.34.0", + "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup": "^4.24.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.41.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.0.tgz", + "integrity": "sha512-Kzs8KGJofe7cfTRODsnG1jNGxSvU8gVoNNd7Z/QaY25AYwe2LSSUpx/kPxqF38NYkpR8de3m51r9uwJpDlz6dg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/bun": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.14.tgz", + "integrity": "sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.1.37" + } + }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz", + "integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pako": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz", + "integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/spark-md5": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/spark-md5/-/spark-md5-3.0.4.tgz", + "integrity": "sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", + "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/type-utils": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.41.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", + "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", + "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.41.0", + "@typescript-eslint/types": "^8.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", + "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", + "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", + "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", + "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", + "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.41.0", + "@typescript-eslint/tsconfig-utils": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", + "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bun-types": { + "version": "1.1.37", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.37.tgz", + "integrity": "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "~20.12.8", + "@types/ws": "~8.5.10" + } + }, + "node_modules/bun-types/node_modules/@types/node": { + "version": "20.12.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz", + "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", + "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.5.tgz", + "integrity": "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.5.0", + "commander": "^14.0.0", + "debug": "^4.4.1", + "lilconfig": "^3.1.3", + "listr2": "^9.0.1", + "micromatch": "^4.0.8", + "nano-spawn": "^1.0.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.2.tgz", + "integrity": "sha512-VVd7cS6W+vLJu2wmq4QmfVj14Iep7cz4r/OWNk36Aq5ZOY7G8/BfCrQFexcwB1OIxB3yERiePfE/REBjEFulag==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nano-spawn": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz", + "integrity": "sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tldts": { + "version": "6.1.64", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.64.tgz", + "integrity": "sha512-ph4AE5BXWIOsSy9stpoeo7bYe/Cy7VfpciIH4RhVZUPItCJmhqWCN0EVzxd8BOHiyNb42vuJc6NWTjJkg91Tuw==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.64" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.64", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.64.tgz", + "integrity": "sha512-uqnl8vGV16KsyflHOzqrYjjArjfXaU6rMPXYy2/ZWoRKCkXtghgB4VwTDXUG+t0OTGeSewNAG31/x1gCTfLt+Q==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", + "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.41.0", + "@typescript-eslint/parser": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/_libs/zca-js/package.json b/_libs/zca-js/package.json new file mode 100644 index 0000000..ededf22 --- /dev/null +++ b/_libs/zca-js/package.json @@ -0,0 +1,78 @@ +{ + "name": "zca-js", + "version": "2.1.2", + "description": "Unofficial Zalo API for JavaScript", + "main": "dist/index.js", + "type": "module", + "exports": { + ".": { + "require": "./dist/cjs/index.cjs", + "types": "./index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "prebuild": "bun run scripts/buildAPI.js", + "build:clean": "rimraf dist", + "build:esm": "tsc", + "build:cjs": "rollup -c rollup.config.js", + "build": "bun run build:clean && bun run build:esm && bun run build:cjs", + "test:feat": "bun run test/feat.ts", + "prettier": "prettier --write .", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "prepare": "husky" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/RFS-ADRENO/zca-js.git" + }, + "homepage": "https://github.com/RFS-ADRENO/zca-js", + "bugs": { + "url": "https://github.com/RFS-ADRENO/zca-js/issues" + }, + "engines": { + "node": ">=18.0.0" + }, + "keywords": [ + "chatbot", + "zalo", + "api" + ], + "author": "RFS-ADRENO, truong9c2208, JustKemForFun", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@eslint/js": "^9.34.0", + "@rollup/plugin-typescript": "^12.1.0", + "@types/bun": "^1.1.14", + "@types/crypto-js": "^4.2.2", + "@types/json-bigint": "^1.0.4", + "@types/node": "^20.14.10", + "@types/pako": "^2.0.3", + "@types/semver": "^7.5.8", + "@types/spark-md5": "^3.0.4", + "@types/ws": "^8.5.11", + "eslint": "^9.34.0", + "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup": "^4.24.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.41.0" + }, + "lint-staged": { + "*.{js,ts}": "bunx eslint" + } +} diff --git a/_libs/zca-js/rollup.config.js b/_libs/zca-js/rollup.config.js new file mode 100644 index 0000000..4a3e3d0 --- /dev/null +++ b/_libs/zca-js/rollup.config.js @@ -0,0 +1,28 @@ +// rollup.config.js + +export default { + input: "dist/index.js", + output: { + dir: "dist/cjs", + entryFileNames: "[name].cjs", + format: "cjs", + preserveModules: true, + }, + external: [ + "crypto-js", + "events", + "form-data", + "fs", + "node:crypto", + "node:fs", + "node:fs/promises", + "node:path", + "pako", + "semver", + "sharp", + "spark-md5", + "tough-cookie", + "ws", + "json-bigint" + ], +}; diff --git a/_libs/zca-js/scripts/buildAPI.js b/_libs/zca-js/scripts/buildAPI.js new file mode 100644 index 0000000..40f7f31 --- /dev/null +++ b/_libs/zca-js/scripts/buildAPI.js @@ -0,0 +1,72 @@ +import fs from "fs"; +import path from "path"; + +const apisDir = path.join(process.cwd(), "src", "apis"); +const outputFile = path.join(process.cwd(), "src", "apis.ts"); + +function getAllApiFiles(dir) { + const ignoreFiles = ["listen.ts", "login.ts", "loginQR.ts", "custom.ts"]; + return fs + .readdirSync(dir) + .filter((file) => file.endsWith(".ts") && !ignoreFiles.includes(file)) + .map((file) => { + const nonExtension = file.slice(0, -3); // Remove .ts extension + + return { + name: nonExtension, + factoryName: `${nonExtension}Factory`, + }; + }); +} + +function generateAPIsFile() { + const allApiFiles = getAllApiFiles(apisDir); + + const importLines = allApiFiles.map((file) => { + return `import { ${file.factoryName} } from "./apis/${file.name}.js";`; + }); + + const propertyLines = allApiFiles.map((file) => { + return ` public ${file.name}: ReturnType;`; + }); + + const constructorLines = allApiFiles.map((file) => { + return ` this.${file.name} = ${file.factoryName}(ctx, this);`; + }); + + const emptyNewLine = "\n"; + + const content = + "" + + 'import { Listener } from "./apis/listen.js";\n' + + emptyNewLine + + importLines.join("\n") + + emptyNewLine + + 'import { customFactory } from "./apis/custom.js";\n' + + 'import type { ZPWServiceMap, ContextSession } from "./context.js";\n' + + emptyNewLine + + "export class API {\n" + + " public zpwServiceMap: ZPWServiceMap;\n" + + " public listener: Listener;\n" + + emptyNewLine + + propertyLines.join("\n") + + emptyNewLine + + emptyNewLine + + " public custom: ReturnType;\n" + + emptyNewLine + + " constructor(ctx: ContextSession, zpwServiceMap: ZPWServiceMap, wsUrls: string[]) {\n" + + " this.zpwServiceMap = zpwServiceMap;\n" + + " this.listener = new Listener(ctx, wsUrls);\n" + + emptyNewLine + + constructorLines.join("\n") + + emptyNewLine + + emptyNewLine + + " this.custom = customFactory(ctx, this);\n" + + " }\n" + + "}\n"; + + fs.writeFileSync(outputFile, content, "utf-8"); + console.log(`\nAPIs file generated at ${outputFile}\n`); +} + +generateAPIsFile(); diff --git a/_libs/zca-js/src/Errors/ZaloApiError.ts b/_libs/zca-js/src/Errors/ZaloApiError.ts new file mode 100644 index 0000000..bd2adcb --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiError.ts @@ -0,0 +1,10 @@ +export class ZaloApiError extends Error { + public code: number | null; + + constructor(message: string, code?: number) { + super(message); + + this.name = "ZcaApiError"; + this.code = code || null; + } +} diff --git a/_libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts b/_libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts new file mode 100644 index 0000000..1ed55d7 --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts @@ -0,0 +1,8 @@ +import { ZaloApiError } from "./ZaloApiError.js"; + +export class ZaloApiLoginQRAborted extends ZaloApiError { + constructor(message: string = "Operation aborted") { + super(message); + this.name = "ZaloApiLoginQRAborted"; + } +} diff --git a/_libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts b/_libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts new file mode 100644 index 0000000..845b8fd --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts @@ -0,0 +1,8 @@ +import { ZaloApiError } from "./ZaloApiError.js"; + +export class ZaloApiLoginQRDeclined extends ZaloApiError { + constructor(message: string = "Login QR request declined") { + super(message); + this.name = "ZaloApiLoginQRDeclined"; + } +} diff --git a/_libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts b/_libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts new file mode 100644 index 0000000..2db29b4 --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts @@ -0,0 +1,8 @@ +import { ZaloApiError } from "./ZaloApiError.js"; + +export class ZaloApiMissingImageMetadataGetter extends ZaloApiError { + constructor() { + super("Missing `imageMetadataGetter`. Please provide it in the Zalo object options."); + this.name = "ZaloApiMissingImageMetadataGetter"; + } +} diff --git a/_libs/zca-js/src/Errors/index.ts b/_libs/zca-js/src/Errors/index.ts new file mode 100644 index 0000000..dc73921 --- /dev/null +++ b/_libs/zca-js/src/Errors/index.ts @@ -0,0 +1,4 @@ +export * from "./ZaloApiError.js"; +export * from "./ZaloApiMissingImageMetadataGetter.js"; +export * from "./ZaloApiLoginQRAborted.js"; +export * from "./ZaloApiLoginQRDeclined.js"; diff --git a/_libs/zca-js/src/apis.ts b/_libs/zca-js/src/apis.ts new file mode 100644 index 0000000..a3c914a --- /dev/null +++ b/_libs/zca-js/src/apis.ts @@ -0,0 +1,455 @@ +import { Listener } from "./apis/listen.js"; + +import { acceptFriendRequestFactory } from "./apis/acceptFriendRequest.js"; +import { addGroupBlockedMemberFactory } from "./apis/addGroupBlockedMember.js"; +import { addGroupDeputyFactory } from "./apis/addGroupDeputy.js"; +import { addPollOptionsFactory } from "./apis/addPollOptions.js"; +import { addQuickMessageFactory } from "./apis/addQuickMessage.js"; +import { addReactionFactory } from "./apis/addReaction.js"; +import { addUnreadMarkFactory } from "./apis/addUnreadMark.js"; +import { addUserToGroupFactory } from "./apis/addUserToGroup.js"; +import { blockUserFactory } from "./apis/blockUser.js"; +import { blockViewFeedFactory } from "./apis/blockViewFeed.js"; +import { changeAccountAvatarFactory } from "./apis/changeAccountAvatar.js"; +import { changeFriendAliasFactory } from "./apis/changeFriendAlias.js"; +import { changeGroupAvatarFactory } from "./apis/changeGroupAvatar.js"; +import { changeGroupNameFactory } from "./apis/changeGroupName.js"; +import { changeGroupOwnerFactory } from "./apis/changeGroupOwner.js"; +import { createAutoReplyFactory } from "./apis/createAutoReply.js"; +import { createCatalogFactory } from "./apis/createCatalog.js"; +import { createGroupFactory } from "./apis/createGroup.js"; +import { createNoteFactory } from "./apis/createNote.js"; +import { createPollFactory } from "./apis/createPoll.js"; +import { createProductCatalogFactory } from "./apis/createProductCatalog.js"; +import { createReminderFactory } from "./apis/createReminder.js"; +import { deleteAutoReplyFactory } from "./apis/deleteAutoReply.js"; +import { deleteAvatarFactory } from "./apis/deleteAvatar.js"; +import { deleteCatalogFactory } from "./apis/deleteCatalog.js"; +import { deleteChatFactory } from "./apis/deleteChat.js"; +import { deleteGroupInviteBoxFactory } from "./apis/deleteGroupInviteBox.js"; +import { deleteMessageFactory } from "./apis/deleteMessage.js"; +import { deleteProductCatalogFactory } from "./apis/deleteProductCatalog.js"; +import { disableGroupLinkFactory } from "./apis/disableGroupLink.js"; +import { disperseGroupFactory } from "./apis/disperseGroup.js"; +import { editNoteFactory } from "./apis/editNote.js"; +import { editReminderFactory } from "./apis/editReminder.js"; +import { enableGroupLinkFactory } from "./apis/enableGroupLink.js"; +import { fetchAccountInfoFactory } from "./apis/fetchAccountInfo.js"; +import { findUserFactory } from "./apis/findUser.js"; +import { findUserByUsernameFactory } from "./apis/findUserByUsername.js"; +import { forwardMessageFactory } from "./apis/forwardMessage.js"; +import { getAliasListFactory } from "./apis/getAliasList.js"; +import { getAllFriendsFactory } from "./apis/getAllFriends.js"; +import { getAllGroupsFactory } from "./apis/getAllGroups.js"; +import { getArchivedChatListFactory } from "./apis/getArchivedChatList.js"; +import { getAutoDeleteChatFactory } from "./apis/getAutoDeleteChat.js"; +import { getAutoReplyListFactory } from "./apis/getAutoReplyList.js"; +import { getAvatarListFactory } from "./apis/getAvatarList.js"; +import { getAvatarUrlProfileFactory } from "./apis/getAvatarUrlProfile.js"; +import { getBizAccountFactory } from "./apis/getBizAccount.js"; +import { getCatalogListFactory } from "./apis/getCatalogList.js"; +import { getCloseFriendsFactory } from "./apis/getCloseFriends.js"; +import { getContextFactory } from "./apis/getContext.js"; +import { getCookieFactory } from "./apis/getCookie.js"; +import { getFriendBoardListFactory } from "./apis/getFriendBoardList.js"; +import { getFriendOnlinesFactory } from "./apis/getFriendOnlines.js"; +import { getFriendRecommendationsFactory } from "./apis/getFriendRecommendations.js"; +import { getFriendRequestStatusFactory } from "./apis/getFriendRequestStatus.js"; +import { getFullAvatarFactory } from "./apis/getFullAvatar.js"; +import { getGroupBlockedMemberFactory } from "./apis/getGroupBlockedMember.js"; +import { getGroupChatHistoryFactory } from "./apis/getGroupChatHistory.js"; +import { getGroupInfoFactory } from "./apis/getGroupInfo.js"; +import { getGroupInviteBoxInfoFactory } from "./apis/getGroupInviteBoxInfo.js"; +import { getGroupInviteBoxListFactory } from "./apis/getGroupInviteBoxList.js"; +import { getGroupLinkDetailFactory } from "./apis/getGroupLinkDetail.js"; +import { getGroupLinkInfoFactory } from "./apis/getGroupLinkInfo.js"; +import { getGroupMembersInfoFactory } from "./apis/getGroupMembersInfo.js"; +import { getHiddenConversationsFactory } from "./apis/getHiddenConversations.js"; +import { getLabelsFactory } from "./apis/getLabels.js"; +import { getListBoardFactory } from "./apis/getListBoard.js"; +import { getListReminderFactory } from "./apis/getListReminder.js"; +import { getMultiUsersByPhonesFactory } from "./apis/getMultiUsersByPhones.js"; +import { getMuteFactory } from "./apis/getMute.js"; +import { getOwnIdFactory } from "./apis/getOwnId.js"; +import { getPendingGroupMembersFactory } from "./apis/getPendingGroupMembers.js"; +import { getPinConversationsFactory } from "./apis/getPinConversations.js"; +import { getPollDetailFactory } from "./apis/getPollDetail.js"; +import { getProductCatalogListFactory } from "./apis/getProductCatalogList.js"; +import { getQRFactory } from "./apis/getQR.js"; +import { getQuickMessageListFactory } from "./apis/getQuickMessageList.js"; +import { getRelatedFriendGroupFactory } from "./apis/getRelatedFriendGroup.js"; +import { getReminderFactory } from "./apis/getReminder.js"; +import { getReminderResponsesFactory } from "./apis/getReminderResponses.js"; +import { getSentFriendRequestFactory } from "./apis/getSentFriendRequest.js"; +import { getSettingsFactory } from "./apis/getSettings.js"; +import { getStickerCategoryDetailFactory } from "./apis/getStickerCategoryDetail.js"; +import { getStickersFactory } from "./apis/getStickers.js"; +import { getStickersDetailFactory } from "./apis/getStickersDetail.js"; +import { getUnreadMarkFactory } from "./apis/getUnreadMark.js"; +import { getUserInfoFactory } from "./apis/getUserInfo.js"; +import { inviteUserToGroupsFactory } from "./apis/inviteUserToGroups.js"; +import { joinGroupInviteBoxFactory } from "./apis/joinGroupInviteBox.js"; +import { joinGroupLinkFactory } from "./apis/joinGroupLink.js"; +import { keepAliveFactory } from "./apis/keepAlive.js"; +import { lastOnlineFactory } from "./apis/lastOnline.js"; +import { leaveGroupFactory } from "./apis/leaveGroup.js"; +import { lockPollFactory } from "./apis/lockPoll.js"; +import { parseLinkFactory } from "./apis/parseLink.js"; +import { rejectFriendRequestFactory } from "./apis/rejectFriendRequest.js"; +import { removeFriendFactory } from "./apis/removeFriend.js"; +import { removeFriendAliasFactory } from "./apis/removeFriendAlias.js"; +import { removeGroupBlockedMemberFactory } from "./apis/removeGroupBlockedMember.js"; +import { removeGroupDeputyFactory } from "./apis/removeGroupDeputy.js"; +import { removeQuickMessageFactory } from "./apis/removeQuickMessage.js"; +import { removeReminderFactory } from "./apis/removeReminder.js"; +import { removeUnreadMarkFactory } from "./apis/removeUnreadMark.js"; +import { removeUserFromGroupFactory } from "./apis/removeUserFromGroup.js"; +import { resetHiddenConversPinFactory } from "./apis/resetHiddenConversPin.js"; +import { reuseAvatarFactory } from "./apis/reuseAvatar.js"; +import { reviewPendingMemberRequestFactory } from "./apis/reviewPendingMemberRequest.js"; +import { searchStickerFactory } from "./apis/searchSticker.js"; +import { sendBankCardFactory } from "./apis/sendBankCard.js"; +import { sendCardFactory } from "./apis/sendCard.js"; +import { sendDeliveredEventFactory } from "./apis/sendDeliveredEvent.js"; +import { sendFriendRequestFactory } from "./apis/sendFriendRequest.js"; +import { sendLinkFactory } from "./apis/sendLink.js"; +import { sendMessageFactory } from "./apis/sendMessage.js"; +import { sendReportFactory } from "./apis/sendReport.js"; +import { sendSeenEventFactory } from "./apis/sendSeenEvent.js"; +import { sendStickerFactory } from "./apis/sendSticker.js"; +import { sendTypingEventFactory } from "./apis/sendTypingEvent.js"; +import { sendVideoFactory } from "./apis/sendVideo.js"; +import { sendVoiceFactory } from "./apis/sendVoice.js"; +import { setHiddenConversationsFactory } from "./apis/setHiddenConversations.js"; +import { setMuteFactory } from "./apis/setMute.js"; +import { setPinnedConversationsFactory } from "./apis/setPinnedConversations.js"; +import { sharePollFactory } from "./apis/sharePoll.js"; +import { unblockUserFactory } from "./apis/unblockUser.js"; +import { undoFactory } from "./apis/undo.js"; +import { undoFriendRequestFactory } from "./apis/undoFriendRequest.js"; +import { updateActiveStatusFactory } from "./apis/updateActiveStatus.js"; +import { updateArchivedChatListFactory } from "./apis/updateArchivedChatList.js"; +import { updateAutoDeleteChatFactory } from "./apis/updateAutoDeleteChat.js"; +import { updateAutoReplyFactory } from "./apis/updateAutoReply.js"; +import { updateCatalogFactory } from "./apis/updateCatalog.js"; +import { updateGroupSettingsFactory } from "./apis/updateGroupSettings.js"; +import { updateHiddenConversPinFactory } from "./apis/updateHiddenConversPin.js"; +import { updateLabelsFactory } from "./apis/updateLabels.js"; +import { updateLangFactory } from "./apis/updateLang.js"; +import { updateProductCatalogFactory } from "./apis/updateProductCatalog.js"; +import { updateProfileFactory } from "./apis/updateProfile.js"; +import { updateProfileBioFactory } from "./apis/updateProfileBio.js"; +import { updateQuickMessageFactory } from "./apis/updateQuickMessage.js"; +import { updateSettingsFactory } from "./apis/updateSettings.js"; +import { upgradeGroupToCommunityFactory } from "./apis/upgradeGroupToCommunity.js"; +import { uploadAttachmentFactory } from "./apis/uploadAttachment.js"; +import { uploadProductPhotoFactory } from "./apis/uploadProductPhoto.js"; +import { votePollFactory } from "./apis/votePoll.js"; +import { customFactory } from "./apis/custom.js"; +import type { ZPWServiceMap, ContextSession } from "./context.js"; + +export class API { + public zpwServiceMap: ZPWServiceMap; + public listener: Listener; + + public acceptFriendRequest: ReturnType; + public addGroupBlockedMember: ReturnType; + public addGroupDeputy: ReturnType; + public addPollOptions: ReturnType; + public addQuickMessage: ReturnType; + public addReaction: ReturnType; + public addUnreadMark: ReturnType; + public addUserToGroup: ReturnType; + public blockUser: ReturnType; + public blockViewFeed: ReturnType; + public changeAccountAvatar: ReturnType; + public changeFriendAlias: ReturnType; + public changeGroupAvatar: ReturnType; + public changeGroupName: ReturnType; + public changeGroupOwner: ReturnType; + public createAutoReply: ReturnType; + public createCatalog: ReturnType; + public createGroup: ReturnType; + public createNote: ReturnType; + public createPoll: ReturnType; + public createProductCatalog: ReturnType; + public createReminder: ReturnType; + public deleteAutoReply: ReturnType; + public deleteAvatar: ReturnType; + public deleteCatalog: ReturnType; + public deleteChat: ReturnType; + public deleteGroupInviteBox: ReturnType; + public deleteMessage: ReturnType; + public deleteProductCatalog: ReturnType; + public disableGroupLink: ReturnType; + public disperseGroup: ReturnType; + public editNote: ReturnType; + public editReminder: ReturnType; + public enableGroupLink: ReturnType; + public fetchAccountInfo: ReturnType; + public findUser: ReturnType; + public findUserByUsername: ReturnType; + public forwardMessage: ReturnType; + public getAliasList: ReturnType; + public getAllFriends: ReturnType; + public getAllGroups: ReturnType; + public getArchivedChatList: ReturnType; + public getAutoDeleteChat: ReturnType; + public getAutoReplyList: ReturnType; + public getAvatarList: ReturnType; + public getAvatarUrlProfile: ReturnType; + public getBizAccount: ReturnType; + public getCatalogList: ReturnType; + public getCloseFriends: ReturnType; + public getContext: ReturnType; + public getCookie: ReturnType; + public getFriendBoardList: ReturnType; + public getFriendOnlines: ReturnType; + public getFriendRecommendations: ReturnType; + public getFriendRequestStatus: ReturnType; + public getFullAvatar: ReturnType; + public getGroupBlockedMember: ReturnType; + public getGroupChatHistory: ReturnType; + public getGroupInfo: ReturnType; + public getGroupInviteBoxInfo: ReturnType; + public getGroupInviteBoxList: ReturnType; + public getGroupLinkDetail: ReturnType; + public getGroupLinkInfo: ReturnType; + public getGroupMembersInfo: ReturnType; + public getHiddenConversations: ReturnType; + public getLabels: ReturnType; + public getListBoard: ReturnType; + public getListReminder: ReturnType; + public getMultiUsersByPhones: ReturnType; + public getMute: ReturnType; + public getOwnId: ReturnType; + public getPendingGroupMembers: ReturnType; + public getPinConversations: ReturnType; + public getPollDetail: ReturnType; + public getProductCatalogList: ReturnType; + public getQR: ReturnType; + public getQuickMessageList: ReturnType; + public getRelatedFriendGroup: ReturnType; + public getReminder: ReturnType; + public getReminderResponses: ReturnType; + public getSentFriendRequest: ReturnType; + public getSettings: ReturnType; + public getStickerCategoryDetail: ReturnType; + public getStickers: ReturnType; + public getStickersDetail: ReturnType; + public getUnreadMark: ReturnType; + public getUserInfo: ReturnType; + public inviteUserToGroups: ReturnType; + public joinGroupInviteBox: ReturnType; + public joinGroupLink: ReturnType; + public keepAlive: ReturnType; + public lastOnline: ReturnType; + public leaveGroup: ReturnType; + public lockPoll: ReturnType; + public parseLink: ReturnType; + public rejectFriendRequest: ReturnType; + public removeFriend: ReturnType; + public removeFriendAlias: ReturnType; + public removeGroupBlockedMember: ReturnType; + public removeGroupDeputy: ReturnType; + public removeQuickMessage: ReturnType; + public removeReminder: ReturnType; + public removeUnreadMark: ReturnType; + public removeUserFromGroup: ReturnType; + public resetHiddenConversPin: ReturnType; + public reuseAvatar: ReturnType; + public reviewPendingMemberRequest: ReturnType; + public searchSticker: ReturnType; + public sendBankCard: ReturnType; + public sendCard: ReturnType; + public sendDeliveredEvent: ReturnType; + public sendFriendRequest: ReturnType; + public sendLink: ReturnType; + public sendMessage: ReturnType; + public sendReport: ReturnType; + public sendSeenEvent: ReturnType; + public sendSticker: ReturnType; + public sendTypingEvent: ReturnType; + public sendVideo: ReturnType; + public sendVoice: ReturnType; + public setHiddenConversations: ReturnType; + public setMute: ReturnType; + public setPinnedConversations: ReturnType; + public sharePoll: ReturnType; + public unblockUser: ReturnType; + public undo: ReturnType; + public undoFriendRequest: ReturnType; + public updateActiveStatus: ReturnType; + public updateArchivedChatList: ReturnType; + public updateAutoDeleteChat: ReturnType; + public updateAutoReply: ReturnType; + public updateCatalog: ReturnType; + public updateGroupSettings: ReturnType; + public updateHiddenConversPin: ReturnType; + public updateLabels: ReturnType; + public updateLang: ReturnType; + public updateProductCatalog: ReturnType; + public updateProfile: ReturnType; + public updateProfileBio: ReturnType; + public updateQuickMessage: ReturnType; + public updateSettings: ReturnType; + public upgradeGroupToCommunity: ReturnType; + public uploadAttachment: ReturnType; + public uploadProductPhoto: ReturnType; + public votePoll: ReturnType; + + public custom: ReturnType; + + constructor(ctx: ContextSession, zpwServiceMap: ZPWServiceMap, wsUrls: string[]) { + this.zpwServiceMap = zpwServiceMap; + this.listener = new Listener(ctx, wsUrls); + + this.acceptFriendRequest = acceptFriendRequestFactory(ctx, this); + this.addGroupBlockedMember = addGroupBlockedMemberFactory(ctx, this); + this.addGroupDeputy = addGroupDeputyFactory(ctx, this); + this.addPollOptions = addPollOptionsFactory(ctx, this); + this.addQuickMessage = addQuickMessageFactory(ctx, this); + this.addReaction = addReactionFactory(ctx, this); + this.addUnreadMark = addUnreadMarkFactory(ctx, this); + this.addUserToGroup = addUserToGroupFactory(ctx, this); + this.blockUser = blockUserFactory(ctx, this); + this.blockViewFeed = blockViewFeedFactory(ctx, this); + this.changeAccountAvatar = changeAccountAvatarFactory(ctx, this); + this.changeFriendAlias = changeFriendAliasFactory(ctx, this); + this.changeGroupAvatar = changeGroupAvatarFactory(ctx, this); + this.changeGroupName = changeGroupNameFactory(ctx, this); + this.changeGroupOwner = changeGroupOwnerFactory(ctx, this); + this.createAutoReply = createAutoReplyFactory(ctx, this); + this.createCatalog = createCatalogFactory(ctx, this); + this.createGroup = createGroupFactory(ctx, this); + this.createNote = createNoteFactory(ctx, this); + this.createPoll = createPollFactory(ctx, this); + this.createProductCatalog = createProductCatalogFactory(ctx, this); + this.createReminder = createReminderFactory(ctx, this); + this.deleteAutoReply = deleteAutoReplyFactory(ctx, this); + this.deleteAvatar = deleteAvatarFactory(ctx, this); + this.deleteCatalog = deleteCatalogFactory(ctx, this); + this.deleteChat = deleteChatFactory(ctx, this); + this.deleteGroupInviteBox = deleteGroupInviteBoxFactory(ctx, this); + this.deleteMessage = deleteMessageFactory(ctx, this); + this.deleteProductCatalog = deleteProductCatalogFactory(ctx, this); + this.disableGroupLink = disableGroupLinkFactory(ctx, this); + this.disperseGroup = disperseGroupFactory(ctx, this); + this.editNote = editNoteFactory(ctx, this); + this.editReminder = editReminderFactory(ctx, this); + this.enableGroupLink = enableGroupLinkFactory(ctx, this); + this.fetchAccountInfo = fetchAccountInfoFactory(ctx, this); + this.findUser = findUserFactory(ctx, this); + this.findUserByUsername = findUserByUsernameFactory(ctx, this); + this.forwardMessage = forwardMessageFactory(ctx, this); + this.getAliasList = getAliasListFactory(ctx, this); + this.getAllFriends = getAllFriendsFactory(ctx, this); + this.getAllGroups = getAllGroupsFactory(ctx, this); + this.getArchivedChatList = getArchivedChatListFactory(ctx, this); + this.getAutoDeleteChat = getAutoDeleteChatFactory(ctx, this); + this.getAutoReplyList = getAutoReplyListFactory(ctx, this); + this.getAvatarList = getAvatarListFactory(ctx, this); + this.getAvatarUrlProfile = getAvatarUrlProfileFactory(ctx, this); + this.getBizAccount = getBizAccountFactory(ctx, this); + this.getCatalogList = getCatalogListFactory(ctx, this); + this.getCloseFriends = getCloseFriendsFactory(ctx, this); + this.getContext = getContextFactory(ctx, this); + this.getCookie = getCookieFactory(ctx, this); + this.getFriendBoardList = getFriendBoardListFactory(ctx, this); + this.getFriendOnlines = getFriendOnlinesFactory(ctx, this); + this.getFriendRecommendations = getFriendRecommendationsFactory(ctx, this); + this.getFriendRequestStatus = getFriendRequestStatusFactory(ctx, this); + this.getFullAvatar = getFullAvatarFactory(ctx, this); + this.getGroupBlockedMember = getGroupBlockedMemberFactory(ctx, this); + this.getGroupChatHistory = getGroupChatHistoryFactory(ctx, this); + this.getGroupInfo = getGroupInfoFactory(ctx, this); + this.getGroupInviteBoxInfo = getGroupInviteBoxInfoFactory(ctx, this); + this.getGroupInviteBoxList = getGroupInviteBoxListFactory(ctx, this); + this.getGroupLinkDetail = getGroupLinkDetailFactory(ctx, this); + this.getGroupLinkInfo = getGroupLinkInfoFactory(ctx, this); + this.getGroupMembersInfo = getGroupMembersInfoFactory(ctx, this); + this.getHiddenConversations = getHiddenConversationsFactory(ctx, this); + this.getLabels = getLabelsFactory(ctx, this); + this.getListBoard = getListBoardFactory(ctx, this); + this.getListReminder = getListReminderFactory(ctx, this); + this.getMultiUsersByPhones = getMultiUsersByPhonesFactory(ctx, this); + this.getMute = getMuteFactory(ctx, this); + this.getOwnId = getOwnIdFactory(ctx, this); + this.getPendingGroupMembers = getPendingGroupMembersFactory(ctx, this); + this.getPinConversations = getPinConversationsFactory(ctx, this); + this.getPollDetail = getPollDetailFactory(ctx, this); + this.getProductCatalogList = getProductCatalogListFactory(ctx, this); + this.getQR = getQRFactory(ctx, this); + this.getQuickMessageList = getQuickMessageListFactory(ctx, this); + this.getRelatedFriendGroup = getRelatedFriendGroupFactory(ctx, this); + this.getReminder = getReminderFactory(ctx, this); + this.getReminderResponses = getReminderResponsesFactory(ctx, this); + this.getSentFriendRequest = getSentFriendRequestFactory(ctx, this); + this.getSettings = getSettingsFactory(ctx, this); + this.getStickerCategoryDetail = getStickerCategoryDetailFactory(ctx, this); + this.getStickers = getStickersFactory(ctx, this); + this.getStickersDetail = getStickersDetailFactory(ctx, this); + this.getUnreadMark = getUnreadMarkFactory(ctx, this); + this.getUserInfo = getUserInfoFactory(ctx, this); + this.inviteUserToGroups = inviteUserToGroupsFactory(ctx, this); + this.joinGroupInviteBox = joinGroupInviteBoxFactory(ctx, this); + this.joinGroupLink = joinGroupLinkFactory(ctx, this); + this.keepAlive = keepAliveFactory(ctx, this); + this.lastOnline = lastOnlineFactory(ctx, this); + this.leaveGroup = leaveGroupFactory(ctx, this); + this.lockPoll = lockPollFactory(ctx, this); + this.parseLink = parseLinkFactory(ctx, this); + this.rejectFriendRequest = rejectFriendRequestFactory(ctx, this); + this.removeFriend = removeFriendFactory(ctx, this); + this.removeFriendAlias = removeFriendAliasFactory(ctx, this); + this.removeGroupBlockedMember = removeGroupBlockedMemberFactory(ctx, this); + this.removeGroupDeputy = removeGroupDeputyFactory(ctx, this); + this.removeQuickMessage = removeQuickMessageFactory(ctx, this); + this.removeReminder = removeReminderFactory(ctx, this); + this.removeUnreadMark = removeUnreadMarkFactory(ctx, this); + this.removeUserFromGroup = removeUserFromGroupFactory(ctx, this); + this.resetHiddenConversPin = resetHiddenConversPinFactory(ctx, this); + this.reuseAvatar = reuseAvatarFactory(ctx, this); + this.reviewPendingMemberRequest = reviewPendingMemberRequestFactory(ctx, this); + this.searchSticker = searchStickerFactory(ctx, this); + this.sendBankCard = sendBankCardFactory(ctx, this); + this.sendCard = sendCardFactory(ctx, this); + this.sendDeliveredEvent = sendDeliveredEventFactory(ctx, this); + this.sendFriendRequest = sendFriendRequestFactory(ctx, this); + this.sendLink = sendLinkFactory(ctx, this); + this.sendMessage = sendMessageFactory(ctx, this); + this.sendReport = sendReportFactory(ctx, this); + this.sendSeenEvent = sendSeenEventFactory(ctx, this); + this.sendSticker = sendStickerFactory(ctx, this); + this.sendTypingEvent = sendTypingEventFactory(ctx, this); + this.sendVideo = sendVideoFactory(ctx, this); + this.sendVoice = sendVoiceFactory(ctx, this); + this.setHiddenConversations = setHiddenConversationsFactory(ctx, this); + this.setMute = setMuteFactory(ctx, this); + this.setPinnedConversations = setPinnedConversationsFactory(ctx, this); + this.sharePoll = sharePollFactory(ctx, this); + this.unblockUser = unblockUserFactory(ctx, this); + this.undo = undoFactory(ctx, this); + this.undoFriendRequest = undoFriendRequestFactory(ctx, this); + this.updateActiveStatus = updateActiveStatusFactory(ctx, this); + this.updateArchivedChatList = updateArchivedChatListFactory(ctx, this); + this.updateAutoDeleteChat = updateAutoDeleteChatFactory(ctx, this); + this.updateAutoReply = updateAutoReplyFactory(ctx, this); + this.updateCatalog = updateCatalogFactory(ctx, this); + this.updateGroupSettings = updateGroupSettingsFactory(ctx, this); + this.updateHiddenConversPin = updateHiddenConversPinFactory(ctx, this); + this.updateLabels = updateLabelsFactory(ctx, this); + this.updateLang = updateLangFactory(ctx, this); + this.updateProductCatalog = updateProductCatalogFactory(ctx, this); + this.updateProfile = updateProfileFactory(ctx, this); + this.updateProfileBio = updateProfileBioFactory(ctx, this); + this.updateQuickMessage = updateQuickMessageFactory(ctx, this); + this.updateSettings = updateSettingsFactory(ctx, this); + this.upgradeGroupToCommunity = upgradeGroupToCommunityFactory(ctx, this); + this.uploadAttachment = uploadAttachmentFactory(ctx, this); + this.uploadProductPhoto = uploadProductPhotoFactory(ctx, this); + this.votePoll = votePollFactory(ctx, this); + + this.custom = customFactory(ctx, this); + } +} diff --git a/_libs/zca-js/src/apis/acceptFriendRequest.ts b/_libs/zca-js/src/apis/acceptFriendRequest.ts new file mode 100644 index 0000000..0d1f9a9 --- /dev/null +++ b/_libs/zca-js/src/apis/acceptFriendRequest.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AcceptFriendRequestResponse = ""; + +export const acceptFriendRequestFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/accept`); + + /** + * Accept a friend request from a User + * + * @param friendId The friend ID to user request is accept + * + * @throws {ZaloApiError} + */ + return async function acceptFriendRequest(friendId: string) { + const params = { + fid: friendId, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addGroupBlockedMember.ts b/_libs/zca-js/src/apis/addGroupBlockedMember.ts new file mode 100644 index 0000000..6e514fd --- /dev/null +++ b/_libs/zca-js/src/apis/addGroupBlockedMember.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AddGroupBlockedMemberResponse = ""; + +export const addGroupBlockedMemberFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/blockedmems/add`); + + /** + * Add group blocked member + * + * @param memberId member id(s) + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function addGroupBlockedMember(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addGroupDeputy.ts b/_libs/zca-js/src/apis/addGroupDeputy.ts new file mode 100644 index 0000000..fd73a81 --- /dev/null +++ b/_libs/zca-js/src/apis/addGroupDeputy.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AddGroupDeputyResponse = ""; + +export const addGroupDeputyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/admins/add`); + + /** + * Add group deputy + * + * @param memberId user Id or list of user Ids + * @param groupId group Id + * + * @throws {ZaloApiError} + * + */ + return async function addGroupDeputy(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addPollOptions.ts b/_libs/zca-js/src/apis/addPollOptions.ts new file mode 100644 index 0000000..8a8be15 --- /dev/null +++ b/_libs/zca-js/src/apis/addPollOptions.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { PollOptions } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddPollOptionsOption = { + voted: boolean; + content: string; +}; + +export type AddPollOptionsPayload = { + pollId: number; + options: AddPollOptionsOption[]; + votedOptionIds: number[]; +}; + +export type AddPollOptionsResponse = { + options: PollOptions[]; +}; + +export const addPollOptionsFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/option/add`); + + /** + * Add new option to poll + * + * @param payload + * + * @throws {ZaloApiError} + */ + return async function addPollOptions(payload: AddPollOptionsPayload) { + const params = { + poll_id: payload.pollId, + new_options: JSON.stringify(payload.options), + voted_option_ids: payload.votedOptionIds, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addQuickMessage.ts b/_libs/zca-js/src/apis/addQuickMessage.ts new file mode 100644 index 0000000..df6c5c6 --- /dev/null +++ b/_libs/zca-js/src/apis/addQuickMessage.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { QuickMessage, AttachmentSource } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddQuickMessagePayload = { + keyword: string; + title: string; + media?: AttachmentSource; +}; + +export type AddQuickMessageResponse = { + item: QuickMessage; + version: number; +}; + +export const addQuickMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/create`); + + /** + * Add quick message + * + * @param addPayload - The payload containing data to add the quick message + * + * @note Zalo might throw an error with code 821 if you have reached the limit of quick messages. + * + * @throws {ZaloApiError} + */ + return async function addQuickMessage(addPayload: AddQuickMessagePayload) { + const isType = !addPayload.media ? 0 : 1; + + const params: Record = { + keyword: addPayload.keyword, + message: { + title: addPayload.title, + params: "", + }, + type: isType, + imei: ctx.imei, + }; + + if (isType === 1) { + if (!addPayload.media) throw new ZaloApiError("Media is required"); + const uploadMedia = await api.uploadProductPhoto({ + file: addPayload.media, + }); + + const photoId = uploadMedia.photoId; + const thumbUrl = uploadMedia.thumbUrl; + const normalUrl = uploadMedia.normalUrl; + const hdUrl = uploadMedia.hdUrl; + + params.media = { + items: [ + { + type: 0, + photoId: photoId, + title: "", + width: "", + height: "", + previewThumb: thumbUrl, + rawUrl: normalUrl || hdUrl, + thumbUrl: thumbUrl, + normalUrl: normalUrl || hdUrl, + hdUrl: hdUrl || normalUrl, + }, + ], + }; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addReaction.ts b/_libs/zca-js/src/apis/addReaction.ts new file mode 100644 index 0000000..64a684f --- /dev/null +++ b/_libs/zca-js/src/apis/addReaction.ts @@ -0,0 +1,321 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType, Reactions } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddReactionResponse = { + msgIds: number[]; +}; + +export type CustomReaction = { + rType: number; + source: number; + icon: string; +}; + +export type AddReactionDestination = { + data: { + msgId: string; + cliMsgId: string; + }; + threadId: string; + type: ThreadType; +}; + +export const addReactionFactory = apiFactory()((api, ctx, utils) => { + const serviceURLs = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.reaction[0]}/api/message/reaction`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.reaction[0]}/api/group/reaction`), + }; + + /** + * Add reaction to a message + * + * @param icon Reaction icon + * @param dest Destination data including message IDs and thread information + * + * @throws {ZaloApiError} + */ + return async function addReaction(icon: Reactions | CustomReaction, dest: AddReactionDestination) { + const serviceURL = serviceURLs[dest.type]; + let rType, source; + + if (typeof icon == "object") { + rType = icon.rType; + source = icon.source; + } else + switch (icon) { + case Reactions.HAHA: + rType = 0; + source = 6; + break; + case Reactions.LIKE: + rType = 3; + source = 6; + break; + case Reactions.HEART: + rType = 5; + source = 6; + break; + case Reactions.WOW: + rType = 32; + source = 6; + break; + case Reactions.CRY: + rType = 2; + source = 6; + break; + case Reactions.ANGRY: + rType = 20; + source = 6; + break; + case Reactions.KISS: + rType = 8; + source = 6; + break; + case Reactions.TEARS_OF_JOY: + rType = 7; + source = 6; + break; + case Reactions.SHIT: + rType = 66; + source = 6; + break; + case Reactions.ROSE: + rType = 120; + source = 6; + break; + case Reactions.BROKEN_HEART: + rType = 65; + source = 6; + break; + case Reactions.DISLIKE: + rType = 4; + source = 6; + break; + case Reactions.LOVE: + rType = 29; + source = 6; + break; + case Reactions.CONFUSED: + rType = 51; + source = 6; + break; + case Reactions.WINK: + rType = 45; + source = 6; + break; + case Reactions.FADE: + rType = 121; + source = 6; + break; + case Reactions.SUN: + rType = 67; + source = 6; + break; + case Reactions.BIRTHDAY: + rType = 126; + source = 6; + break; + case Reactions.BOMB: + rType = 127; + source = 6; + break; + case Reactions.OK: + rType = 68; + source = 6; + break; + case Reactions.PEACE: + rType = 69; + source = 6; + break; + case Reactions.THANKS: + rType = 70; + source = 6; + break; + case Reactions.PUNCH: + rType = 71; + source = 6; + break; + case Reactions.SHARE: + rType = 72; + source = 6; + break; + case Reactions.PRAY: + rType = 73; + source = 6; + break; + case Reactions.NO: + rType = 131; + source = 6; + break; + case Reactions.BAD: + rType = 132; + source = 6; + break; + case Reactions.LOVE_YOU: + rType = 133; + source = 6; + break; + case Reactions.SAD: + rType = 1; + source = 6; + break; + case Reactions.VERY_SAD: + rType = 16; + source = 6; + break; + case Reactions.COOL: + rType = 21; + source = 6; + break; + case Reactions.NERD: + rType = 22; + source = 6; + break; + case Reactions.BIG_SMILE: + rType = 23; + source = 6; + break; + case Reactions.SUNGLASSES: + rType = 26; + source = 6; + break; + case Reactions.NEUTRAL: + rType = 30; + source = 6; + break; + case Reactions.SAD_FACE: + rType = 35; + source = 6; + break; + case Reactions.BYE: + rType = 36; + source = 6; + break; + case Reactions.SLEEPY: + rType = 38; + source = 6; + break; + case Reactions.WIPE: + rType = 39; + source = 6; + break; + case Reactions.DIG: + rType = 42; + source = 6; + break; + case Reactions.ANGUISH: + rType = 44; + source = 6; + break; + case Reactions.HANDCLAP: + rType = 46; + source = 6; + break; + case Reactions.ANGRY_FACE: + rType = 47; + source = 6; + break; + case Reactions.F_CHAIR: + rType = 48; + source = 6; + break; + case Reactions.L_CHAIR: + rType = 49; + source = 6; + break; + case Reactions.R_CHAIR: + rType = 50; + source = 6; + break; + case Reactions.SILENT: + rType = 52; + source = 6; + break; + case Reactions.SURPRISE: + rType = 53; + source = 6; + break; + case Reactions.EMBARRASSED: + rType = 54; + source = 6; + break; + case Reactions.AFRAID: + rType = 60; + source = 6; + break; + case Reactions.SAD2: + rType = 61; + source = 6; + break; + case Reactions.BIG_LAUGH: + rType = 62; + source = 6; + break; + case Reactions.RICH: + rType = 63; + source = 6; + break; + case Reactions.BEER: + rType = 99; + source = 6; + break; + default: + rType = -1; + source = 6; + } + + const rIcon = typeof icon == "object" ? icon.icon : icon; + + if (rType == undefined || source == undefined || rIcon == undefined) { + throw new ZaloApiError("Invalid reaction"); + } + + const params: Record = { + react_list: [ + { + message: JSON.stringify({ + rMsg: [ + { + gMsgID: parseInt(dest.data.msgId), + cMsgID: parseInt(dest.data.cliMsgId), + msgType: 1, + }, + ], + rIcon, + rType, + source, + }), + clientId: Date.now(), + }, + ], + }; + + if (dest.type == ThreadType.User) { + params.toid = dest.threadId; + } else { + params.grid = dest.threadId; + params.imei = ctx.imei; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + if (typeof (result.data as { msgIds: unknown }).msgIds === "string") { + return { + msgIds: JSON.parse((result.data as { msgIds: string }).msgIds), + }; + } + + return result.data as AddReactionResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/addUnreadMark.ts b/_libs/zca-js/src/apis/addUnreadMark.ts new file mode 100644 index 0000000..583bc74 --- /dev/null +++ b/_libs/zca-js/src/apis/addUnreadMark.ts @@ -0,0 +1,65 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddUnreadMarkResponse = { + data: { + updateId: number; + }; + status: number; +}; + +export const addUnreadMarkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/addUnreadMark`); + + /** + * Add unread mark to conversation + * + * @param threadId Thread ID + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function addUnreadMark(threadId: string, type: ThreadType = ThreadType.User) { + const timestamp = Date.now(); + const timestampString = timestamp.toString(); + const isGroup = type === ThreadType.Group; + + const requestParams = { + param: JSON.stringify({ + [isGroup ? "convsGroup" : "convsUser"]: [ + { + id: threadId, + cliMsgId: timestampString, + fromUid: "0", + ts: timestamp, + }, + ], + [isGroup ? "convsUser" : "convsGroup"]: [], + imei: ctx.imei, + }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + const data = result.data as { data: unknown; status: number }; + if (typeof data.data === "string") { + return { + data: JSON.parse(data.data), + status: data.status, + }; + } + + return result.data as AddUnreadMarkResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/addUserToGroup.ts b/_libs/zca-js/src/apis/addUserToGroup.ts new file mode 100644 index 0000000..f50c85d --- /dev/null +++ b/_libs/zca-js/src/apis/addUserToGroup.ts @@ -0,0 +1,43 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AddUserToGroupResponse = { + errorMembers: string[]; + error_data: Record; +}; + +export const addUserToGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/invite/v2`); + + /** + * Add user to existing group + * + * @param memberId User ID or list of user IDs to add + * @param groupId Group ID + * + * @throws {ZaloApiError} + */ + return async function addUserToGroup(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + memberTypes: memberId.map(() => -1), + imei: ctx.imei, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/blockUser.ts b/_libs/zca-js/src/apis/blockUser.ts new file mode 100644 index 0000000..166c3ad --- /dev/null +++ b/_libs/zca-js/src/apis/blockUser.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type BlockUserResponse = ""; + +export const blockUserFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/block`); + + /** + * Block a User + * + * @param userId The ID of the User to block + * + * @throws {ZaloApiError} + */ + return async function blockUser(userId: string) { + const params = { + fid: userId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/blockViewFeed.ts b/_libs/zca-js/src/apis/blockViewFeed.ts new file mode 100644 index 0000000..e5a0223 --- /dev/null +++ b/_libs/zca-js/src/apis/blockViewFeed.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type BlockViewFeedResponse = ""; + +export const blockViewFeedFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/feed/block`); + + /** + * Block/Unblock friend view feed by ID + * + * @param isBlockFeed Boolean to block/unblock view feed + * @param userId User ID to block/unblock view feed + * + * @throws {ZaloApiError} + */ + return async function blockViewFeed(isBlockFeed: boolean, userId: string) { + const params = { + fid: userId, + isBlockFeed: isBlockFeed ? 1 : 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeAccountAvatar.ts b/_libs/zca-js/src/apis/changeAccountAvatar.ts new file mode 100644 index 0000000..2be9960 --- /dev/null +++ b/_libs/zca-js/src/apis/changeAccountAvatar.ts @@ -0,0 +1,65 @@ +import FormData from "form-data"; +import fs from "node:fs"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory, formatTime, getImageMetaData } from "../utils.js"; + +export type ChangeAccountAvatarResponse = ""; + +export const changeAccountAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/profile/upavatar`); + + /** + * Change account avatar + * + * @param avatarSource Attachment source, can be a file path or an Attachment object + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function changeAccountAvatar(avatarSource: AttachmentSource) { + const isSourceFilePath = typeof avatarSource == "string"; + const imageMetaData = isSourceFilePath ? await getImageMetaData(ctx, avatarSource) : avatarSource.metadata; + + const fileSize = imageMetaData.totalSize || 0; + + const params = { + avatarSize: 120, + clientId: String(ctx.uid + formatTime("%H:%M %d/%m/%Y")), + language: ctx.language, + metaData: JSON.stringify({ + origin: { + width: imageMetaData.width || 1080, + height: imageMetaData.height || 1080, + }, + processed: { + width: imageMetaData.width || 1080, + height: imageMetaData.height || 1080, + size: fileSize, + }, + }), + }; + + const avatarData = isSourceFilePath ? fs.readFileSync(avatarSource) : avatarSource.data; + const formData = new FormData(); + formData.append("fileContent", avatarData, { + filename: "blob", + contentType: "image/jpeg", + }); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeFriendAlias.ts b/_libs/zca-js/src/apis/changeFriendAlias.ts new file mode 100644 index 0000000..899cf68 --- /dev/null +++ b/_libs/zca-js/src/apis/changeFriendAlias.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ChangeFriendAliasResponse = ""; + +export const changeFriendAliasFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/update`); + + /** + * Change friend's alias + * + * @param alias new alias (nickname - bietdanh) + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function changeFriendAlias(alias: string, friendId: string) { + const params = { + friendId: friendId, + alias: alias, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeGroupAvatar.ts b/_libs/zca-js/src/apis/changeGroupAvatar.ts new file mode 100644 index 0000000..e9301a1 --- /dev/null +++ b/_libs/zca-js/src/apis/changeGroupAvatar.ts @@ -0,0 +1,57 @@ +import FormData from "form-data"; +import fs from "node:fs"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory, getFullTimeFromMillisecond, getImageMetaData } from "../utils.js"; + +export type ChangeGroupAvatarResponse = ""; + +export const changeGroupAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/upavatar`); + + /** + * Change group avatar + * + * @param avatarSource Attachment source, can be a file path or an Attachment object + * @param groupId Group ID + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function changeGroupAvatar(avatarSource: AttachmentSource, groupId: string) { + const params: Record = { + grid: groupId, + avatarSize: 120, + clientId: `g${groupId}${getFullTimeFromMillisecond(new Date().getTime())}`, + imei: ctx.imei, + }; + + const isSourceFilePath = typeof avatarSource == "string"; + const imageMetaData = isSourceFilePath ? await getImageMetaData(ctx, avatarSource) : avatarSource.metadata; + + params.originWidth = imageMetaData.width || 1080; + params.originHeight = imageMetaData.height || 1080; + + const avatarData = isSourceFilePath ? fs.readFileSync(avatarSource) : avatarSource.data; + const formData = new FormData(); + formData.append("fileContent", avatarData, { + filename: "blob", + contentType: "image/jpeg", + }); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeGroupName.ts b/_libs/zca-js/src/apis/changeGroupName.ts new file mode 100644 index 0000000..0719d87 --- /dev/null +++ b/_libs/zca-js/src/apis/changeGroupName.ts @@ -0,0 +1,40 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ChangeGroupNameResponse = { + status: number; +}; + +export const changeGroupNameFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/updateinfo`); + + /** + * Change group name + * + * @param name New group name + * @param groupId Group ID + * + * @throws {ZaloApiError} + */ + return async function changeGroupName(name: string, groupId: string) { + if (name.length == 0) name = Date.now().toString(); + + const params = { + grid: groupId, + gname: name, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeGroupOwner.ts b/_libs/zca-js/src/apis/changeGroupOwner.ts new file mode 100644 index 0000000..ba48207 --- /dev/null +++ b/_libs/zca-js/src/apis/changeGroupOwner.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ChangeGroupOwnerResponse = { + time: number; +}; + +export const changeGroupOwnerFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/change-owner`); + + /** + * Change group owner + * + * @param memberId User Id of new group owner + * @param groupId Group Id + * @note Be careful when changing the key, as it will result in losing group admin rights + * + * @throws {ZaloApiError} + * + */ + return async function changeGroupOwner(memberId: string, groupId: string) { + const params = { + grid: groupId, + newAdminId: memberId, + imei: ctx.imei, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createAutoReply.ts b/_libs/zca-js/src/apis/createAutoReply.ts new file mode 100644 index 0000000..2c071ac --- /dev/null +++ b/_libs/zca-js/src/apis/createAutoReply.ts @@ -0,0 +1,58 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AutoReplyItem, AutoReplyScope } from "../models/index.js"; + +export type CreateAutoReplyPayload = { + content: string; + isEnable: boolean; + startTime: number; + endTime: number; + scope: AutoReplyScope; + uids?: string | string[]; +}; + +export type CreateAutoReplyResponse = { + item: AutoReplyItem; + version: number; +}; + +export const createAutoReplyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/create`); + + /** + * Create auto reply + * + * @param payload payload + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function createAutoReply(payload: CreateAutoReplyPayload) { + const uids = Array.isArray(payload.uids) ? payload.uids : [payload.uids]; + const resultUids = (payload.scope === 2 || payload.scope === 3) ? uids : []; + + const params = { + cliLang: ctx.language, + enable: payload.isEnable, + content: payload.content, + startTime: payload.startTime, + endTime: payload.endTime, + recurrence: ["RRULE:FREQ=DAILY;"], + scope: payload.scope, + uids: resultUids, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createCatalog.ts b/_libs/zca-js/src/apis/createCatalog.ts new file mode 100644 index 0000000..a031d7a --- /dev/null +++ b/_libs/zca-js/src/apis/createCatalog.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { CatalogItem } from "../models/index.js"; + +export type CreateCatalogResponse = { + item: CatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const createCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/create`); + + /** + * Create catalog? + * + * @param catalogName catalog name + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function createCatalog(catalogName: string) { + const params = { + catalog_name: catalogName, + catalog_photo: "", + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createGroup.ts b/_libs/zca-js/src/apis/createGroup.ts new file mode 100644 index 0000000..8b56fcc --- /dev/null +++ b/_libs/zca-js/src/apis/createGroup.ts @@ -0,0 +1,77 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type CreateGroupResponse = { + groupType: number; + sucessMembers: string[]; + groupId: string; + errorMembers: string[]; + error_data: Record; +}; + +export type CreateGroupOptions = { + /** + * Group name + */ + name?: string; + /** + * List of member IDs to add to the group + */ + members: string[]; + /** + * Avatar source, can be a file path or an Attachment object + */ + avatarSource?: AttachmentSource; + /** + * Path to the avatar image file + * @deprecated Use `avatarSource` instead + */ + avatarPath?: string; +}; + +export const createGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/create/v2`); + + /** + * Create a new group + * + * @param options Group options + * + * @throws {ZaloApiError} + */ + return async function createGroup(options: CreateGroupOptions) { + if (options.members.length == 0) throw new ZaloApiError("Group must have at least one member"); + + const params = { + clientId: Date.now(), + gname: String(Date.now()), + gdesc: null, + members: options.members, + membersTypes: options.members.map(() => -1), + nameChanged: 0, + createLink: 1, + clientLang: ctx.language, + imei: ctx.imei, + zsource: 601, + }; + + if (options.name && options.name.length > 0) { + params.gname = options.name; + params.nameChanged = 1; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "POST", + }); + + const data = await utils.resolve(response); + options.avatarSource = options.avatarSource || options.avatarPath; + if (options.avatarSource) await api.changeGroupAvatar(options.avatarSource, data.groupId).catch(utils.logger.error); + + return data; + }; +}); diff --git a/_libs/zca-js/src/apis/createNote.ts b/_libs/zca-js/src/apis/createNote.ts new file mode 100644 index 0000000..af8d37f --- /dev/null +++ b/_libs/zca-js/src/apis/createNote.ts @@ -0,0 +1,62 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { NoteDetail } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type CreateNoteOptions = { + title: string; + pinAct?: boolean; +}; + +export type CreateNoteResponse = NoteDetail; + +export const createNoteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/createv2`); + + /** + * Create a note in a group + * + * @param options note options + * @param options.title note title + * @param options.pinAct pin action (pin note) + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function createNote(options: CreateNoteOptions, groupId: string) { + const params = { + grid: groupId, + type: 0, + color: -16777216, + emoji: "", + startTime: -1, + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + repeat: 0, + src: 1, + imei: ctx.imei, + pinAct: options.pinAct ? 1 : 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + if (typeof (result.data as { params: unknown }).params === "string") { + (result.data as CreateNoteResponse).params = JSON.parse( + (result.data as { params: string }).params, + ); + } + + return result.data as CreateNoteResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/createPoll.ts b/_libs/zca-js/src/apis/createPoll.ts new file mode 100644 index 0000000..343e2da --- /dev/null +++ b/_libs/zca-js/src/apis/createPoll.ts @@ -0,0 +1,87 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { PollDetail } from "../models/index.js"; + +/** + * Options for creating a poll. + */ +export type CreatePollOptions = { + /** + * Question for the poll. + */ + question: string; + + /** + * List of options for the poll. + */ + options: string[]; + + /** + * Poll expiration time in milliseconds (0 = no expiration). + */ + expiredTime?: number; + + /** + * Allows multiple choices in the poll. + */ + allowMultiChoices?: boolean; + + /** + * Allows members to add new options to the poll. + */ + allowAddNewOption?: boolean; + + /** + * Hides voting results until the user has voted. + */ + hideVotePreview?: boolean; + + /** + * Hides poll voters (anonymous poll). + */ + isAnonymous?: boolean; +}; + +export type CreatePollResponse = PollDetail; + +export const createPollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/create`); + + /** + * Create a poll in a group. + * + * @param options Poll options + * @param groupId Group ID to create poll from + * + * @throws {ZaloApiError} + */ + return async function createPoll(options: CreatePollOptions, groupId: string) { + const params = { + group_id: groupId, + question: options.question, + options: options.options, + expired_time: options.expiredTime ?? 0, + pinAct: false, + allow_multi_choices: !!options.allowMultiChoices, + allow_add_new_option: !!options.allowAddNewOption, + is_hide_vote_preview: !!options.hideVotePreview, + is_anonymous: !!options.isAnonymous, + poll_type: 0, + src: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createProductCatalog.ts b/_libs/zca-js/src/apis/createProductCatalog.ts new file mode 100644 index 0000000..8588a58 --- /dev/null +++ b/_libs/zca-js/src/apis/createProductCatalog.ts @@ -0,0 +1,86 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AttachmentSource, ProductCatalogItem } from "../models/index.js"; + +export type CreateProductCatalogPayload = { + catalogId: string; + + productName: string; + price: string; + description: string; + + /** + * Upto 5 media files are allowed, will be ignored if product_photos is provided + */ + files?: AttachmentSource[]; + /** + * List of product photo URLs, upto 5 + * + * You can manually get the URL using `uploadProductPhoto` api + */ + product_photos?: string[]; +}; + +export type CreateProductCatalogResponse = { + item: ProductCatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const createProductCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/create`); + + /** + * Create product catalog? + * + * @param payload payload + * + * @note this API is used for zBussiness - Maximum 5 media files are supported + * @throws {ZaloApiError} + */ + return async function createProductCatalog(payload: CreateProductCatalogPayload) { + const productPhoto = payload.product_photos || []; + + if (payload.files && payload.files.length == 0) { + if (payload.files.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + for (const mediaFile of payload.files) { + const uploadMedia = await api.uploadProductPhoto({ + file: mediaFile, + }); + + const url = uploadMedia.normalUrl || uploadMedia.hdUrl; + productPhoto.push(url); + } + } + + if (productPhoto.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + const params = { + product_name: payload.productName, + price: payload.price, + description: payload.description, + product_photos: productPhoto, + catalog_id: payload.catalogId, + currency_unit: "โ‚ซ", // $ + create_time: Date.now(), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createReminder.ts b/_libs/zca-js/src/apis/createReminder.ts new file mode 100644 index 0000000..9cb535f --- /dev/null +++ b/_libs/zca-js/src/apis/createReminder.ts @@ -0,0 +1,84 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderGroup, ReminderUser} from "../models/index.js"; +import { ReminderRepeatMode, ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type CreateReminderOptions = { + title: string; + emoji?: string; + startTime?: number; + repeat?: ReminderRepeatMode; +}; + +export type CreateReminderUser = ReminderUser; +export type CreateReminderGroup = Omit; + +export type CreateReminderResponse = CreateReminderUser | CreateReminderGroup; + +export const createReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/create`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/createv2`), + }; + + /** + * Create a reminder in a group + * + * @param options reminder options + * @param threadId Group ID to create note from + * @param type Thread type (User or Group) + * + * @throws {ZaloApiError} + */ + return async function createReminder( + options: CreateReminderOptions, + threadId: string, + type: ThreadType = ThreadType.User, + ): Promise { + const params = + type === ThreadType.User + ? { + objectData: JSON.stringify({ + toUid: threadId, + type: 0, + color: -16245706, + emoji: options.emoji ?? "โฐ", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: { title: options.title }, + needPin: false, + repeat: options.repeat ?? ReminderRepeatMode.None, + creatorUid: ctx.uid, // Note: for some reason, you can put any valid UID here instead of your own and it still works, atleast for mobile + src: 1, + }), + imei: ctx.imei, + } + : { + grid: threadId, + type: 0, + color: -16245706, + emoji: options.emoji ?? "โฐ", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + repeat: options.repeat ?? ReminderRepeatMode.None, + src: 1, + imei: ctx.imei, + pinAct: 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/custom.ts b/_libs/zca-js/src/apis/custom.ts new file mode 100644 index 0000000..5ce4382 --- /dev/null +++ b/_libs/zca-js/src/apis/custom.ts @@ -0,0 +1,19 @@ +import { type ContextSession } from "../context.js"; +import { apiFactory, type FactoryUtils } from "../utils.js"; + +export type CustomAPIProps = { ctx: ContextSession; utils: FactoryUtils; props: K }; +export type CustomAPICallback = (props: CustomAPIProps) => T | Promise; + +/* eslint-disable */ +export const customFactory = apiFactory()((api, ctx, utils) => { + return function custom(name: string, callback: CustomAPICallback) { + Object.defineProperty(api, name, { + value: function (props: K) { + return callback({ ctx, utils, props }); + }, + writable: false, + enumerable: false, + configurable: false, + }); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteAutoReply.ts b/_libs/zca-js/src/apis/deleteAutoReply.ts new file mode 100644 index 0000000..e02dac2 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteAutoReply.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteAutoReplyResponse = { + item: number; + version: number; +}; + +export const deleteAutoReplyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/delete`); + + /** + * Delete auto reply + * + * @param id id of auto reply + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function deleteAutoReply(id: number) { + const params = { + cliLang: ctx.language, + id: id, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteAvatar.ts b/_libs/zca-js/src/apis/deleteAvatar.ts new file mode 100644 index 0000000..1e4afd9 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteAvatar.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteAvatarResponse = { + delPhotoIds: string[]; + errMap: { + [key: string]: { + err: number; + }; + }; +}; + +export const deleteAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/del-avatars`); + + /** + * Delete avatar from avatar list + * + * @param photoId avatar photo ID(s) to delete - can be a single string or array of strings + * + * @throws {ZaloApiError} + */ + return async function deleteAvatar(photoId: string | string[]) { + const photoIds = Array.isArray(photoId) ? photoId : [photoId]; + const delPhotos = photoIds.map((id) => ({ photoId: id })); + + const params = { + delPhotos: JSON.stringify(delPhotos), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteCatalog.ts b/_libs/zca-js/src/apis/deleteCatalog.ts new file mode 100644 index 0000000..0f913b4 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteCatalog.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteCatalogResponse = ""; + +export const deleteCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/delete`); + + /** + * Delete catalog? + * + * @param catalogId catalog id + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function deleteCatalog(catalogId: string) { + const params = { + catalog_id: catalogId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteChat.ts b/_libs/zca-js/src/apis/deleteChat.ts new file mode 100644 index 0000000..0daff36 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteChat.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteChatResponse = { + status: number; +}; + +export type DeleteChatLastMessage = { + /** + * Last message owner ID to delete backwards + */ + ownerId: string; + /** + * Last message client ID to delete backwards + */ + cliMsgId: string; + /** + * Last message global ID to delete backwards + */ + globalMsgId: string; +}; + +export const deleteChatFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/deleteconver`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/deleteconver`, { + nretry: 0, + }), + }; + + /** + * Delete chat + * + * @param lastMessage Last message info + * @param threadId Thread ID + * @param type Thread type + * + * @throws {ZaloApiError} + */ + return async function deleteChat( + lastMessage: DeleteChatLastMessage, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + const timestampString = Date.now().toString(); + + const params = + type === ThreadType.User + ? { + toid: threadId, + cliMsgId: timestampString, + conver: lastMessage, + onlyMe: 1, + imei: ctx.imei, + } + : { + grid: threadId, + cliMsgId: timestampString, + conver: lastMessage, + onlyMe: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteGroupInviteBox.ts b/_libs/zca-js/src/apis/deleteGroupInviteBox.ts new file mode 100644 index 0000000..e7bfabc --- /dev/null +++ b/_libs/zca-js/src/apis/deleteGroupInviteBox.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteGroupInviteBoxResponse = { + delInvitaionIds: string[]; + errMap: { + [groupId: string]: { + err: number; + }; + }; +}; + +export const deleteGroupInviteBoxFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/mdel-inv`); + + /** + * Delete group invite box + * + * @param groupId - The group id + * @param blockFutureInvite - Whether to block future invites from this group + * + * @throws {ZaloApiError} + */ + return async function deleteGroupInviteBox(groupId: string | string[], blockFutureInvite: boolean = false) { + const grids = Array.isArray(groupId) ? groupId : [groupId]; + + const params = { + invitations: JSON.stringify(grids.map((grid) => ({ grid }))), + block: blockFutureInvite ? 1 : 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteMessage.ts b/_libs/zca-js/src/apis/deleteMessage.ts new file mode 100644 index 0000000..2226b74 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteMessage.ts @@ -0,0 +1,72 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteMessageResponse = { + status: number; +}; + +export type DeleteMessageDestination = { + data: { + cliMsgId: string; + msgId: string; + uidFrom: string; + }; + threadId: string; + type?: ThreadType; +}; + +export const deleteMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/delete`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/deletemsg`), + }; + /** + * Delete a message + * + * @param dest Delete target + * @param onlyMe Delete message for only you + * + * @throws {ZaloApiError} + */ + return async function deleteMessage(dest: DeleteMessageDestination, onlyMe = false) { + const { threadId, type = ThreadType.User, data } = dest; + const isGroup = type === ThreadType.Group; + const isSelf = ctx.uid == data.uidFrom; + + if (isSelf && onlyMe === false) + throw new ZaloApiError("To delete your message for everyone, use undo api instead"); + + if (!isGroup && onlyMe === false) throw new ZaloApiError("Can't delete message for everyone in a private chat"); + + const params = { + [isGroup ? "grid" : "toid"]: threadId, + cliMsgId: Date.now(), + msgs: [ + { + cliMsgId: data.cliMsgId, + globalMsgId: data.msgId, + ownerId: data.uidFrom, + destId: threadId, + }, + ], + onlyMe: onlyMe ? 1 : 0, + }; + + if (!isGroup) { + params.imei = ctx.imei; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteProductCatalog.ts b/_libs/zca-js/src/apis/deleteProductCatalog.ts new file mode 100644 index 0000000..e7de226 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteProductCatalog.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteProductCatalogPayload = { + productIds: string | string[]; + catalogId: string; +}; + +export type DeleteProductCatalogResponse = { + item: number[]; + version_ls_catalog: number; + version_catalog: number; +}; + +export const deleteProductCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/mdelete`); + + /** + * Delete product catalog? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function deleteProductCatalog(payload: DeleteProductCatalogPayload) { + if (!Array.isArray(payload.productIds)) payload.productIds = [payload.productIds]; + + const params = { + product_ids: payload.productIds, + catalog_id: payload.catalogId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/disableGroupLink.ts b/_libs/zca-js/src/apis/disableGroupLink.ts new file mode 100644 index 0000000..b9f5431 --- /dev/null +++ b/_libs/zca-js/src/apis/disableGroupLink.ts @@ -0,0 +1,30 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DisableGroupLinkResponse = ""; + +export const disableGroupLinkFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/disable`); + + /** + * Disable group link + * + * @param groupId The group id + * + * @throws {ZaloApiError} + */ + return async function disableGroupLink(groupId: string) { + const params = { + grid: groupId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/disperseGroup.ts b/_libs/zca-js/src/apis/disperseGroup.ts new file mode 100644 index 0000000..c46c943 --- /dev/null +++ b/_libs/zca-js/src/apis/disperseGroup.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DisperseGroupResponse = ""; + +export const disperseGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/disperse`); + + /** + * Disperse Group + * + * @param groupId Group ID to disperse Group from + * + * @throws {ZaloApiError} + */ + return async function disperseGroup(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/editNote.ts b/_libs/zca-js/src/apis/editNote.ts new file mode 100644 index 0000000..6b17a60 --- /dev/null +++ b/_libs/zca-js/src/apis/editNote.ts @@ -0,0 +1,70 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { NoteDetail } from "../models/index.js"; + +export type EditNoteOptions = { + /** + * New note title + */ + title: string; + /** + * Topic ID to edit note from + */ + topicId: string; + /** + * Should the note be pinned? + */ + pinAct?: boolean; +}; + +export type EditNoteResponse = NoteDetail; + +export const editNoteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/updatev2`); + + /** + * Edit an existing note in a group + * + * @param options Options for editing the note + * @param groupId Group ID to create note from + * + * @throws {ZaloApiError} + */ + return async function editNote(options: EditNoteOptions, groupId: string) { + const params = { + grid: groupId, + type: 0, + color: -16777216, + emoji: "", + startTime: -1, + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + topicId: options.topicId, + repeat: 0, + imei: ctx.imei, + pinAct: options.pinAct ? 1 : 2, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + const data = result.data as Omit & { params: unknown }; + if (typeof data.params == "string") { + data.params = JSON.parse(data.params); + } + + return data as NoteDetail; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/editReminder.ts b/_libs/zca-js/src/apis/editReminder.ts new file mode 100644 index 0000000..3a39cff --- /dev/null +++ b/_libs/zca-js/src/apis/editReminder.ts @@ -0,0 +1,83 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderRepeatMode, ReminderGroup, ReminderUser} from "../models/index.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type EditReminderOptions = { + title: string; + topicId: string; + emoji?: string; + startTime?: number; + repeat?: ReminderRepeatMode; +}; + +export type EditReminderUser = ReminderUser; +export type EditReminderGroup = Omit; + +export type EditReminderResponse = EditReminderUser | EditReminderGroup; + +export const editReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/update`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/updatev2`), + }; + + /** + * Edit an existing reminder + * + * @param options Reminder parameters + * @param threadId Thread ID + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function editReminder( + options: EditReminderOptions, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + const requestParams = + type === ThreadType.User + ? { + objectData: JSON.stringify({ + toUid: threadId, + type: 0, + color: -16777216, + emoji: options.emoji ?? "", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: { title: options.title }, + needPin: false, + reminderId: options.topicId, + repeat: options.repeat ?? 0, + }), + } + : { + grid: threadId, + type: 0, + color: -16777216, + emoji: options.emoji ?? "", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + topicId: options.topicId, + repeat: options.repeat ?? 0, + imei: ctx.imei, + pinAct: 2, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/enableGroupLink.ts b/_libs/zca-js/src/apis/enableGroupLink.ts new file mode 100644 index 0000000..6c40100 --- /dev/null +++ b/_libs/zca-js/src/apis/enableGroupLink.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type EnableGroupLinkResponse = { + link: string; + expiration_date: number; + enabled: number; +}; + +export const enableGroupLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/new`); + + /** + * Enable and create new group link + * + * @param groupId The group id + * + * @throws {ZaloApiError} + */ + return async function enableGroupLink(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/fetchAccountInfo.ts b/_libs/zca-js/src/apis/fetchAccountInfo.ts new file mode 100644 index 0000000..307d41d --- /dev/null +++ b/_libs/zca-js/src/apis/fetchAccountInfo.ts @@ -0,0 +1,17 @@ +import { apiFactory } from "../utils.js"; + +import type { User } from "../models/index.js"; + +export type FetchAccountInfoResponse = { profile: User }; + +export const fetchAccountInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/me-v2`); + + return async function fetchAccountInfo() { + const response = await utils.request(serviceURL, { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/findUser.ts b/_libs/zca-js/src/apis/findUser.ts new file mode 100644 index 0000000..cfa3ccf --- /dev/null +++ b/_libs/zca-js/src/apis/findUser.ts @@ -0,0 +1,52 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type UserBasic } from "../models/index.js"; + +export type FindUserResponse = UserBasic; + +export const findUserFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/profile/get`); + + /** + * Find user by phone number + * + * @param phoneNumber Phone number + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function findUser(phoneNumber: string, avatarSize: AvatarSize = AvatarSize.Large) { + if (!phoneNumber) throw new ZaloApiError("Missing phoneNumber"); + if (phoneNumber.startsWith("0")) { + if (ctx.language == "vi") phoneNumber = "84" + phoneNumber.slice(1); + } + + const params = { + phone: phoneNumber, + avatar_size: avatarSize, + language: ctx.language, + imei: ctx.imei, + reqSrc: 40, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const finalServiceUrl = new URL(serviceURL); + finalServiceUrl.searchParams.append("params", encryptedParams); + + const response = await utils.request( + utils.makeURL(finalServiceUrl.toString(), { + params: encryptedParams, + }), + ); + + return utils.resolve(response, (result) => { + if (result.error && result.error.code != 216) + throw new ZaloApiError(result.error.message, result.error.code); + + return result.data as FindUserResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/findUserByUsername.ts b/_libs/zca-js/src/apis/findUserByUsername.ts new file mode 100644 index 0000000..964a67c --- /dev/null +++ b/_libs/zca-js/src/apis/findUserByUsername.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type UserBasic } from "../models/index.js"; + +export type FindUserByUsernameResponse = UserBasic; + +export const findUserByUsernameFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/search/by-user-name`); + + /** + * Find user by username + * + * @param username username for find + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function findUserByUsername(username: string, avatarSize: AvatarSize = AvatarSize.Large) { + const params = { + user_name: username, + avatar_size: avatarSize, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/forwardMessage.ts b/_libs/zca-js/src/apis/forwardMessage.ts new file mode 100644 index 0000000..7259388 --- /dev/null +++ b/_libs/zca-js/src/apis/forwardMessage.ts @@ -0,0 +1,127 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type ForwardMessagePayload = { + message: string; + ttl?: number; + reference?: { + id: string; + ts: number; + logSrcType: number; + fwLvl: number; + }; +}; + +export type ForwardMessageSuccess = { + clientId: string; + msgId: string; +}; + +export type ForwardMessageFail = { + clientId: string; + error_code: string; +}; + +export type ForwardMessageResponse = { + success: ForwardMessageSuccess[]; + fail: ForwardMessageFail[]; +}; + +export const forwardMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/mforward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/mforward`), + }; + + /** + * Forward message to multiple threads + * + * @param payload Forward message payload + * @param threadId Thread ID(s) + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function forwardMessage( + payload: ForwardMessagePayload, + threadIds: string[], + type: ThreadType = ThreadType.User, + ) { + if (!payload.message) throw new ZaloApiError("Missing message content"); + if (!threadIds || threadIds.length === 0) throw new ZaloApiError("Missing thread IDs"); + + const timestamp = Date.now(); + const clientId = timestamp.toString(); + + const msgInfo = { + message: payload.message, + reference: payload.reference + ? JSON.stringify({ + type: 3, + data: JSON.stringify(payload.reference), + }) + : undefined, + }; + + const decorLog = payload.reference + ? { + fw: { + pmsg: { + st: 1, + ts: payload.reference.ts, + id: payload.reference.id, + }, + rmsg: { + st: 1, + ts: payload.reference.ts, + id: payload.reference.id, + }, + fwLvl: payload.reference.fwLvl, + }, + } + : null; + + let params; + if (type === ThreadType.User) { + params = { + toIds: threadIds.map((threadId) => ({ + clientId, + toUid: threadId, + ttl: payload.ttl ?? 0, + })), + imei: ctx.imei, + ttl: payload.ttl ?? 0, + msgType: "1", + totalIds: threadIds.length, + msgInfo: JSON.stringify(msgInfo), + decorLog: JSON.stringify(decorLog), + }; + } else { + params = { + grids: threadIds.map((threadId) => ({ + clientId, + grid: threadId, + ttl: payload.ttl ?? 0, + })), + ttl: payload.ttl ?? 0, + msgType: "1", + totalIds: threadIds.length, + msgInfo: JSON.stringify(msgInfo), + decorLog: JSON.stringify(decorLog), + }; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAliasList.ts b/_libs/zca-js/src/apis/getAliasList.ts new file mode 100644 index 0000000..8e24d73 --- /dev/null +++ b/_libs/zca-js/src/apis/getAliasList.ts @@ -0,0 +1,39 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetAliasListResponse = { + items: { + userId: string; + alias: string; + }[]; + updateTime: string; +}; + +export const getAliasListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/list`); + + /** + * Get alias list + * + * @param count Page size (default: 100) + * @param page Page number (default: 1) + * + * @throws {ZaloApiError} + */ + return async function getAliasList(count: number = 100, page: number = 1) { + const params = { + page, + count, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAllFriends.ts b/_libs/zca-js/src/apis/getAllFriends.ts new file mode 100644 index 0000000..9fbe138 --- /dev/null +++ b/_libs/zca-js/src/apis/getAllFriends.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type User } from "../models/index.js"; + +export type GetAllFriendsResponse = User[]; + +export const getAllFriendsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getfriends`); + + /** + * Get all friends + * + * @param count Page size (default: 20000) + * @param page Page number (default: 1) + * @param avatarSize Avatar size (default: AvatarSize.Small) + * + * @throws {ZaloApiError} + */ + return async function getAllFriends(count: number = 20000, page: number = 1, avatarSize: AvatarSize = AvatarSize.Small) { + const params = { + incInvalid: 1, + page, + count, + avatar_size: avatarSize, + actiontime: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "GET", + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAllGroups.ts b/_libs/zca-js/src/apis/getAllGroups.ts new file mode 100644 index 0000000..6899ec0 --- /dev/null +++ b/_libs/zca-js/src/apis/getAllGroups.ts @@ -0,0 +1,20 @@ +import { apiFactory } from "../utils.js"; + +export type GetAllGroupsResponse = { + version: string; + gridVerMap: { + [groupId: string]: string; + }; +}; + +export const getAllGroupsFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_poll[0]}/api/group/getlg/v4`); + + return async function getAllGroups() { + const response = await utils.request(serviceURL, { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getArchivedChatList.ts b/_libs/zca-js/src/apis/getArchivedChatList.ts new file mode 100644 index 0000000..fac826a --- /dev/null +++ b/_libs/zca-js/src/apis/getArchivedChatList.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetArchivedChatListResponse = { + items: unknown[]; // @TODO: check type + version: number; +}; + +export const getArchivedChatListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/archivedchat/list`); + + /** + * Get arcnived chat list + * + * @throws {ZaloApiError} + */ + return async function getArchivedChatList() { + const params = { + version: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAutoDeleteChat.ts b/_libs/zca-js/src/apis/getAutoDeleteChat.ts new file mode 100644 index 0000000..eeff5fa --- /dev/null +++ b/_libs/zca-js/src/apis/getAutoDeleteChat.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetAutoDeleteChatResponse = { + convers: { + destId: string; + isGroup: boolean; + ttl: number; + createdAt: number; + }[]; +}; + +export const getAutoDeleteChatFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/autodelete/getConvers`); + + /** + * Get auto delete chat + * + * @throws {ZaloApiError} + * + */ + return async function getAutoDeleteChat() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAutoReplyList.ts b/_libs/zca-js/src/apis/getAutoReplyList.ts new file mode 100644 index 0000000..3ad3916 --- /dev/null +++ b/_libs/zca-js/src/apis/getAutoReplyList.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AutoReplyItem } from "../models/index.js"; + +export type GetAutoReplyListResponse = { + item: AutoReplyItem[]; + version: number; +}; +export const getAutoReplyListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/list`); + + /** + * Get auto reply list + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function getAutoReplyList() { + const params = { + version: 0, + cliLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAvatarList.ts b/_libs/zca-js/src/apis/getAvatarList.ts new file mode 100644 index 0000000..efe331b --- /dev/null +++ b/_libs/zca-js/src/apis/getAvatarList.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetAvatarListResponse = { + albumId: string; + nextPhotoId: string; + hasMore: number; + photos: { + photoId: string; + thumbnail: string; + url: string; + bkUrl: string; + }[]; +}; + +export const getAvatarListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/avatar-list`); + + /** + * Get avatar list + * + * @param count The number of avatars to fetch (default: 50) + * @param page The page number to fetch (default: 1) + * + * @throws {ZaloApiError} + */ + return async function getAvatarList(count: number = 50, page: number = 1) { + const params = { + page, + albumId: "0", + count, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAvatarUrlProfile.ts b/_libs/zca-js/src/apis/getAvatarUrlProfile.ts new file mode 100644 index 0000000..8e5689a --- /dev/null +++ b/_libs/zca-js/src/apis/getAvatarUrlProfile.ts @@ -0,0 +1,40 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { AvatarSize } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetAvatarUrlProfileResponse = { + [userId: string]: { + avatar: string; + }; +}; + +export const getAvatarUrlProfileFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/avatar-url`); + + /** + * Get avatar url profile + * + * @param friendId friend id(s) + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function getAvatarUrlProfile(friendIds: string | string[], avatarSize: AvatarSize = AvatarSize.Large) { + if (!Array.isArray(friendIds)) friendIds = [friendIds]; + + const params = { + friend_ids: friendIds, + avatar_size: avatarSize, + srcReq: -1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getBizAccount.ts b/_libs/zca-js/src/apis/getBizAccount.ts new file mode 100644 index 0000000..c625723 --- /dev/null +++ b/_libs/zca-js/src/apis/getBizAccount.ts @@ -0,0 +1,55 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { BusinessCategory } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetBizAccountResponse = { + biz?: { + desc: string | null; + cate: BusinessCategory; + addr: string; + website: string; + email: string; + }; + setting_start_page?: { + enable_biz_label: number; + enable_cate: number; + enable_add: number; + cta_profile: number; + /** + * Relative path used to build the catalog URL. + * + * Example: https://catalog.zalo.me/${cta_catalog} + */ + cta_catalog: string | null; + } | null; + pkgId: number; +}; + +export const getBizAccountFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/get-bizacc`); + + /** + * Get biz account + * + * @param friendId The friend ID to get biz account + * + * @throws {ZaloApiError} + */ + return async function getBizAccount(friendId: string) { + const params = { + fid: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getCatalogList.ts b/_libs/zca-js/src/apis/getCatalogList.ts new file mode 100644 index 0000000..7757199 --- /dev/null +++ b/_libs/zca-js/src/apis/getCatalogList.ts @@ -0,0 +1,55 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { CatalogItem } from "../models/index.js"; + +export type GetCatalogListPayload = { + /** + * Number of items to retrieve (default: 20) + */ + limit?: number; + lastProductId?: number; + /** + * Page number (default: 0) + */ + page?: number; +}; + +export type GetCatalogListResponse = { + items: CatalogItem[]; + version: number; + has_more: number; +}; + +export const getCatalogListFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/list`); + + /** + * Get catalog list? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function getCatalogList(payload?: GetCatalogListPayload) { + const params = { + version_list_catalog: 0, + limit: payload?.limit ?? 20, + last_product_id: payload?.lastProductId ?? -1, + page: payload?.page ?? 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getCloseFriends.ts b/_libs/zca-js/src/apis/getCloseFriends.ts new file mode 100644 index 0000000..658964d --- /dev/null +++ b/_libs/zca-js/src/apis/getCloseFriends.ts @@ -0,0 +1,27 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { User } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetCloseFriendsResponse = User[]; + +export const getCloseFriendsFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getclosedfriends`); + + /** + * Get close friends + * + * @throws {ZaloApiError} + */ + return async function getCloseFriends() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getContext.ts b/_libs/zca-js/src/apis/getContext.ts new file mode 100644 index 0000000..e224ad7 --- /dev/null +++ b/_libs/zca-js/src/apis/getContext.ts @@ -0,0 +1,5 @@ +import { apiFactory } from "../utils.js"; + +export const getContextFactory = apiFactory()((_, ctx) => { + return () => ctx; +}); diff --git a/_libs/zca-js/src/apis/getCookie.ts b/_libs/zca-js/src/apis/getCookie.ts new file mode 100644 index 0000000..913cec6 --- /dev/null +++ b/_libs/zca-js/src/apis/getCookie.ts @@ -0,0 +1,10 @@ +import { apiFactory } from "../utils.js"; + +export const getCookieFactory = apiFactory()((_, ctx) => { + /** + * Get the current cookie string + */ + return function getCookie() { + return ctx.cookie; + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendBoardList.ts b/_libs/zca-js/src/apis/getFriendBoardList.ts new file mode 100644 index 0000000..7cd20de --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendBoardList.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFriendBoardListResponse = { + data: string[]; + version: number; +}; + +export const getFriendBoardListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend_board[0]}/api/friendboard/list`); + + /** + * Get friend board list + * + * @param conversationId conversation id + * + * @throws {ZaloApiError} + */ + return async function getFriendBoardList(conversationId: string) { + const params = { + conversationId: conversationId, + version: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendOnlines.ts b/_libs/zca-js/src/apis/getFriendOnlines.ts new file mode 100644 index 0000000..2707d43 --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendOnlines.ts @@ -0,0 +1,53 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFriendOnlinesStatus = { + userId: string; + status: string; +}; + + +export type GetFriendOnlinesResponse = { + predefine: string[]; + ownerStatus: string; + onlines: GetFriendOnlinesStatus[]; +}; + +export const getFriendOnlinesFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/onlines`); + + /** + * Get friend onlines? + * + * @throws {ZaloApiError} + */ + return async function getFriendOnlines() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as GetFriendOnlinesResponse; + + if (Array.isArray(data.onlines)) { + for (const online of data.onlines) { + if (typeof online.status === "string") { + const parsed = JSON.parse(online.status); + if (parsed && typeof (parsed as { status?: unknown }).status === "string") { + online.status = (parsed as { status: string }).status; + } + } + } + } + + return data as GetFriendOnlinesResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendRecommendations.ts b/_libs/zca-js/src/apis/getFriendRecommendations.ts new file mode 100644 index 0000000..7bf8ab0 --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendRecommendations.ts @@ -0,0 +1,72 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { Gender, ZBusinessPackage } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export enum FriendRecommendationsType { + RecommendedFriend = 1, + ReceivedFriendRequest = 2, +} + +export type FriendRecommendationsCollapseMsgListConfig = { + collapseId: number; + collapseXItem: number; + collapseYItem: number; +}; + +export type FriendRecommendationsDataInfo = { + userId: string; + zaloName: string; + displayName: string; + avatar: string; + phoneNumber: string; + status: string; + gender: Gender; + dob: number; + type: number; + recommType: FriendRecommendationsType; + recommSrc: number; + recommTime: number; + recommInfo: { + suggestWay: number; + source: number; + message: string; + customText: string | null; + }; + bizPkg: ZBusinessPackage; + isSeenFriendReq: boolean; +}; + +export type FriendRecommendationsRecommItem = { + recommItemType: number; + dataInfo: FriendRecommendationsDataInfo; +}; + +export type GetFriendRecommendationsResponse = { + expiredDuration: number; + collapseMsgListConfig: FriendRecommendationsCollapseMsgListConfig; + recommItems: FriendRecommendationsRecommItem[]; +}; + +export const getFriendRecommendationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/recommendsv2/list`); + + /** + * Get friend recommendations/received friend requests + * + * @throws {ZaloApiError} + */ + return async function getFriendRecommendations() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendRequestStatus.ts b/_libs/zca-js/src/apis/getFriendRequestStatus.ts new file mode 100644 index 0000000..db34533 --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendRequestStatus.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFriendRequestStatusResponse = { + addFriendPrivacy: number; + isSeenFriendReq: boolean; + is_friend: number; + is_requested: number; + is_requesting: number; +}; + +export const getFriendRequestStatusFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/reqstatus`); + + /** + * Get friend request status + * + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function getFriendRequestStatus(friendId: string) { + const params = { + fid: friendId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getFullAvatar.ts b/_libs/zca-js/src/apis/getFullAvatar.ts new file mode 100644 index 0000000..013fa54 --- /dev/null +++ b/_libs/zca-js/src/apis/getFullAvatar.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFullAvatarResponse = { + bk_full_avatar: string; + full_avatar: string; +}; + +export const getFullAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/avatar`); + + /** + * Get full avatar + * + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function getFullAvatar(friendId: string) { + const params = { + fid: friendId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupBlockedMember.ts b/_libs/zca-js/src/apis/getGroupBlockedMember.ts new file mode 100644 index 0000000..b704f96 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupBlockedMember.ts @@ -0,0 +1,56 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupBlockedMemberPayload = { + /** + * Page number (default: 1) + */ + page?: number; + /** + * Number of items to retrieve (default: 50) + */ + count?: number; +}; + +export type GetGroupBlockedMemberResponse = { + blocked_members: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }[]; + has_more: number; +}; + +export const getGroupBlockedMemberFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/blockedmems/list`); + + /** + * Get group blocked member + * + * @param payload payload + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function getGroupBlockedMember(payload: GetGroupBlockedMemberPayload, groupId: string) { + const params = { + grid: groupId, + page: payload.page ?? 1, + count: payload.count ?? 50, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupChatHistory.ts b/_libs/zca-js/src/apis/getGroupChatHistory.ts new file mode 100644 index 0000000..a75698f --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupChatHistory.ts @@ -0,0 +1,51 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { GroupMessage, type TGroupMessage } from "../models/index.js"; + +export type GetGroupChatHistoryResponse = { + lastActionId: string; + lastActionIdOther: string; + more: number; + groupMsgs: GroupMessage[]; +}; + +export const getGroupChatHistoryFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/history`); + + /** + * Get group chat history + * + * @param groupId group id + * @param count count of messages to return (default: 50) + * + * @throws {ZaloApiError} + */ + return async function getGroupChatHistory(groupId: string, count: number = 50) { + const params = { + grid: groupId, + count: count, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + let data = result.data as unknown as GetGroupChatHistoryResponse | string; + + if (typeof data === "string") { + data = JSON.parse(data) as GetGroupChatHistoryResponse; + } + + for (let i = 0; i < data.groupMsgs.length; i++) { + data.groupMsgs[i] = new GroupMessage(ctx.uid, data.groupMsgs[i] as unknown as TGroupMessage); + } + + return data; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupInfo.ts b/_libs/zca-js/src/apis/getGroupInfo.ts new file mode 100644 index 0000000..ad8b0cf --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupInfo.ts @@ -0,0 +1,55 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { GroupInfo } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GroupInfoResponse = { + removedsGroup: string[]; + unchangedsGroup: string[]; + gridInfoMap: { + [groupId: string]: GroupInfo & { + memVerList: string[]; + pendingApprove: GroupInfoPendingApprove; + }; + }; +}; + +export type GroupInfoPendingApprove = { + time: number; + uids: string[] | null; +}; + +export const getGroupInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/getmg-v2`); + + /** + * Get group information + * + * @param groupId Group ID or list of group IDs + * + * @throws {ZaloApiError} + */ + return async function getGroupInfo(groupId: string | string[]) { + if (!Array.isArray(groupId)) groupId = [groupId]; + + const params = { + gridVerMap: JSON.stringify( + groupId.reduce>((acc, id) => { + acc[id] = 0; + return acc; + }, {}), + ), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupInviteBoxInfo.ts b/_libs/zca-js/src/apis/getGroupInviteBoxInfo.ts new file mode 100644 index 0000000..dd203e4 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupInviteBoxInfo.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { GroupInfo, GroupTopic } from "../models/index.js"; + +export type GetGroupInviteBoxInfoPayload = { + groupId: string; + mpage?: number; + mcount?: number; +}; + +export type GetGroupInviteBoxInfoResponse = { + groupInfo: GroupInfo & { + topic?: Omit; + }; + inviterInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + grCreatorInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + expiredTs: string; + type: number; +}; + +export const getGroupInviteBoxInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/inv-info`); + + /** + * Get group invite box info + * + * @param payload - The payload of the request + * + * @throws {ZaloApiError} + */ + return async function getGroupInviteBoxInfo(payload: GetGroupInviteBoxInfoPayload) { + const params = { + grId: payload.groupId, + mcount: payload.mcount ?? 10, + mpage: payload.mpage ?? 1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as GetGroupInviteBoxInfoResponse; + const topic = data.groupInfo.topic as GetGroupInviteBoxInfoResponse["groupInfo"]["topic"] & { + params: unknown; + }; + if (typeof topic.params == "string") { + const params = JSON.parse(topic.params); + if (typeof params.extra == "string") { + params.extra = JSON.parse(params.extra); + } + + topic.params = params; + } + + return data as GetGroupInviteBoxInfoResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupInviteBoxList.ts b/_libs/zca-js/src/apis/getGroupInviteBoxList.ts new file mode 100644 index 0000000..2f21908 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupInviteBoxList.ts @@ -0,0 +1,73 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { GroupInfo } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupInviteBoxListPayload = { + mpage?: number; + page?: number; + invPerPage?: number; + mcount?: number; +}; + +export type GetGroupInviteBoxListResponse = { + invitations: { + groupInfo: GroupInfo; + inviterInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + grCreatorInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + /** + * Expired timestamp max 7 days + */ + expiredTs: string; + type: number; + }[]; + total: number; + hasMore: boolean; +}; + +export const getGroupInviteBoxListFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/list`); + + /** + * Get group invite box list + * + * @param payload - The payload of the request + * + * @throws {ZaloApiError} + */ + return async function getGroupInviteBoxList(payload?: GetGroupInviteBoxListPayload) { + const params = { + mpage: payload?.mpage ?? 1, + page: payload?.page ?? 0, + invPerPage: payload?.invPerPage ?? 12, + mcount: payload?.mcount ?? 10, + lastGroupId: null, // @TODO: check type + avatar_size: 120, + member_avatar_size: 120, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupLinkDetail.ts b/_libs/zca-js/src/apis/getGroupLinkDetail.ts new file mode 100644 index 0000000..faccb8e --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupLinkDetail.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupLinkDetailResponse = { + link?: string; + expiration_date?: number; + /** + * 1: enabled, 0: disabled + */ + enabled: number; +}; + +export const getGroupLinkDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/detail`); + + /** + * Get group link detail + * + * @param groupId The group id + * + * @throws {ZaloApiError} + */ + return async function getGroupLinkDetail(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupLinkInfo.ts b/_libs/zca-js/src/apis/getGroupLinkInfo.ts new file mode 100644 index 0000000..367ecee --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupLinkInfo.ts @@ -0,0 +1,66 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { GroupSetting } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupLinkInfoPayload = { + link: string; + /** + * Default: 1 + */ + memberPage?: number; +}; + +export type GetGroupLinkInfoResponse = { + groupId: string; + name: string; + desc: string; + type: number; + creatorId: string; + avt: string; + fullAvt: string; + adminIds: string[]; + currentMems: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }[]; + admins: unknown[]; + hasMoreMember: number; + subType: number; + totalMember: number; + setting: GroupSetting; + globalId: string; +}; + +export const getGroupLinkInfoFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/ginfo`); + + /** + * Get group link info + * + * @param payload - The payload of the request + * + * @throws {ZaloApiError} + */ + return async function getGroupLinkInfo(payload: GetGroupLinkInfoPayload) { + const params = { + link: payload.link, + avatar_size: 120, + member_avatar_size: 120, + mpage: payload.memberPage ?? 1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupMembersInfo.ts b/_libs/zca-js/src/apis/getGroupMembersInfo.ts new file mode 100644 index 0000000..107e132 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupMembersInfo.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GroupMemberProfile = { + displayName: string; + zaloName: string; + avatar: string; + accountStatus: number; + type: number; + lastUpdateTime: number; + globalId: string; + id: string; +}; + +export type GetGroupMembersInfoResponse = { + profiles: { + [memberId: string]: GroupMemberProfile; + }; + unchangeds_profile: unknown[]; +}; + +export const getGroupMembersInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/group/members`); + + /** + * Get group information + * + * @param memberId member id or array of member ids + * + * @throws {ZaloApiError} + */ + return async function getGroupMembersInfo(memberId: string | string[]) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + friend_pversion_map: memberId.map((id) => (id.endsWith("_0") ? id : `${id}_0`)), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams })); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getHiddenConversations.ts b/_libs/zca-js/src/apis/getHiddenConversations.ts new file mode 100644 index 0000000..69e5ddd --- /dev/null +++ b/_libs/zca-js/src/apis/getHiddenConversations.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetHiddenConversationsResponse = { + pin: string; + threads: { + /** + * 1: true, 0: false + */ + is_group: number; + thread_id: string; + }[]; +}; + +export const getHiddenConversationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/get-all`); + + /** + * Get hidden conversations + * + * @throws {ZaloApiError} + * + */ + return async function getHiddenConversations() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getLabels.ts b/_libs/zca-js/src/apis/getLabels.ts new file mode 100644 index 0000000..16e1ce8 --- /dev/null +++ b/_libs/zca-js/src/apis/getLabels.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { LabelData } from "../models/index.js"; + +export type GetLabelsResponse = { + labelData: LabelData[]; + version: number; + lastUpdateTime: number; +}; + +type RawResponseType = Omit & { + labelData: string; +}; + +export const getLabelsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/convlabel/get`); + + /** + * Get all labels + * + * @throws {ZaloApiError} + */ + return async function getLabels() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams })); + + return utils.resolve(response, (result) => { + const data = result.data as RawResponseType; + const formattedData: GetLabelsResponse = { + labelData: JSON.parse(data.labelData), + version: data.version, + lastUpdateTime: data.lastUpdateTime, + }; + return formattedData; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getListBoard.ts b/_libs/zca-js/src/apis/getListBoard.ts new file mode 100644 index 0000000..39a8459 --- /dev/null +++ b/_libs/zca-js/src/apis/getListBoard.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { NoteDetail, PinnedMessageDetail, PollDetail} from "../models/index.js"; +import { BoardType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type ListBoardOptions = { + /** + * Page number (default: 1) + */ + page?: number; + /** + * Number of items to retrieve (default: 20) + */ + count?: number; +}; + +export type BoardItem = { + boardType: BoardType; + data: PollDetail | NoteDetail | PinnedMessageDetail; +}; + +export type GetListBoardResponse = { + items: BoardItem[]; + count: number; +}; + +type RawListBoard = { + items: { + boardType: BoardType; + data: Record; + }[]; + count: number; +}; + +export const getListBoardFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/list`); + + /** + * Get list board items in a group + * + * @param options - The options for the request + * @param groupId - The ID of the group + * + * @throws {ZaloApiError} + * + */ + return async function getListBoard(options: ListBoardOptions, groupId: string) { + const requestParams = { + group_id: groupId, + board_type: 0, + page: options.page ?? 1, + count: options.count ?? 20, + last_id: 0, + last_type: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as RawListBoard; + data.items.forEach((item) => { + if (item.boardType != BoardType.Poll) { + const detailData = item.data; + if (typeof detailData.params === "string") { + detailData.params = JSON.parse(detailData.params); + } + } + }); + + return data as GetListBoardResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getListReminder.ts b/_libs/zca-js/src/apis/getListReminder.ts new file mode 100644 index 0000000..4b92d19 --- /dev/null +++ b/_libs/zca-js/src/apis/getListReminder.ts @@ -0,0 +1,88 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderGroup, ReminderUser} from "../models/index.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type ListReminderOptions = { + /** + * Page number (default: 1) + */ + page?: number; + /** + * Number of items to retrieve (default: 20) + */ + count?: number; +}; + +export type ReminderListUser = ReminderUser; +export type ReminderListGroup = ReminderGroup & { + groupId: string; + eventType: number; + responseMem: { + rejectMember: number; + myResp: number; + acceptMember: number; + }; + repeatInfo: { + list_ts: unknown[]; + }; + repeatData: unknown[]; +}; + +export type GetListReminderResponse = (ReminderListUser & ReminderListGroup)[]; + +export const getListReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/list`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/listReminder`), + }; + + /** + * Get list reminder + * + * @param options - The options for the request + * @param threadId - The ID of the thread + * @param type - The type of the thread (User or Group) + * + * @throws {ZaloApiError} + */ + return async function getListReminder( + options: ListReminderOptions, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + const requestParams = { + objectData: JSON.stringify( + type === ThreadType.User + ? { + uid: threadId, + board_type: 1, + page: options.page ?? 1, + count: options.count ?? 20, + last_id: 0, + last_type: 0, + } + : { + group_id: threadId, + board_type: 1, + page: options.page ?? 1, + count: options.count ?? 20, + last_id: 0, + last_type: 0, + }, + ), + ...(type === ThreadType.Group && { imei: ctx.imei }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL[type], { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + return JSON.parse(result.data as string) as GetListReminderResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getMultiUsersByPhones.ts b/_libs/zca-js/src/apis/getMultiUsersByPhones.ts new file mode 100644 index 0000000..345cf2e --- /dev/null +++ b/_libs/zca-js/src/apis/getMultiUsersByPhones.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type UserBasic } from "../models/index.js"; + +export type GetMultiUsersByPhonesResponse = { + [phoneNumber: string]: UserBasic; +}; + +export const getMultiUsersByPhonesFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/profile/multiget`); + + /** + * Get multiple user(s) by their phone numbers. + * + * @param phoneNumbers Phone number(s) + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function getMultiUsersByPhones(phoneNumbers: string | string[], avatarSize: AvatarSize = AvatarSize.Large) { + if (!phoneNumbers) throw new ZaloApiError("Missing phoneNumbers"); + if (!Array.isArray(phoneNumbers)) phoneNumbers = [phoneNumbers]; + + phoneNumbers = phoneNumbers.map((phone) => { + if (phone.startsWith("0")) { + if (ctx.language == "vi") phone = "84" + phone.slice(1); + } + return phone; + }); + + const params = { + phones: phoneNumbers, + avatar_size: avatarSize, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getMute.ts b/_libs/zca-js/src/apis/getMute.ts new file mode 100644 index 0000000..d83311f --- /dev/null +++ b/_libs/zca-js/src/apis/getMute.ts @@ -0,0 +1,40 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type MuteEntriesInfo = { + id: string; + duration: number; + startTime: number; + systemTime: number; + currentTime: number; + muteMode: number; +}; + +export type GetMuteResponse = { + chatEntries: MuteEntriesInfo[]; + groupChatEntries: MuteEntriesInfo[]; +}; + +export const getMuteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/getmute`); + + /** + * Get mute + * + * @throws {ZaloApiError} + */ + return async function getMute() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getOwnId.ts b/_libs/zca-js/src/apis/getOwnId.ts new file mode 100644 index 0000000..e6ad339 --- /dev/null +++ b/_libs/zca-js/src/apis/getOwnId.ts @@ -0,0 +1,5 @@ +import { apiFactory } from "../utils.js"; + +export const getOwnIdFactory = apiFactory()((_, ctx) => { + return () => ctx.uid; +}); diff --git a/_libs/zca-js/src/apis/getPendingGroupMembers.ts b/_libs/zca-js/src/apis/getPendingGroupMembers.ts new file mode 100644 index 0000000..51c9b0a --- /dev/null +++ b/_libs/zca-js/src/apis/getPendingGroupMembers.ts @@ -0,0 +1,43 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetPendingGroupMembersUserInfo = { + uid: string; + dpn: string; + avatar: string; + user_submit: null; +}; + +export type GetPendingGroupMembersResponse = { + time: number; + users: GetPendingGroupMembersUserInfo[]; +}; + +export const getPendingGroupMembersFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/pending-mems/list`); + + /** + * Get pending group members + * + * @param groupId - The id of the group to get the pending members + * + * @note Only the group leader and deputy group leader can view + * + * @throws {ZaloApiError} + */ + return async function getPendingGroupMembers(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getPinConversations.ts b/_libs/zca-js/src/apis/getPinConversations.ts new file mode 100644 index 0000000..2e013ed --- /dev/null +++ b/_libs/zca-js/src/apis/getPinConversations.ts @@ -0,0 +1,31 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetPinConversationsResponse = { + conversations: string[]; + version: number; +}; + +export const getPinConversationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/pinconvers/list`); + + /** + * Get pin conversations + * + * @throws {ZaloApiError} + */ + return async function getPinConversations() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getPollDetail.ts b/_libs/zca-js/src/apis/getPollDetail.ts new file mode 100644 index 0000000..62468d6 --- /dev/null +++ b/_libs/zca-js/src/apis/getPollDetail.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { PollDetail } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type PollDetailResponse = PollDetail; + +export const getPollDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/detail`); + + /** + * Get poll detail + * + * @param pollId Poll ID + * + * @throws {ZaloApiError} + */ + return async function getPollDetail(pollId: number) { + if (!pollId) throw new ZaloApiError("Missing poll id"); + + const params = { + poll_id: pollId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getProductCatalogList.ts b/_libs/zca-js/src/apis/getProductCatalogList.ts new file mode 100644 index 0000000..e7d3c55 --- /dev/null +++ b/_libs/zca-js/src/apis/getProductCatalogList.ts @@ -0,0 +1,58 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { ProductCatalogItem } from "../models/index.js"; + +export type GetProductCatalogListPayload = { + catalogId: string; + /** + * Number of items to retrieve (default: 100) + */ + limit?: number; + versionCatalog?: number; + lastProductId?: string; + /** + * Page number (default: 0) + */ + page?: number; +}; + +export type GetProductCatalogListResponse = { + items: ProductCatalogItem[]; + version: number; + has_more: number; +}; + +export const getProductCatalogListFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/list`); + + /** + * Get product catalog list? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function getProductCatalogList(payload: GetProductCatalogListPayload) { + const params = { + catalog_id: payload.catalogId, + limit: payload.limit ?? 100, + version_catalog: payload.versionCatalog ?? 0, + last_product_id: payload.lastProductId ?? -1, + page: payload.page ?? 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getQR.ts b/_libs/zca-js/src/apis/getQR.ts new file mode 100644 index 0000000..2302b9f --- /dev/null +++ b/_libs/zca-js/src/apis/getQR.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetQRResponse = { + [userId: string]: string; +}; + +export const getQRFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/mget-qr`); + + /** + * Get QR code for users + * + * @param userId User ID or list of user IDs + * + * @throws {ZaloApiError} + */ + return async function getQR(userId: string | string[]) { + if (typeof userId == "string") userId = [userId]; + + const params = { + fids: userId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getQuickMessageList.ts b/_libs/zca-js/src/apis/getQuickMessageList.ts new file mode 100644 index 0000000..9a168c2 --- /dev/null +++ b/_libs/zca-js/src/apis/getQuickMessageList.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { QuickMessage } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetQuickMessageListResponse = { + cursor: number; + version: number; + items: QuickMessage[]; +}; + +export const getQuickMessageListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/list`); + + /** + * Get quick message list + * + * @throws {ZaloApiError} + */ + return async function getQuickMessageList() { + const params = { + version: 0, + lang: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getRelatedFriendGroup.ts b/_libs/zca-js/src/apis/getRelatedFriendGroup.ts new file mode 100644 index 0000000..514d34f --- /dev/null +++ b/_libs/zca-js/src/apis/getRelatedFriendGroup.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetRelatedFriendGroupResponse = { + groupRelateds: { + [friendId: string]: string[]; + }; +}; + +export const getRelatedFriendGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/group/related`); + + /** + * Get related friend group + * + * @param friendId friend ids + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function getRelatedFriendGroup(friendId: string | string[]) { + const friendIds = Array.isArray(friendId) ? friendId : [friendId]; + + const params = { + friend_ids: JSON.stringify(friendIds), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getReminder.ts b/_libs/zca-js/src/apis/getReminder.ts new file mode 100644 index 0000000..a624b7a --- /dev/null +++ b/_libs/zca-js/src/apis/getReminder.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderGroup } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetReminderResponse = ReminderGroup; + +export const getReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/getReminder`); + + /** + * Get reminder details from a group + * + * @param reminderId Reminder ID + * + * @throws {ZaloApiError} + */ + return async function getReminder(reminderId: string) { + const params = { + eventId: reminderId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getReminderResponses.ts b/_libs/zca-js/src/apis/getReminderResponses.ts new file mode 100644 index 0000000..54bb8c5 --- /dev/null +++ b/_libs/zca-js/src/apis/getReminderResponses.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetReminderResponsesResponse = { + rejectMember: string[]; + acceptMember: string[]; +}; + +export const getReminderResponsesFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/listResponseEvent`); + + /** + * Get reminder responses + * + * @param reminderId reminder id + * + * @throws {ZaloApiError} + */ + return async function getReminderResponses(reminderId: string) { + const params = { + eventId: reminderId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getSentFriendRequest.ts b/_libs/zca-js/src/apis/getSentFriendRequest.ts new file mode 100644 index 0000000..af0748a --- /dev/null +++ b/_libs/zca-js/src/apis/getSentFriendRequest.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ZBusinessPackage } from "../models/ZBusiness.js"; +import { apiFactory } from "../utils.js"; + +export type SentFriendRequestInfo = { + userId: string; + zaloName: string; + displayName: string; + avatar: string; + globalId: string; + bizPkg: ZBusinessPackage; + fReqInfo: { + message: string; + src: number; + time: number; + }; +}; + +export type GetSentFriendRequestResponse = { + [userId: string]: SentFriendRequestInfo; +}; + +export const getSentFriendRequestFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/requested/list`); + + /** + * Get friend requested + * + * @note Zalo might throw an error with code 112 if you have no friend requests. + * + * @throws {ZaloApiError} + */ + return async function getSentFriendRequest() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getSettings.ts b/_libs/zca-js/src/apis/getSettings.ts new file mode 100644 index 0000000..4cf61c8 --- /dev/null +++ b/_libs/zca-js/src/apis/getSettings.ts @@ -0,0 +1,27 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { UserSetting } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetSettingsResponse = UserSetting; + +export const getSettingsFactory = apiFactory()((_api, _ctx, utils) => { + const serviceURL = utils.makeURL(`https://wpa.chat.zalo.me/api/setting/me`); + + /** + * Get my account settings + * + * @throws {ZaloApiError} + */ + return async function getSettings() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getStickerCategoryDetail.ts b/_libs/zca-js/src/apis/getStickerCategoryDetail.ts new file mode 100644 index 0000000..395948d --- /dev/null +++ b/_libs/zca-js/src/apis/getStickerCategoryDetail.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { StickerDetail } from "../models/index.js"; + +export type GetStickerCategoryDetailResponse = StickerDetail[]; + +export const getStickerCategoryDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker[0]}/api/message/sticker/category/sticker_detail`); + + /** + * Get sticker category detail + * + * @param cateId Sticker category ID + * + * @throws {ZaloApiError} + */ + return async function getStickerCategoryDetail(cateId: number) { + const params = { + cid: cateId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getStickers.ts b/_libs/zca-js/src/apis/getStickers.ts new file mode 100644 index 0000000..0fa97a6 --- /dev/null +++ b/_libs/zca-js/src/apis/getStickers.ts @@ -0,0 +1,57 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { StickerBasic } from "../models/index.js"; + +interface StickerSuggestions { + sugg_sticker: StickerBasic[] | null; + sugg_guggy: StickerBasic[] | null; + sugg_gif: StickerBasic[] | null; +} + +export const getStickersFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker}/api/message/sticker`); + + /** + * Get stickers by keyword + * + * @param keyword Keyword to search for + * @returns Sticker IDs + * + * @throws {ZaloApiError} + */ + return async function getStickers(keyword: string) { + if (!keyword) throw new ZaloApiError("Missing keyword"); + + const params = { + keyword: keyword, + gif: 1, + guggy: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const finalServiceUrl = new URL(serviceURL); + finalServiceUrl.pathname = finalServiceUrl.pathname + "/suggest/stickers"; + + const response = await utils.request( + utils.makeURL(finalServiceUrl.toString(), { + params: encryptedParams, + }), + ); + + return utils.resolve(response, (result) => { + const suggestions = result.data as StickerSuggestions; + const stickerIds: number[] = []; + + // @TODO: Implement these + // suggestions.sugg_guggy, suggestions.sugg_gif + if (suggestions.sugg_sticker) + suggestions.sugg_sticker.forEach((sticker) => stickerIds.push(sticker.sticker_id)); + + return stickerIds; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getStickersDetail.ts b/_libs/zca-js/src/apis/getStickersDetail.ts new file mode 100644 index 0000000..f41e956 --- /dev/null +++ b/_libs/zca-js/src/apis/getStickersDetail.ts @@ -0,0 +1,49 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory, resolveResponse } from "../utils.js"; + +import type { StickerDetail } from "../models/index.js"; + +export type StickerDetailResponse = StickerDetail[]; + +export const getStickersDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker}/api/message/sticker/sticker_detail`); + + /** + * Get stickers detail by sticker IDs + * + * @param stickerIds Sticker IDs to search for + * + * @throws {ZaloApiError} + */ + return async function getStickersDetail(stickerIds: number | number[]) { + if (!stickerIds) throw new ZaloApiError("Missing sticker id"); + if (!Array.isArray(stickerIds)) stickerIds = [stickerIds]; + if (stickerIds.length == 0) throw new ZaloApiError("Missing sticker id"); + + const stickers: StickerDetailResponse = []; + const tasks = stickerIds.map((stickerId) => getStickerDetail(stickerId)); + const tasksResult = await Promise.allSettled(tasks); + tasksResult.forEach((result) => { + if (result.status === "fulfilled") stickers.push(result.value); + }); + + return stickers; + }; + + async function getStickerDetail(stickerId: number) { + const params = { + sid: stickerId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + ); + + return resolveResponse(ctx, response); + } +}); diff --git a/_libs/zca-js/src/apis/getUnreadMark.ts b/_libs/zca-js/src/apis/getUnreadMark.ts new file mode 100644 index 0000000..b9f6a5e --- /dev/null +++ b/_libs/zca-js/src/apis/getUnreadMark.ts @@ -0,0 +1,50 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UnreadMark = { + id: number; + cliMsgId: number; + fromUid: number; + ts: number; +}; + +export type GetUnreadMarkResponse = { + data: { + convsGroup: UnreadMark[]; + convsUser: UnreadMark[]; + }; + status: number; +}; + +export const getUnreadMarkFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/getUnreadMark`); + + /** + * Get unread mark + * + * @throws {ZaloApiError} + * + */ + return async function getUnreadMark() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as { data: unknown; status: number }; + if (typeof data.data === "string") { + return { + data: JSON.parse(data.data) as GetUnreadMarkResponse["data"], + status: data.status, + }; + } + + return data as GetUnreadMarkResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getUserInfo.ts b/_libs/zca-js/src/apis/getUserInfo.ts new file mode 100644 index 0000000..d160836 --- /dev/null +++ b/_libs/zca-js/src/apis/getUserInfo.ts @@ -0,0 +1,58 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type User } from "../models/index.js"; + +export type ProfileInfo = User; + +export type UserInfoResponse = { + unchanged_profiles: Record; + phonebook_version: number; + changed_profiles: Record; +}; + +export const getUserInfoFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getprofiles/v2`); + + /** + * Get user info using user id + * + * @param userId User id(s) + * @param avatarSize Avatar size (default: AvatarSize.Small) + * + * @throws {ZaloApiError} + */ + return async function getUserInfo(userId: string | string[], avatarSize: AvatarSize = AvatarSize.Small) { + if (!userId) throw new ZaloApiError("Missing user id"); + + if (!Array.isArray(userId)) userId = [userId]; + + userId = userId.map((id) => { + if (id.split("_").length > 1) { + return id; + } + return `${id}_0`; + }); + + const params = { + phonebook_version: ctx.extraVer!.phonebook, + friend_pversion_map: userId, + avatar_size: avatarSize, + language: ctx.language, + show_online_status: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/inviteUserToGroups.ts b/_libs/zca-js/src/apis/inviteUserToGroups.ts new file mode 100644 index 0000000..68d3424 --- /dev/null +++ b/_libs/zca-js/src/apis/inviteUserToGroups.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type InviteUserToGroupsResponse = { + grid_message_map: { + [groupId: string]: { + error_code: number; + error_message: string; + data: string | null; + }; + }; +}; + +export const inviteUserToGroupsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/invite/multi`); + + /** + * Invite user to group + * + * @param groupId group ID(s) + * @param memberId member ID + * + * @throws {ZaloApiError} + * + */ + return async function inviteUserToGroups(userId: string, groupId: string | string[]) { + const params = { + grids: Array.isArray(groupId) ? groupId : [groupId], + member: userId, + memberType: -1, + srcInteraction: 2, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/joinGroupInviteBox.ts b/_libs/zca-js/src/apis/joinGroupInviteBox.ts new file mode 100644 index 0000000..1123e9c --- /dev/null +++ b/_libs/zca-js/src/apis/joinGroupInviteBox.ts @@ -0,0 +1,31 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type JoinGroupInviteBoxResponse = ""; + +export const joinGroupInviteBoxFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/join`); + + /** + * Join group invite box + * + * @param groupId - The group id + * + * @throws {ZaloApiError} + */ + return async function joinGroupInviteBox(groupId: string) { + const params = { + grid: groupId, + lang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/joinGroupLink.ts b/_libs/zca-js/src/apis/joinGroupLink.ts new file mode 100644 index 0000000..f9b2bb4 --- /dev/null +++ b/_libs/zca-js/src/apis/joinGroupLink.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type JoinGroupLinkResponse = ""; + +export const joinGroupLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/join`); + + /** + * Join group via invite link + * + * @note Zalo might throw an error with code 240 if the group enabled membership approval, 178 if you are already a member. + * + * @param link - The link join group + * + * @throws {ZaloApiError} + */ + return async function joinGroupLink(link: string) { + const params = { + link: link, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/keepAlive.ts b/_libs/zca-js/src/apis/keepAlive.ts new file mode 100644 index 0000000..a6e9ed4 --- /dev/null +++ b/_libs/zca-js/src/apis/keepAlive.ts @@ -0,0 +1,28 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type KeepAliveResponse = { config_vesion: number }; + +export const keepAliveFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.chat[0]}/keepalive`); + + /** + * Keep Alive? + * + * @throws {ZaloApiError} + */ + return async function keepAlive() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, undefined, false); + }; +}); diff --git a/_libs/zca-js/src/apis/lastOnline.ts b/_libs/zca-js/src/apis/lastOnline.ts new file mode 100644 index 0000000..2d6fca8 --- /dev/null +++ b/_libs/zca-js/src/apis/lastOnline.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type LastOnlineResponse = { + settings: { + show_online_status: boolean; + }; + lastOnline: number; +}; + +export const lastOnlineFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/lastOnline`); + + /** + * Get last online + * + * @param uid User ID + * + * @throws {ZaloApiError} + */ + return async function lastOnline(uid: string) { + const params = { + uid: uid, + conv_type: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/leaveGroup.ts b/_libs/zca-js/src/apis/leaveGroup.ts new file mode 100644 index 0000000..901c102 --- /dev/null +++ b/_libs/zca-js/src/apis/leaveGroup.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type LeaveGroupResponse = { + memberError: unknown[]; +}; + +export const leaveGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/leave`); + + /** + * Leave group + * + * @param groupId - groupId to leave + * @param silent - Turn on/off silent leave group + * + * @note Zalo might throw an error with code 166 if you are not a member of the group + * + * @throws {ZaloApiError} + */ + return async function leaveGroup(groupId: string, silent: boolean = false) { + const requestParams = { + grids: [groupId], // API only supports leaving one group at a time + imei: ctx.imei, + silent: silent ? 1 : 0, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/listen.ts b/_libs/zca-js/src/apis/listen.ts new file mode 100644 index 0000000..becd9dd --- /dev/null +++ b/_libs/zca-js/src/apis/listen.ts @@ -0,0 +1,544 @@ +import EventEmitter from "events"; +import WebSocket from "ws"; +import { type FriendEvent, initializeFriendEvent, type TFriendEvent } from "../models/FriendEvent.js"; +import { type GroupEvent, initializeGroupEvent, type TGroupEvent } from "../models/GroupEvent.js"; +import type { Message, TGroupMessage, TMessage, TReaction, Typing } from "../models/index.js"; +import { GroupMessage, UserMessage, Reaction, Undo, ThreadType, GroupTyping, UserTyping } from "../models/index.js"; +import { decodeEventData, getFriendEventType, getGroupEventType, hasOwn, logger, makeURL } from "../utils.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ContextSession } from "../context.js"; +import { type SeenMessage, GroupSeenMessage, UserSeenMessage } from "../models/SeenMessage.js"; +import { type DeliveredMessage, UserDeliveredMessage, GroupDeliveredMessage } from "../models/DeliveredMessage.js"; + +type UploadEventData = { + fileUrl: string; + fileId: string; +}; + +export type WsPayload> = { + version: number; + cmd: number; + subCmd: number; + data: T; +}; + +export type OnMessageCallback = (message: Message) => unknown; +export type OnClosedCallback = (code: CloseReason, reason: string) => unknown; +export type OnErrorCallback = (error: unknown) => unknown; + +export enum CloseReason { + ManualClosure = 1000, + AbnormalClosure = 1006, + DuplicateConnection = 3000, + KickConnection = 3003, +} + +interface ListenerEvents { + connected: []; + disconnected: [code: CloseReason, reason: string]; + closed: [code: CloseReason, reason: string]; + error: [error: unknown]; + typing: [typing: Typing]; + message: [message: Message]; + old_messages: [messages: Message[], type: ThreadType]; + seen_messages: [messages: SeenMessage[]]; + delivered_messages: [messages: DeliveredMessage[]]; + reaction: [reaction: Reaction]; + old_reactions: [reactions: Reaction[], isGroup: boolean]; + upload_attachment: [data: UploadEventData]; + undo: [data: Undo]; + friend_event: [data: FriendEvent]; + group_event: [data: GroupEvent]; + cipher_key: [key: string]; +} + +export class Listener extends EventEmitter { + private wsURL: string; + private cookie: string; + private userAgent: string; + + private ws: WebSocket | null; + private retryCount: Record< + string, + { + count: number; + max: number; + times: number[]; + } + >; + private rotateCount: number; + + private onConnectedCallback: () => unknown; + private onClosedCallback: OnClosedCallback; + private onErrorCallback: OnErrorCallback; + private onMessageCallback: OnMessageCallback; + private cipherKey?: string; + + private selfListen; + private pingInterval?: Timer; + + private id = 0; + + constructor( + private ctx: ContextSession, + private urls: string[], + ) { + super(); + if (!ctx.cookie) throw new ZaloApiError("Cookie is not available"); + if (!ctx.userAgent) throw new ZaloApiError("User agent is not available"); + + this.wsURL = makeURL(this.ctx, this.urls[0], { + t: Date.now(), + }); + this.retryCount = {}; + this.rotateCount = 0; + + for (const retry in ctx.settings.features.socket.retries) { + const { times, max } = ctx.settings.features.socket.retries[retry]; + this.retryCount[retry] = { + count: 0, + max: max || 0, + times: typeof times === "number" ? [times] : times, + }; + } + + this.cookie = ctx.cookie.getCookieStringSync("https://chat.zalo.me"); + this.userAgent = ctx.userAgent; + + this.selfListen = ctx.options.selfListen; + + this.ws = null; + + this.onConnectedCallback = () => {}; + this.onClosedCallback = () => {}; + this.onErrorCallback = () => {}; + this.onMessageCallback = () => {}; + } + + /** + * @deprecated Use `on` method instead + */ + public onConnected(cb: () => unknown) { + this.onConnectedCallback = cb; + } + + /** + * @deprecated Use `on` method instead + */ + public onClosed(cb: OnClosedCallback) { + this.onClosedCallback = cb; + } + + /** + * @deprecated Use `on` method instead + */ + public onError(cb: OnErrorCallback) { + this.onErrorCallback = cb; + } + + /** + * @deprecated Use `on` method instead + */ + public onMessage(cb: OnMessageCallback) { + this.onMessageCallback = cb; + } + + private canRetry(code: CloseReason) { + if (!this.ctx.settings.features.socket.close_and_retry_codes.includes(code)) return false; + if (this.retryCount[code.toString()].count >= this.retryCount[code.toString()].max) return false; + this.retryCount[code.toString()].count++; + + const { count, max, times } = this.retryCount[code.toString()]; + const retryTime = count - 1 < times.length ? times[count - 1] : times[times.length - 1]; + logger(this.ctx).verbose(`Retry for code ${code} in ${retryTime}ms (${count}/${max})`); + + return retryTime; + } + + private shouldRotate(code: CloseReason) { + if (!this.ctx.settings.features.socket.rotate_error_codes.includes(code)) return false; + if (this.rotateCount >= this.urls.length - 1) return false; + + return true; + } + + private rotateEndpoint() { + this.rotateCount++; + this.wsURL = makeURL(this.ctx, this.urls[this.rotateCount], { + t: Date.now(), + }); + logger(this.ctx).verbose(`Rotating endpoint to ${this.wsURL}`); + } + + public start({ retryOnClose = false }: { retryOnClose?: boolean } = {}) { + if (this.ws) throw new ZaloApiError("Already started"); + + const ws = new WebSocket(this.wsURL, { + headers: { + "accept-encoding": "gzip, deflate, br, zstd", + "accept-language": "en-US,en;q=0.9", + "cache-control": "no-cache", + connection: "Upgrade", + host: new URL(this.wsURL).host, + origin: "https://chat.zalo.me", + prgama: "no-cache", + "sec-websocket-extensions": "permessage-deflate; client_max_window_bits", + "sec-websocket-version": "13", + upgrade: "websocket", + "user-agent": this.userAgent, + cookie: this.cookie, + }, + agent: this.ctx.options.agent + }); + this.ws = ws; + + ws.onopen = () => { + this.onConnectedCallback(); + this.emit("connected"); + }; + + ws.onclose = (event) => { + this.reset(); + this.emit("disconnected", event.code as CloseReason, event.reason); + const retry = retryOnClose && this.canRetry(event.code as CloseReason); + if (retry && retryOnClose) { + const shouldRotate = this.shouldRotate(event.code as CloseReason); + if (shouldRotate) { + this.rotateEndpoint(); + } + setTimeout(() => { + this.start({ retryOnClose: true }); + }, retry); + } else { + this.onClosedCallback(event.code, event.reason); + this.emit("closed", event.code as CloseReason, event.reason); + } + }; + + ws.onerror = (event) => { + this.onErrorCallback(event); + this.emit("error", event); + }; + + ws.onmessage = async (event) => { + const { data } = event; + if (!(data instanceof Buffer)) return; + + const encodedHeader = data.subarray(0, 4); + const [version, cmd, subCmd] = getHeader(encodedHeader); + + try { + const dataToDecode = data.subarray(4); + const decodedData = new TextDecoder("utf-8").decode(dataToDecode); + if (decodedData.length == 0) return; + + const parsed = JSON.parse(decodedData); + + if (version == 1 && cmd == 1 && subCmd == 1 && hasOwn(parsed, "key")) { + this.cipherKey = parsed.key; + this.emit("cipher_key", parsed.key); + + if (this.pingInterval) clearInterval(this.pingInterval); + + const ping = () => { + const payload = { + version: 1, + cmd: 2, + subCmd: 1, + data: { eventId: Date.now() }, + }; + this.sendWs(payload, false); + }; + + this.pingInterval = setInterval(() => { + ping(); + }, this.ctx.settings.features.socket.ping_interval); + } + + if (version == 1 && cmd == 501 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { msgs } = parsedData; + for (const msg of msgs) { + if (typeof msg.content == "object" && hasOwn(msg.content, "deleteMsg")) { + const undoObject = new Undo(this.ctx.uid, msg, false); + if (undoObject.isSelf && !this.selfListen) continue; + this.emit("undo", undoObject); + } else { + const messageObject = new UserMessage(this.ctx.uid, msg); + if (messageObject.isSelf && !this.selfListen) continue; + this.onMessageCallback(messageObject); + this.emit("message", messageObject); + } + } + } + + if (version == 1 && cmd == 521 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { groupMsgs } = parsedData; + for (const msg of groupMsgs) { + if (typeof msg.content == "object" && hasOwn(msg.content, "deleteMsg")) { + const undoObject = new Undo(this.ctx.uid, msg, true); + if (undoObject.isSelf && !this.selfListen) continue; + this.emit("undo", undoObject); + } else { + const messageObject = new GroupMessage(this.ctx.uid, msg); + if (messageObject.isSelf && !this.selfListen) continue; + this.onMessageCallback(messageObject); + this.emit("message", messageObject); + } + } + } + + if (version == 1 && cmd == 601 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { controls } = parsedData; + for (const control of controls) { + if (control.content.act_type == "file_done") { + const data = { + fileUrl: control.content.data.url, + fileId: control.content.fileId, + }; + + const uploadCallback = this.ctx.uploadCallbacks.get(String(control.content.fileId)); + if (uploadCallback) uploadCallback(data); + this.ctx.uploadCallbacks.delete(String(control.content.fileId)); + + this.emit("upload_attachment", data); + } else if (control.content.act_type == "group") { + // 31/08/2024 + // for some reason, Zalo send both join and join_reject event when admin approve join requests + // Zalo itself doesn't seem to handle this properly either, so we gonna ignore the join_reject event + + if (control.content.act == "join_reject") continue; + + const groupEventData: TGroupEvent = + typeof control.content.data == "string" + ? JSON.parse(control.content.data) + : control.content.data; + + const groupEvent = initializeGroupEvent( + this.ctx.uid, + groupEventData, + getGroupEventType(control.content.act), + control.content.act + ); + if (groupEvent.isSelf && !this.selfListen) continue; + this.emit("group_event", groupEvent); + } else if (control.content.act_type == "fr") { + // 28/02/2025 + // Zalo send both req and req_v2 event when user send friend request + // Zalo itself doesn't seem to handle this properly either, so we gonna ignore the req event + + if (control.content.act == "req") continue; + + const friendEventData: TFriendEvent = + typeof control.content.data == "string" + ? JSON.parse(control.content.data) + : control.content.data; + + // Handles the case when act is "pin_create" and params is a string + if ( + typeof friendEventData == "object" && + "topic" in friendEventData && + typeof friendEventData.topic == "object" && + "params" in friendEventData.topic + ) { + friendEventData.topic.params = JSON.parse(`${friendEventData.topic.params}`); + } + + const friendEvent = initializeFriendEvent( + this.ctx.uid, + typeof friendEventData == "number" ? control.content.data : friendEventData, + getFriendEventType(control.content.act), + ); + if (friendEvent.isSelf && !this.selfListen) continue; + this.emit("friend_event", friendEvent); + } + } + } + + if (cmd == 612) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { reacts, reactGroups } = parsedData; + + for (const react of reacts) { + react.content = JSON.parse(react.content); + const reactionObject = new Reaction(this.ctx.uid, react, false); + if (reactionObject.isSelf && !this.selfListen) continue; + + this.emit("reaction", reactionObject); + } + + for (const reactGroup of reactGroups) { + reactGroup.content = JSON.parse(reactGroup.content); + const reactionObject = new Reaction(this.ctx.uid, reactGroup, true); + if (reactionObject.isSelf && !this.selfListen) continue; + + this.emit("reaction", reactionObject); + } + } + + if (cmd == 610 || cmd == 611) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const isGroup = cmd == 611; + + const reacts = parsedData[isGroup ? "reactGroups" : "reacts"] as TReaction[]; + const reactionObjects = reacts.map((react) => new Reaction(this.ctx.uid, react, isGroup)); + + this.emit("old_reactions", reactionObjects, isGroup); + } + + if (cmd == 510 && subCmd == 1) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const msgs = parsedData.msgs as TMessage[]; + const responseMsgs = msgs.map((msg) => new UserMessage(this.ctx.uid, msg)); + this.emit("old_messages", responseMsgs, ThreadType.User); + } + + if (cmd == 511 && subCmd == 1) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const groupMsgs = parsedData.groupMsgs as TGroupMessage[]; + const responseMsgs = groupMsgs.map((msg) => new GroupMessage(this.ctx.uid, msg)); + this.emit("old_messages", responseMsgs, ThreadType.Group); + } + + if (cmd == 602 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { actions } = parsedData; + + for (const action of actions) { + if (action.act_type == "typing") { + const data = JSON.parse(`{${action.data}}`); + if (action.act == "typing") { + const typingObject = new UserTyping(data); + this.emit("typing", typingObject); + } else if (action.act == "gtyping") { + // 26/02/2025 + // For a group with only two people, Zalo doesn't send a typing event. + + const typingObject = new GroupTyping(data); + this.emit("typing", typingObject); + } + } + } + } + + if (cmd == 502 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { delivereds: deliveredMsgs, seens: seenMsgs } = parsedData; + + if (Array.isArray(deliveredMsgs) && deliveredMsgs.length > 0) { + const deliveredObjects = deliveredMsgs.map((delivered) => new UserDeliveredMessage(delivered)); + this.emit("delivered_messages", deliveredObjects); + } + + if (Array.isArray(seenMsgs) && seenMsgs.length > 0) { + const seenObjects = seenMsgs.map((seen) => new UserSeenMessage(seen)); + this.emit("seen_messages", seenObjects); + } + } + + if (cmd == 522 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { delivereds: deliveredMsgs, groupSeens: groupSeenMsgs } = parsedData; + + if (Array.isArray(deliveredMsgs) && deliveredMsgs.length > 0) { + let deliveredObjects = deliveredMsgs.map( + (delivered) => new GroupDeliveredMessage(this.ctx.uid, delivered), + ); + if (!this.selfListen) + deliveredObjects = deliveredObjects.filter((delivered) => !delivered.isSelf); + this.emit("delivered_messages", deliveredObjects); + } + + if (Array.isArray(groupSeenMsgs) && groupSeenMsgs.length > 0) { + let seenObjects = groupSeenMsgs.map((seen) => new GroupSeenMessage(this.ctx.uid, seen)); + if (!this.selfListen) seenObjects = seenObjects.filter((seen) => !seen.isSelf); + this.emit("seen_messages", seenObjects); + } + } + + if (version == 1 && cmd == 3000 && subCmd == 0) { + logger(this.ctx).error(); + logger(this.ctx).error("Another connection is opened, closing this one"); + logger(this.ctx).error(); + if (ws.readyState !== WebSocket.CLOSED) ws.close(CloseReason.DuplicateConnection); + } + } catch (error) { + this.onErrorCallback(error); + this.emit("error", error); + } + }; + } + + public stop() { + if (this.ws) { + this.ws.close(CloseReason.ManualClosure); + this.reset(); + } + } + + public sendWs(payload: WsPayload, requireId: boolean = true) { + if (this.ws) { + if (requireId) payload.data["req_id"] = `req_${this.id++}`; + + const encodedData = new TextEncoder().encode(JSON.stringify(payload.data)); + const dataLength = encodedData.length; + + const data = new DataView(Buffer.alloc(4 + dataLength).buffer); + data.setUint8(0, payload.version); + data.setInt32(1, payload.cmd, true); + data.setInt8(3, payload.subCmd); + + encodedData.forEach((e, i) => { + data.setUint8(4 + i, e); + }); + + this.ws.send(data); + } + } + + /** + * Request old messages + * + * @param lastMsgId + */ + public requestOldMessages(threadType: ThreadType, lastMsgId: string | null = null) { + const payload = { + version: 1, + cmd: threadType === ThreadType.User ? 510 : 511, + subCmd: 1, + data: { first: true, lastId: lastMsgId, preIds: [] }, + }; + this.sendWs(payload); + } + + /** + * Request old messages + * + * @param lastMsgId + */ + public requestOldReactions(threadType: ThreadType, lastMsgId: string | null = null) { + const payload = { + version: 1, + cmd: threadType === ThreadType.User ? 610 : 611, + subCmd: 1, + data: { first: true, lastId: lastMsgId, preIds: [] }, + }; + this.sendWs(payload); + } + + private reset() { + this.ws = null; + this.cipherKey = undefined; + if (this.pingInterval) clearInterval(this.pingInterval); + } +} + +function getHeader(buffer: Buffer) { + if (buffer.byteLength < 4) { + throw new ZaloApiError("Invalid header"); + } + + return [buffer[0], buffer.readUInt16LE(1), buffer[3]]; +} diff --git a/_libs/zca-js/src/apis/lockPoll.ts b/_libs/zca-js/src/apis/lockPoll.ts new file mode 100644 index 0000000..205ed90 --- /dev/null +++ b/_libs/zca-js/src/apis/lockPoll.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type LockPollResponse = ""; + +export const lockPollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/end`); + + /** + * Lock a poll, preventing further votes. + * + * @param pollId Poll id to lock + * + * @throws {ZaloApiError} + */ + return async function lockPoll(pollId: number) { + const params = { + poll_id: pollId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/login.ts b/_libs/zca-js/src/apis/login.ts new file mode 100644 index 0000000..78cb342 --- /dev/null +++ b/_libs/zca-js/src/apis/login.ts @@ -0,0 +1,118 @@ +import type { ContextBase } from "../context.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { decryptResp, getSignKey, logger, makeURL, ParamsEncryptor, request } from "../utils.js"; + +export async function login(ctx: ContextBase, encryptParams: boolean) { + const encryptedParams = await getEncryptParam(ctx, encryptParams, "getlogininfo"); + + try { + const response = await request( + ctx, + makeURL(ctx, "https://wpa.chat.zalo.me/api/login/getLoginInfo", { + ...encryptedParams.params, + nretry: 0, + }), + ); + if (!response.ok) throw new ZaloApiError("Failed to fetch login info: " + response.statusText); + const data = await response.json(); + + if (encryptedParams.enk) { + const decryptedData = decryptResp(encryptedParams.enk, data.data); + return decryptedData != null && typeof decryptedData != "string" ? decryptedData : null; + } + + return null; + } catch (error) { + logger(ctx).error("Login failed:", error); + throw error; + } +} + +export async function getServerInfo(ctx: ContextBase, encryptParams: boolean) { + const encryptedParams = await getEncryptParam(ctx, encryptParams, "getserverinfo"); + if (!encryptedParams.params.signkey || typeof encryptedParams.params.signkey !== "string") + throw new ZaloApiError("Missing signkey"); + + const response = await request( + ctx, + makeURL( + ctx, + "https://wpa.chat.zalo.me/api/login/getServerInfo", + { + imei: ctx.imei as string, + type: ctx.API_TYPE, + client_version: ctx.API_VERSION, + computer_name: "Web", + signkey: encryptedParams.params.signkey, + }, + false, + ), + ); + if (!response.ok) throw new ZaloApiError("Failed to fetch server info: " + response.statusText); + const data = await response.json(); + + if (data.data == null) throw new ZaloApiError("Failed to fetch server info: " + data.error_message); + return data.data; +} + +async function getEncryptParam(ctx: ContextBase, encryptParams: boolean, type: string) { + const params = {} as Record; + const data = { + computer_name: "Web", + imei: ctx.imei!, + language: ctx.language!, + ts: Date.now(), + }; + const encryptedData = await _encryptParam(ctx, data, encryptParams); + + if (encryptedData == null) Object.assign(params, data); + else { + const { encrypted_params, encrypted_data } = encryptedData; + Object.assign(params, encrypted_params); + params.params = encrypted_data; + } + + params.type = ctx.API_TYPE; + params.client_version = ctx.API_VERSION; + params.signkey = + type == "getserverinfo" + ? getSignKey(type, { + imei: ctx.imei, + type: ctx.API_TYPE, + client_version: ctx.API_VERSION, + computer_name: "Web", + }) + : getSignKey(type, params); + + return { + params, + enk: encryptedData ? encryptedData.enk : null, + }; +} + +async function _encryptParam(ctx: ContextBase, data: Record, encryptParams: boolean) { + if (encryptParams) { + const encryptor = new ParamsEncryptor({ + type: ctx.API_TYPE, + imei: data.imei as string, + firstLaunchTime: Date.now(), + }); + try { + const stringifiedData = JSON.stringify(data); + const encryptedKey = encryptor.getEncryptKey(); + const encodedData = ParamsEncryptor.encodeAES(encryptedKey, stringifiedData, "base64", false); + const params = encryptor.getParams(); + + return params + ? { + encrypted_data: encodedData, + encrypted_params: params, + enk: encryptedKey, + } + : null; + } catch (error) { + throw new ZaloApiError("Failed to encrypt params: " + error); + } + } + return null; +} diff --git a/_libs/zca-js/src/apis/loginQR.ts b/_libs/zca-js/src/apis/loginQR.ts new file mode 100644 index 0000000..ae90f04 --- /dev/null +++ b/_libs/zca-js/src/apis/loginQR.ts @@ -0,0 +1,529 @@ +import { CookieJar, type SerializedCookie, type SerializedCookieJar } from "tough-cookie"; + +import { writeFile } from "node:fs/promises"; +import type { ContextBase } from "../context.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { logger, request } from "../utils.js"; +import { ZaloApiLoginQRAborted } from "../Errors/ZaloApiLoginQRAborted.js"; +import { ZaloApiLoginQRDeclined } from "../Errors/ZaloApiLoginQRDeclined.js"; + +export enum LoginQRCallbackEventType { + QRCodeGenerated, + QRCodeExpired, + QRCodeScanned, + QRCodeDeclined, + GotLoginInfo, +} + +export type LoginQRCallbackEvent = + | { + type: LoginQRCallbackEventType.QRCodeGenerated; + data: { + code: string; + image: string; + options: { + enabledCheckOCR: boolean; + enabledMultiLayer: boolean; + }; + token: string; + }; + actions: { + saveToFile: (qrPath?: string) => Promise; + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.QRCodeExpired; + data: null; + actions: { + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.QRCodeScanned; + data: { + avatar: string; + display_name: string; + }; + actions: { + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.QRCodeDeclined; + data: { + code: string; + }; + actions: { + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.GotLoginInfo; + data: { + cookie: SerializedCookie[]; + imei: string; + userAgent: string; + }; + actions: null; + }; + +export type LoginQRCallback = (event: LoginQRCallbackEvent) => unknown; + +async function loadLoginPage(ctx: ContextBase) { + const response = await request(ctx, "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", { + headers: { + accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "cache-control": "max-age=0", + priority: "u=0, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "document", + "sec-fetch-mode": "navigate", + "sec-fetch-site": "same-site", + "sec-fetch-user": "?1", + "upgrade-insecure-requests": "1", + Referer: "https://chat.zalo.me/", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + method: "GET", + }); + + const html = await response.text(); + + const regex = /https:\/\/stc-zlogin\.zdn\.vn\/main-([\d.]+)\.js/; + const match = html.match(regex); + + return match?.[1]; +} + +async function getLoginInfo(ctx: ContextBase, version: string) { + const form = new URLSearchParams(); + form.append("continue", "https://zalo.me/pc"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/logininfo", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fzalo.me%2Fpc", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +async function verifyClient(ctx: ContextBase, version: string) { + const form = new URLSearchParams(); + form.append("type", "device"); + form.append("continue", "https://zalo.me/pc"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/verify-client", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fzalo.me%2Fpc", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +async function generate( + ctx: ContextBase, + version: string, +): Promise<{ + data: { + code: string; + image: string; + options: { + enabledCheckOCR: boolean; + enabledMultiLayer: boolean; + }; + token: string; + } | null; + error_code: number; + error_message: string; +}> { + const form = new URLSearchParams(); + form.append("continue", "https://zalo.me/pc"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/authen/qr/generate", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fzalo.me%2Fpc", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +async function saveQRCodeToFile(filepath: string, imageData: string) { + await writeFile(filepath, imageData, "base64"); +} + +async function waitingScan( + ctx: ContextBase, + version: string, + code: string, + signal: AbortSignal, +): Promise< + | { + data: { + avatar: string; + display_name: string; + } | null; + error_code: number; + error_message: string; + } + | undefined +> { + const form = new URLSearchParams(); + form.append("code", code); + form.append("continue", "https://chat.zalo.me/"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/authen/qr/waiting-scan", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + signal, + }) + .then((res) => res.json()) + .then((data) => { + if (data.error_code == 8) { + return waitingScan(ctx, version, code, signal); + } + + return data; + }) + .catch((e) => { + if (!signal.aborted) logger(ctx).error(e); + }); +} + +async function waitingConfirm( + ctx: ContextBase, + version: string, + code: string, + signal: AbortSignal, +): Promise< + | { + data: null; + error_code: number; + error_message: string; + } + | undefined +> { + const form = new URLSearchParams(); + form.append("code", code); + form.append("gToken", ""); + form.append("gAction", "CONFIRM_QR"); + form.append("continue", "https://chat.zalo.me/"); + form.append("v", version); + + logger(ctx).info("Please confirm on your phone"); + + return await request(ctx, "https://id.zalo.me/account/authen/qr/waiting-confirm", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + signal, + }) + .then((res) => res.json()) + .then((data) => { + if (data.error_code == 8) { + return waitingConfirm(ctx, version, code, signal); + } + + return data; + }) + .catch((e) => { + if (!signal.aborted) logger(ctx).error(e); + }); +} + +async function checkSession(ctx: ContextBase) { + return await request( + ctx, + "https://id.zalo.me/account/checksession?continue=https%3A%2F%2Fchat.zalo.me%2Findex.html", + { + headers: { + accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + priority: "u=0, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "document", + "sec-fetch-mode": "navigate", + "sec-fetch-site": "same-origin", + "upgrade-insecure-requests": "1", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + redirect: "manual", + method: "GET", + }, + ).catch(logger(ctx).error); +} + +async function getUserInfo(ctx: ContextBase): Promise< + | { + data: { + logged: boolean; + session_chat_valid: boolean; + info: { + name: string; + avatar: string; + }; + } | null; + error_code: number; + error_message: string; + } + | undefined +> { + return await request(ctx, "https://jr.chat.zalo.me/jr/userinfo", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-site", + Referer: "https://chat.zalo.me/", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + method: "GET", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +export async function loginQR( + ctx: ContextBase, + options: { userAgent: string; qrPath?: string }, + callback?: LoginQRCallback, +) { + ctx.cookie = new CookieJar(); + ctx.userAgent = options.userAgent; + + return new Promise<{ + userInfo: { + name: string; + avatar: string; + }; + cookies: SerializedCookieJar["cookies"]; + // @TODO + // eslint-disable-next-line + } | null>(async (resolve, reject) => { + const controller = new AbortController(); + let qrTimeout: NodeJS.Timer | null = null; + + function cleanUp() { + controller.abort(); + if (qrTimeout) { + clearTimeout(qrTimeout); + qrTimeout = null; + } + } + + try { + function retry() { + cleanUp(); + return resolve(loginQR(ctx, options, callback)); + } + + function abort() { + cleanUp(); + return reject(new ZaloApiLoginQRAborted()); + } + + if (ctx.options.logging) console.log(); + + const loginVersion = await loadLoginPage(ctx); + if (!loginVersion) throw new ZaloApiError("Cannot get API login version"); + + logger(ctx).info("Got login version:", loginVersion); + + await getLoginInfo(ctx, loginVersion); + await verifyClient(ctx, loginVersion); + const qrGenResult = await generate(ctx, loginVersion); + if (!qrGenResult || !qrGenResult.data) + throw new ZaloApiError(`Unable to generate QRCode\nResponse: ${JSON.stringify(qrGenResult, null, 2)}`); + + const qrData = qrGenResult.data; + + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeGenerated, + data: { + ...qrGenResult.data, + image: qrGenResult.data.image.replace(/^data:image\/png;base64,/, ""), + }, + actions: { + async saveToFile(qrPath = options.qrPath ?? "qr.png") { + await saveQRCodeToFile(qrPath, qrData.image.replace(/^data:image\/png;base64,/, "")); + logger(ctx).info("Scan the QR code at", `'${qrPath}'`, "to proceed with login"); + }, + retry, + abort, + }, + }); + } else { + const qrPath = options.qrPath ?? "qr.png"; + + await saveQRCodeToFile(qrPath, qrData.image.replace(/^data:image\/png;base64,/, "")); + logger(ctx).info("Scan the QR code at", `'${qrPath}'`, "to proceed with login"); + } + + qrTimeout = setTimeout(() => { + cleanUp(); + + logger(ctx).info("QR expired!"); + + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeExpired, + data: null, + actions: { + retry, + abort, + }, + }); + } else { + retry(); + } + }, 100_000); + + const scanResult = await waitingScan(ctx, loginVersion, qrGenResult.data.code, controller.signal); + if (!scanResult || !scanResult.data) throw new ZaloApiError("Cannot get scan result"); + + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeScanned, + data: scanResult.data, + actions: { + retry, + abort, + }, + }); + } + + const confirmResult = await waitingConfirm(ctx, loginVersion, qrGenResult.data.code, controller.signal); + if (!confirmResult) throw new ZaloApiError("Cannot get confirm result"); + + clearTimeout(qrTimeout); + + if (confirmResult.error_code == -13) { + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeDeclined, + data: { + code: qrData.code, + }, + actions: { + retry, + abort, + }, + }); + } else { + logger(ctx).error("QRCode login declined"); + throw new ZaloApiLoginQRDeclined(); + } + + return; + } else if (confirmResult.error_code != 0) { + throw new ZaloApiError(`An error has occurred.\nResponse: ${JSON.stringify(confirmResult, null, 2)}`); + } + + const checkSessionResult = await checkSession(ctx); + if (!checkSessionResult) throw new ZaloApiError("Cannot get session, login failed"); + + logger(ctx).info("Successfully logged into the account", scanResult.data.display_name); + + const userInfo = await getUserInfo(ctx); + if (!userInfo || !userInfo.data) throw new ZaloApiError("Can't get account info"); + if (!userInfo.data.logged) throw new ZaloApiError("Can't login"); + + resolve({ + cookies: ctx.cookie!.toJSON()!.cookies, + userInfo: userInfo.data.info, + }); + } catch (error) { + cleanUp(); + reject(error); + } + }); +} diff --git a/_libs/zca-js/src/apis/parseLink.ts b/_libs/zca-js/src/apis/parseLink.ts new file mode 100644 index 0000000..808763e --- /dev/null +++ b/_libs/zca-js/src/apis/parseLink.ts @@ -0,0 +1,53 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ParseLinkErrorMaps = Record; + +export type ParseLinkResponse = { + data: { + thumb: string; + title: string; + desc: string; + src: string; + href: string; + media: { + type: number; + count: number; + mediaTitle: string; + artist: string; + streamUrl: string; + stream_icon: string; + }; + stream_icon: string; + }; + error_maps: ParseLinkErrorMaps; +}; + +export const parseLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/parselink`); + + /** + * Parse link + * + * @param link link to parse + * + * @throws {ZaloApiError} + * + */ + return async function parseLink(link: string) { + const params = { + link, + version: 1, // version 0 is not available errorMaps || version 1 is errorMaps (for response) + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/rejectFriendRequest.ts b/_libs/zca-js/src/apis/rejectFriendRequest.ts new file mode 100644 index 0000000..045c4e9 --- /dev/null +++ b/_libs/zca-js/src/apis/rejectFriendRequest.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RejectFriendRequestResponse = ""; + +export const rejectFriendRequestFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/reject`); + + /** + * Reject a friend request from a User + * + * @param friendId + * + * @throws {ZaloApiError} + */ + return async function rejectFriendRequest(friendId: string) { + const params = { + fid: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeFriend.ts b/_libs/zca-js/src/apis/removeFriend.ts new file mode 100644 index 0000000..046b026 --- /dev/null +++ b/_libs/zca-js/src/apis/removeFriend.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveFriendResponse = ""; + +export const removeFriendFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/remove`); + + /** + * Remove friend + * + * @param friendId - ID of the friend to remove + * + * @throws {ZaloApiError} + */ + return async function removeFriend(friendId: string) { + const params = { + fid: friendId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeFriendAlias.ts b/_libs/zca-js/src/apis/removeFriendAlias.ts new file mode 100644 index 0000000..f1059e3 --- /dev/null +++ b/_libs/zca-js/src/apis/removeFriendAlias.ts @@ -0,0 +1,30 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveFriendAliasResponse = ""; + +export const removeFriendAliasFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/remove`); + + /** + * Remove friend's alias + * + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function removeFriendAlias(friendId: string) { + const params = { + friendId: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeGroupBlockedMember.ts b/_libs/zca-js/src/apis/removeGroupBlockedMember.ts new file mode 100644 index 0000000..a02ed4d --- /dev/null +++ b/_libs/zca-js/src/apis/removeGroupBlockedMember.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveGroupBlockedMemberResponse = ""; + +export const removeGroupBlockedMemberFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/blockedmems/remove`); + + /** + * Remove group blocked member + * + * @param memberId member id(s) + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function removeGroupBlockedMember(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeGroupDeputy.ts b/_libs/zca-js/src/apis/removeGroupDeputy.ts new file mode 100644 index 0000000..8f4658f --- /dev/null +++ b/_libs/zca-js/src/apis/removeGroupDeputy.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveGroupDeputyResponse = ""; + +export const removeGroupDeputyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/admins/remove`); + + /** + * Remove group deputy + * + * @param memberId user Id or list of user Ids + * @param groupId group Id + * + * @throws {ZaloApiError} + * + */ + return async function removeGroupDeputy(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeQuickMessage.ts b/_libs/zca-js/src/apis/removeQuickMessage.ts new file mode 100644 index 0000000..056e77c --- /dev/null +++ b/_libs/zca-js/src/apis/removeQuickMessage.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveQuickMessageResponse = { + itemIds: number[]; + version: number; +}; + +export const removeQuickMessageFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/delete`); + + /** + * Remove quick message + * + * @param itemIds - The id(s) of the quick message(s) to remove (number or number[]) + * + * @note Zalo might throw an error with code 212 if the item does not exist + * + * @throws {ZaloApiError} + */ + return async function removeQuickMessage(itemIds: number | number[]) { + const idsArray = Array.isArray(itemIds) ? itemIds : [itemIds]; + + const params = { + itemIds: idsArray, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeReminder.ts b/_libs/zca-js/src/apis/removeReminder.ts new file mode 100644 index 0000000..df822b2 --- /dev/null +++ b/_libs/zca-js/src/apis/removeReminder.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveReminderResponse = "" | number; + +export const removeReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/remove`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/remove`), + }; + + /** + * Remove a reminder in a (user/group) + * + * @param reminderId Reminder ID to remove reminder from + * @param threadId (User/Group) ID to remove reminder from + * @param type Thread type (User or Group) + * + * @throws {ZaloApiError} + */ + return async function removeReminder(reminderId: string, threadId: string, type: ThreadType = ThreadType.User) { + const params = + type === ThreadType.User + ? { + uid: threadId, + reminderId: reminderId, + } + : { + grid: threadId, + topicId: reminderId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeUnreadMark.ts b/_libs/zca-js/src/apis/removeUnreadMark.ts new file mode 100644 index 0000000..c1a5729 --- /dev/null +++ b/_libs/zca-js/src/apis/removeUnreadMark.ts @@ -0,0 +1,63 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveUnreadMarkResponse = { + data: { + updateId: number; + }; + status: number; +}; + +export const removeUnreadMarkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/removeUnreadMark`); + + /** + * Remove unread mark from conversation + * + * @param threadId Thread ID + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function removeUnreadMark(threadId: string, type: ThreadType = ThreadType.User) { + const timestamp = Date.now(); + + const isGroup = type === ThreadType.Group; + const requestParams = { + param: JSON.stringify({ + [isGroup ? "convsGroup" : "convsUser"]: [threadId], + [isGroup ? "convsUser" : "convsGroup"]: [], + [isGroup ? "convsGroupData" : "convsUserData"]: [ + { + id: threadId, + ts: timestamp, + }, + ], + [isGroup ? "convsUserData" : "convsGroupData"]: [], + }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + const data = result.data as { data: unknown; status: number }; + if (typeof data.data === "string") { + return { + data: JSON.parse(data.data), + status: data.status, + }; + } + + return result.data as RemoveUnreadMarkResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/removeUserFromGroup.ts b/_libs/zca-js/src/apis/removeUserFromGroup.ts new file mode 100644 index 0000000..d8f0129 --- /dev/null +++ b/_libs/zca-js/src/apis/removeUserFromGroup.ts @@ -0,0 +1,42 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveUserFromGroupResponse = { + errorMembers: string[]; +}; + +export const removeUserFromGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/kickout`); + + /** + * Remove user from existing group + * + * @param memberId User ID or list of user IDs to remove + * @param groupId Group ID + * + * @note Zalo might throw an error with code 165 if the user is not in the group, 166 if you don't have enough permissions or is not in the group + * + * @throws {ZaloApiError} + */ + return async function removeUserFromGroup(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/resetHiddenConversPin.ts b/_libs/zca-js/src/apis/resetHiddenConversPin.ts new file mode 100644 index 0000000..31a1356 --- /dev/null +++ b/_libs/zca-js/src/apis/resetHiddenConversPin.ts @@ -0,0 +1,26 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ResetHiddenConversPinResponse = ""; + +export const resetHiddenConversPinFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/reset`); + + /** + * Reset hidden conversation pin + * + * @throws {ZaloApiError} + */ + return async function resetHiddenConversPin() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/reuseAvatar.ts b/_libs/zca-js/src/apis/reuseAvatar.ts new file mode 100644 index 0000000..a0ba850 --- /dev/null +++ b/_libs/zca-js/src/apis/reuseAvatar.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ReuseAvatarResponse = null; + +export const reuseAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/reuse-avatar`); + + /** + * Reuse avatar + * + * @param photoId photo id from getAvatarList api + * + * @throws {ZaloApiError} + */ + return async function reuseAvatar(photoId: string) { + const params = { + photoId: photoId, + isPostSocial: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/reviewPendingMemberRequest.ts b/_libs/zca-js/src/apis/reviewPendingMemberRequest.ts new file mode 100644 index 0000000..01af499 --- /dev/null +++ b/_libs/zca-js/src/apis/reviewPendingMemberRequest.ts @@ -0,0 +1,51 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ReviewPendingMemberRequestPayload = { + members: string | string[]; + isApprove: boolean; +}; + +export enum ReviewPendingMemberRequestStatus { + SUCCESS = 0, + NOT_IN_PENDING_LIST = 170, + ALREADY_IN_GROUP = 178, + INSUFFICIENT_PERMISSION = 166, +} + +export type ReviewPendingMemberRequestResponse = { + [memberId: string]: ReviewPendingMemberRequestStatus; +}; + +export const reviewPendingMemberRequestFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/pending-mems/review`); + + /** + * Review pending member(s) request + * + * @param payload - The payload containing data to review the pending member(s) request + * @param groupId - The id of the group to review the pending member(s) request + * + * @note Only the group leader and deputy group leader can review + * + * @throws {ZaloApiError} + */ + return async function reviewPendingMemberRequest(payload: ReviewPendingMemberRequestPayload, groupId: string) { + if (!Array.isArray(payload.members)) payload.members = [payload.members]; + + const params = { + grid: groupId, + members: payload.members, + isApprove: payload.isApprove ? 1 : 0, // 1: approve, 0: reject + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/searchSticker.ts b/_libs/zca-js/src/apis/searchSticker.ts new file mode 100644 index 0000000..5c7e6b9 --- /dev/null +++ b/_libs/zca-js/src/apis/searchSticker.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { StickerBasic } from "../models/index.js"; + +export type SearchStickerResponse = StickerBasic[]; + +export const searchStickerFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker[0]}/api/message/sticker/search`); + + /** + * Search stickers + * + * @param keyword Keyword to search stickers + * @param limit Limit of stickers to return (default: 50) + * + * @throws {ZaloApiError} + */ + return async function searchSticker(keyword: string, limit: number = 50) { + const params = { + keyword: keyword, + limit: limit, + srcType: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendBankCard.ts b/_libs/zca-js/src/apis/sendBankCard.ts new file mode 100644 index 0000000..810c422 --- /dev/null +++ b/_libs/zca-js/src/apis/sendBankCard.ts @@ -0,0 +1,49 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { BinBankCard } from "../models/index.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendBankCardPayload = { + binBank: BinBankCard; + numAccBank: string; + nameAccBank?: string; +}; + +export type SendBankCardResponse = ""; + +export const sendBankCardFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.zimsg[0]}/api/transfer/card`); + + /** + * Send bank card + * + * @param payload The payload containing the bank card information + * @param threadId The ID of the thread to send the bank card to + * @param type The type of thread to send the bank card to + * + * @throws {ZaloApiError} + */ + return async function sendBankCard(payload: SendBankCardPayload, threadId: string, type: ThreadType = ThreadType.User) { + const params = { + binBank: payload.binBank, + numAccBank: payload.numAccBank, + nameAccBank: payload.nameAccBank?.toUpperCase() || "---", + cliMsgId: Date.now().toString(), + tsMsg: Date.now(), + destUid: threadId, + destType: type === ThreadType.Group ? 1 : 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendCard.ts b/_libs/zca-js/src/apis/sendCard.ts new file mode 100644 index 0000000..dad3476 --- /dev/null +++ b/_libs/zca-js/src/apis/sendCard.ts @@ -0,0 +1,75 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendCardOptions = { + userId: string; + phoneNumber?: string; + ttl?: number; +}; + +export type SendCardResponse = { + msgId: number; +}; + +export const sendCardFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/forward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/forward`), + }; + + /** + * Send a card to a User - Group + * + * @param userId Unique ID for Card + * @param phoneNumber Optional phone number for sending card to a User + * @param ttl Time to live in milliseconds (default: 0) + * @param threadId ID of the conversation + * @param type Message type (User or GroupMessage) + * + * @throws {ZaloApiError} + * + */ + return async function sendCard(options: SendCardOptions, threadId: string, type: ThreadType = ThreadType.User) { + const data = await api.getQR(options.userId); + const QRCodeURL = data[options.userId]; + const clientId = Date.now().toString(); + + const params: Record = { + ttl: options.ttl ?? 0, + msgType: 6, + clientId: clientId, + msgInfo: { + contactUid: options.userId, + qrCodeUrl: QRCodeURL, + }, + }; + + if (options.phoneNumber) { + (params.msgInfo as Record).phone = options.phoneNumber; + } + + if (type == ThreadType.Group) { + params.visibility = 0; + params.grid = threadId; + } else { + params.toId = threadId; + params.imei = ctx.imei; + } + + const msgInfoStringified = JSON.stringify(params.msgInfo); + params.msgInfo = msgInfoStringified; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendDeliveredEvent.ts b/_libs/zca-js/src/apis/sendDeliveredEvent.ts new file mode 100644 index 0000000..fd808c5 --- /dev/null +++ b/_libs/zca-js/src/apis/sendDeliveredEvent.ts @@ -0,0 +1,103 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { MAX_MESSAGES_PER_SEND } from "../context.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendDeliveredEventResponse = "" | { status: number }; + +export type SendDeliveredEventMessageParams = { + msgId: string; + cliMsgId: string; + uidFrom: string; + idTo: string; + msgType: string; + st: number; + at: number; + cmd: number; + ts: string | number; +}; + +type RequestMessageParams = { + gmi: string; + cmi: string; + si: string; + di: string; + mt: string; + st: number; + at: number; + cmd: number; + ts: number; +}; + +type MsgInfos = { + seen: number; + data: RequestMessageParams[]; + grid?: string; // Optional, any random value works +}; + +export const sendDeliveredEventFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/deliveredv2`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/deliveredv2`), + }; + + /** + * Send message delivered event + * + * @param isSeen Whether the message is seen or not + * @param messages List of messages to send delivered event + * @param type Messages type (User or Group), defaults to User + * + * @throws {ZaloApiError} + */ + return async function sendDeliveredEvent( + isSeen: boolean, + messages: SendDeliveredEventMessageParams | SendDeliveredEventMessageParams[], + type: ThreadType = ThreadType.User, + ) { + if (!messages) throw new ZaloApiError("messages are missing or not in a valid array format."); + if (!Array.isArray(messages)) messages = [messages]; + if (messages.length === 0 || messages.length > MAX_MESSAGES_PER_SEND) + throw new ZaloApiError("messages must contain between 1 and 50 messages."); + + // 27/02/2025 + // This can send messages from multiple groups, but to prevent potential issues, + // we will restrict it to sending messages only within the same group. + const idTo = messages[0].idTo; + if (type === ThreadType.Group && !messages.every((msg) => msg.idTo === idTo)) + throw new ZaloApiError("All messages must have the same idTo for Group thread"); + + const msgInfos: MsgInfos = { + seen: isSeen ? 1 : 0, + data: messages.map((msg) => ({ + cmi: msg.cliMsgId, + gmi: msg.msgId, + si: msg.uidFrom, + di: msg.idTo === ctx.uid ? "0" : msg.idTo, + mt: msg.msgType, + st: msg.st || 0 === msg.st ? 0 : -1, + at: msg.at || 0 === msg.at ? 0 : -1, + cmd: msg.cmd || 0 === msg.cmd ? 0 : -1, + ts: parseInt(`${msg.ts}`) || 0 === parseInt(`${msg.ts}`) ? 0 : -1, + })), + ...(type === ThreadType.User ? {} : { grid: idTo }), + }; + + const params = { + msgInfos: JSON.stringify(msgInfos), + ...(type === ThreadType.User ? {} : { imei: ctx.imei }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendFriendRequest.ts b/_libs/zca-js/src/apis/sendFriendRequest.ts new file mode 100644 index 0000000..63cac0e --- /dev/null +++ b/_libs/zca-js/src/apis/sendFriendRequest.ts @@ -0,0 +1,45 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type SendFriendRequestResponse = ""; + +export const sendFriendRequestFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/sendreq`); + + /** + * Send a friend request to a user. + * + * @param msg message sent with friend request + * @param userId User ID to send friend request to + * + * @note Zalo might throw an error with code 225 if the user is already friends with you, + * 215 if the user might have blocked you, + * 222 if user has already sent you a friend request, your request will be treated as acceptance instead. + * + * @throws {ZaloApiError} + */ + return async function sendFriendRequest(msg: string, userId: string) { + const params = { + toid: userId, + msg: msg, + reqsrc: 30, + imei: ctx.imei, + language: ctx.language, + srcParams: JSON.stringify({ + uidTo: userId, + }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendLink.ts b/_libs/zca-js/src/apis/sendLink.ts new file mode 100644 index 0000000..64895bb --- /dev/null +++ b/_libs/zca-js/src/apis/sendLink.ts @@ -0,0 +1,76 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendLinkOptions = { + msg?: string; + link: string; + ttl?: number; +}; + +export type SendLinkResponse = { + msgId: string; +}; + +export const sendLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/link`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/sendlink`, { + nretry: 0, + }), + }; + + /** + * Send link + * + * @param options Link and ttl + * @param threadId Thread ID + * @param type Thread type + * + * @throws {ZaloApiError} + */ + return async function sendLink(options: SendLinkOptions, threadId: string, type: ThreadType = ThreadType.User) { + const res = await api.parseLink(options.link); + + const params: Record = { + msg: + options.msg && options.msg.trim() + ? options.msg.includes(options.link) + ? options.msg + : options.msg + " " + options.link + : options.link, + href: res.data.href, + src: res.data.src, + title: res.data.title, + desc: res.data.desc, + thumb: res.data.thumb, + type: 2, // 0 + media: JSON.stringify(res.data.media), + ttl: options.ttl ?? 0, + clientId: Date.now(), + }; + + if (type == ThreadType.Group) { + params.grid = threadId; + params.imei = ctx.imei; + // params.mentionInfo = "[]"; @TODO: implement this + } else { + params.toId = threadId; + params.mentionInfo = ""; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendMessage.ts b/_libs/zca-js/src/apis/sendMessage.ts new file mode 100644 index 0000000..5e16481 --- /dev/null +++ b/_libs/zca-js/src/apis/sendMessage.ts @@ -0,0 +1,674 @@ +import FormData from "form-data"; +import fs from "node:fs/promises"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType, type TMessage, type AttachmentSource } from "../models/index.js"; +import { + apiFactory, + getClientMessageType, + getFileExtension, + getFileName, + getGifMetaData, + getMd5LargeFileObject, + removeUndefinedKeys, + resolveResponse, +} from "../utils.js"; + +export type SendMessageResult = { + msgId: number; +}; + +export type SendMessageResponse = { + message: SendMessageResult | null; + attachment: SendMessageResult[]; +}; + +export type SendMessageQuote = { + content: TMessage["content"]; + msgType: TMessage["msgType"]; + propertyExt: TMessage["propertyExt"]; + + uidFrom: TMessage["uidFrom"]; + msgId: TMessage["msgId"]; + cliMsgId: TMessage["cliMsgId"]; + ts: TMessage["ts"]; + ttl: TMessage["ttl"]; +}; + +const attachmentUrlType = { + image: "photo_original/send?", + gif: "gif?", + video: "asyncfile/msg?", + others: "asyncfile/msg?", +}; + +function prepareQMSGAttach(quote: SendMessageQuote) { + const quoteData = quote; + if (typeof quoteData.content == "string") return quoteData.propertyExt; + if (quoteData.msgType == "chat.todo") + return { + properties: { + color: 0, + size: 0, + type: 0, + subType: 0, + ext: '{"shouldParseLinkOrContact":0}', + }, + }; + + return { + ...quoteData.content, + thumbUrl: quoteData.content.thumb, + oriUrl: quoteData.content.href, + normalUrl: quoteData.content.href, + }; +} + +function prepareQMSG(quote: SendMessageQuote) { + const quoteData = quote; + if (quoteData.msgType == "chat.todo" && typeof quoteData.content == "object" && typeof quoteData.content.params == "string") { + return JSON.parse(quoteData.content.params).item.content; + } + + return ""; +} + +type SendType = { + url: string; + body?: BodyInit | null; + headers?: Record; +}; + +type UpthumbType = { + hdUrl: string; + clientFileId: string; + url: string; + fileId: string; +}; + +type AttachmentData = + | { + query?: Record; + fileType: "image" | "video" | "others"; + body: URLSearchParams; + params: Record; + } + | { + query?: Record; + fileType: "gif"; + body: Buffer; + headers: Record; + }; + +export enum TextStyle { + Bold = "b", + Italic = "i", + Underline = "u", + StrikeThrough = "s", + Red = "c_db342e", + Orange = "c_f27806", + Yellow = "c_f7b503", + Green = "c_15a85f", + Small = "f_13", + Big = "f_18", + UnorderedList = "lst_1", + OrderedList = "lst_2", + Indent = "ind_$", +} + +export type Style = + | { + start: number; + len: number; + st: Exclude; + } + | { + start: number; + len: number; + st: TextStyle.Indent; + /** + * Number of spaces used for indentation. + */ + indentSize?: number; + }; + +export enum Urgency { + Default, + Important, + Urgent, +} + +export type Mention = { + /** + * mention position + */ + pos: number; + /** + * id of the mentioned user + */ + uid: string; + /** + * length of the mention + */ + len: number; +}; + +export type MessageContent = { + /** + * Text content of the message + */ + msg: string; + /** + * Text styles + */ + styles?: Style[]; + /** + * Urgency of the message + */ + urgency?: Urgency; + /** + * Quoted message (optional) + */ + quote?: SendMessageQuote; + /** + * Mentions in the message (optional) + */ + mentions?: Mention[]; + /** + * Attachments in the message (optional) + */ + attachments?: AttachmentSource | AttachmentSource[]; + /** + * Time to live in milliseconds + */ + ttl?: number; +}; + +export const sendMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURLs = { + message: { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group`, { + nretry: 0, + }), + }, + attachment: { + [ThreadType.User]: `${api.zpwServiceMap.file[0]}/api/message/`, + [ThreadType.Group]: `${api.zpwServiceMap.file[0]}/api/group/`, + }, + }; + + const { sharefile } = ctx.settings!.features; + + function isExceedMaxFile(totalFile: number) { + return totalFile > sharefile.max_file; + } + + function isExceedMaxFileSize(fileSize: number) { + return fileSize > sharefile.max_size_share_file_v3 * 1024 * 1024; + } + + function getGroupLayoutId() { + return Date.now(); + } + + async function send(data: SendType | SendType[]): Promise { + if (!Array.isArray(data)) data = [data]; + + const requests: Promise[] = []; + + for (const each of data) { + requests.push( + (async () => { + const response = await utils.request(each.url, { + method: "POST", + body: each.body, + headers: each.headers, + }); + + return await resolveResponse(ctx, response); + })(), + ); + } + + return await Promise.all(requests); + } + + async function upthumb(source: AttachmentSource, url: string): Promise { + const formData = new FormData(); + const buffer = typeof source == "string" ? await fs.readFile(source) : source.data; + formData.append("fileContent", buffer, { + filename: "blob", + contentType: "image/png", + }); + + const params = { + clientId: Date.now(), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request( + utils.makeURL(url + "upthumb?", { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return await resolveResponse(ctx, response); + } + + function handleMentions(type: ThreadType, msg: string, mentions?: Mention[]) { + let totalMentionLen = 0; + const mentionsFinal = + Array.isArray(mentions) && type == ThreadType.Group + ? mentions + .filter((m) => m.pos >= 0 && m.uid && m.len > 0) + .map((m) => { + totalMentionLen += m.len; + return { + pos: m.pos, + uid: m.uid, + len: m.len, + type: m.uid == "-1" ? 1 : 0, + }; + }) + : []; + + if (totalMentionLen > msg.length) { + throw new ZaloApiError("Invalid mentions: total mention characters exceed message length"); + } + + return { + mentionsFinal, + msgFinal: msg, + }; + } + + function handleStyles(params: Record, styles?: Style[]) { + if (styles) + Object.assign(params, { + textProperties: JSON.stringify({ + styles: styles.map((e) => { + const styleFinal = { + ...e, + indentSize: undefined, + st: + e.st == TextStyle.Indent + ? TextStyle.Indent.replace(/\$/g, `${e.indentSize ?? 1}0`) + : e.st, + }; + + removeUndefinedKeys(styleFinal); + return styleFinal; + }), + ver: 0, + }), + }); + } + + function handleUrgency(params: Record, urgency?: Urgency) { + if (urgency == Urgency.Important || urgency == Urgency.Urgent) { + Object.assign(params, { metaData: { urgency } }); + } + } + + async function handleMessage( + { msg, styles, urgency, mentions, quote, ttl }: MessageContent, + threadId: string, + type: ThreadType, + ) { + if (!msg || msg.length == 0) throw new ZaloApiError("Missing message content"); + const isGroupMessage = type == ThreadType.Group; + + const { mentionsFinal, msgFinal } = handleMentions(type, msg, mentions); + msg = msgFinal; + + if (quote) { + if (typeof quote.content != "string" && quote.msgType == "webchat") { + throw new ZaloApiError("This kind of `webchat` quote type is not available"); + } + + if (quote.msgType == "group.poll") { + throw new ZaloApiError("The `group.poll` quote type is not available"); + } + } + + const isMentionsValid = mentionsFinal.length > 0 && isGroupMessage; + const params = quote + ? { + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + message: msg, + clientId: Date.now(), + mentionInfo: isMentionsValid ? JSON.stringify(mentionsFinal) : undefined, + qmsgOwner: quote.uidFrom, + qmsgId: quote.msgId, + qmsgCliId: quote.cliMsgId, + qmsgType: getClientMessageType(quote.msgType), + qmsgTs: quote.ts, + qmsg: typeof quote.content == "string" ? quote.content : prepareQMSG(quote), + imei: isGroupMessage ? undefined : ctx.imei, + visibility: isGroupMessage ? 0 : undefined, + qmsgAttach: isGroupMessage ? JSON.stringify(prepareQMSGAttach(quote)) : undefined, + qmsgTTL: quote.ttl, + ttl: ttl ?? 0, + } + : { + message: msg, + clientId: Date.now(), + mentionInfo: isMentionsValid ? JSON.stringify(mentionsFinal) : undefined, + imei: isGroupMessage ? undefined : ctx.imei, + ttl: ttl ?? 0, + visibility: isGroupMessage ? 0 : undefined, + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + }; + + handleStyles(params, styles); + handleUrgency(params, urgency); + + removeUndefinedKeys(params); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + const finalServiceUrl = new URL(serviceURLs.message[type]); + if (quote) { + finalServiceUrl.pathname = finalServiceUrl.pathname + "/quote"; + } else { + finalServiceUrl.pathname = + finalServiceUrl.pathname + + "/" + + (isGroupMessage ? (params.mentionInfo ? "mention" : "sendmsg") : "sms"); + } + + return { + url: finalServiceUrl.toString(), + body: new URLSearchParams({ params: encryptedParams }), + }; + } + + async function handleAttachment( + { msg, attachments, mentions, quote, ttl, urgency }: MessageContent, + threadId: string, + type: ThreadType, + ) { + if (!attachments) throw new ZaloApiError("Missing attachments"); + if (!Array.isArray(attachments)) attachments = [attachments]; + if (attachments.length == 0) throw new ZaloApiError("Missing attachments"); + const firstSource = attachments[0]; + const isFilePath = typeof firstSource == "string"; + + const firstExtFile = getFileExtension(isFilePath ? firstSource : firstSource.filename); + const isSingleFile = attachments.length == 1; + const isGroupMessage = type == ThreadType.Group; + + const canBeDesc = isSingleFile && ["jpg", "jpeg", "png", "webp"].includes(firstExtFile); + + const gifFiles = attachments.filter((e) => getFileExtension(typeof e == "string" ? e : e.filename) == "gif"); + attachments = attachments.filter((e) => getFileExtension(typeof e == "string" ? e : e.filename) != "gif"); + + const uploadAttachment = attachments.length == 0 ? [] : await api.uploadAttachment(attachments, threadId, type); + + const attachmentsData: AttachmentData[] = []; + let indexInGroupLayout = 0; + + const groupLayoutId = getGroupLayoutId().toString(); + + const { mentionsFinal, msgFinal } = handleMentions(type, msg, mentions); + msg = msgFinal; + + const isMentionsValid = mentionsFinal.length > 0 && isGroupMessage && attachments.length == 1; + + const isMultiFile = attachments.length > 1; + let clientId = Date.now(); + for (const attachment of uploadAttachment) { + let data: AttachmentData; + switch (attachment.fileType) { + case "image": { + data = { + fileType: attachment.fileType, + params: { + photoId: attachment.photoId, + clientId: (clientId++).toString(), + desc: msg, + width: attachment.width, + height: attachment.height, + toid: isGroupMessage ? undefined : String(threadId), + grid: isGroupMessage ? String(threadId) : undefined, + rawUrl: attachment.normalUrl, + hdUrl: attachment.hdUrl, + thumbUrl: attachment.thumbUrl, + oriUrl: isGroupMessage ? attachment.normalUrl : undefined, + normalUrl: isGroupMessage ? undefined : attachment.normalUrl, + hdSize: String(attachment.totalSize), + zsource: -1, + ttl: ttl ?? 0, + jcp: '{"convertible":"jxl"}', + + groupLayoutId: isMultiFile ? groupLayoutId : undefined, + isGroupLayout: isMultiFile ? 1 : undefined, + idInGroup: isMultiFile ? indexInGroupLayout++ : undefined, + totalItemInGroup: isMultiFile ? uploadAttachment.length : undefined, + extMsgProp: isMultiFile ? `{"groupMediaMsg":{"groupLayoutId":"${groupLayoutId}"}}` : undefined, + + mentionInfo: + isMentionsValid && canBeDesc && !quote ? JSON.stringify(mentionsFinal) : undefined, + }, + body: new URLSearchParams(), + }; + break; + } + + case "video": { + data = { + fileType: attachment.fileType, + params: { + fileId: attachment.fileId, + checksum: attachment.checksum, + checksumSha: "", + extention: getFileExtension(attachment.fileName), + totalSize: attachment.totalSize, + fileName: attachment.fileName, + clientId: attachment.clientFileId, + fType: 1, + fileCount: 0, + fdata: "{}", + toid: isGroupMessage ? undefined : String(threadId), + grid: isGroupMessage ? String(threadId) : undefined, + fileUrl: attachment.fileUrl, + zsource: -1, + ttl: ttl ?? 0, + }, + body: new URLSearchParams(), + }; + break; + } + + case "others": { + data = { + fileType: attachment.fileType, + params: { + fileId: attachment.fileId, + checksum: attachment.checksum, + checksumSha: "", + extention: getFileExtension(attachment.fileName), + totalSize: attachment.totalSize, + fileName: attachment.fileName, + clientId: attachment.clientFileId, + fType: 1, + fileCount: 0, + fdata: "{}", + toid: isGroupMessage ? undefined : String(threadId), + grid: isGroupMessage ? String(threadId) : undefined, + fileUrl: attachment.fileUrl, + zsource: -1, + ttl: ttl ?? 0, + }, + body: new URLSearchParams(), + }; + break; + } + } + + handleUrgency(data.params, urgency); + + removeUndefinedKeys(data.params); + const encryptedParams = utils.encodeAES(JSON.stringify(data.params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + data.body.append("params", encryptedParams); + attachmentsData.push(data); + } + + for (const gif of gifFiles) { + const isFilePath = typeof gif == "string"; + const gifData = isFilePath ? await getGifMetaData(ctx, gif) : { ...gif.metadata, fileName: gif.filename }; + if (isExceedMaxFileSize(gifData.totalSize!)) + throw new ZaloApiError( + `File ${isFilePath ? getFileName(gif) : gif.filename} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + const _upthumb = await upthumb(gif, serviceURLs.attachment[ThreadType.User]); + + const formData = new FormData(); + formData.append("chunkContent", isFilePath ? await fs.readFile(gif) : gif.data, { + filename: isFilePath ? getFileName(gif) : gif.filename, + contentType: "application/octet-stream", + }); + + const params = { + clientId: Date.now().toString(), + fileName: gifData.fileName, + totalSize: gifData.totalSize, + width: gifData.width, + height: gifData.height, + msg: msg, + type: 1, + ttl: ttl ?? 0, + visibility: isGroupMessage ? 0 : undefined, + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + thumb: _upthumb.url, + checksum: (await getMd5LargeFileObject(gif, gifData.totalSize!)).data, + totalChunk: 1, + chunkId: 1, + }; + + handleUrgency(params, urgency); + + removeUndefinedKeys(params); + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + attachmentsData.push({ + query: { + params: encryptedParams, + type: "1", + }, + body: formData.getBuffer(), + headers: formData.getHeaders(), + fileType: "gif", + }); + } + + const responses = []; + + for (const data of attachmentsData) { + responses.push({ + url: utils.makeURL( + serviceURLs.attachment[type] + attachmentUrlType[data.fileType], + Object.assign( + { + nretry: "0", + }, + data.query || {}, + ), + ), + body: data.body, + headers: data.fileType == "gif" ? data.headers : {}, + }); + } + + return responses; + } + + /** + * Send a message to a thread + * + * @param message Message content + * @param threadId group or user id + * @param type Message type (User or Group) + * @param quote Message or GroupMessage instance (optional), used for quoting + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function sendMessage( + message: MessageContent | string, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + if (!message) throw new ZaloApiError("Missing message content"); + if (!threadId) throw new ZaloApiError("Missing threadId"); + if (typeof message == "string") message = { msg: message }; + + let { msg, attachments, mentions } = message; + const { quote, ttl, styles, urgency } = message; + + if (attachments && !Array.isArray(attachments)) { + attachments = [attachments]; + } + + if (!msg && (!attachments || (attachments && attachments.length == 0))) + throw new ZaloApiError("Missing message content"); + + if (attachments && isExceedMaxFile(attachments.length)) + throw new ZaloApiError("Exceed maximum file of " + sharefile.max_file); + + const responses: { + message: SendMessageResult | null; + attachment: SendMessageResult[]; + } = { + message: null, + attachment: [], + }; + + if (attachments && attachments.length > 0) { + const firstExtFile = getFileExtension( + typeof attachments[0] == "string" ? attachments[0] : attachments[0].filename, + ); + const isSingleFile = attachments.length == 1; + + const canBeDesc = isSingleFile && ["jpg", "jpeg", "png", "webp"].includes(firstExtFile); + if ((!canBeDesc && msg.length > 0) || (msg.length > 0 && quote)) { + // send message and attachment separately + await handleMessage(message, threadId, type).then(async (data) => { + responses.message = (await send(data))[0]; + }); + msg = ""; + mentions = undefined; + } + const handledData = await handleAttachment( + { msg, mentions, attachments, quote, ttl, styles, urgency }, + threadId, + type, + ); + responses.attachment = await send(handledData); + msg = ""; + } + + if (msg.length > 0) { + const handledData = await handleMessage(message, threadId, type); + responses.message = (await send(handledData))[0]; + } + + return responses; + }; +}); diff --git a/_libs/zca-js/src/apis/sendReport.ts b/_libs/zca-js/src/apis/sendReport.ts new file mode 100644 index 0000000..9375669 --- /dev/null +++ b/_libs/zca-js/src/apis/sendReport.ts @@ -0,0 +1,72 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory, removeUndefinedKeys } from "../utils.js"; + +export enum ReportReason { + Sensitive = 1, + Annoy = 2, + Fraud = 3, + Other = 0, +} + +export type SendReportOptions = + | { + reason: ReportReason.Other; + content: string; + } + | { + reason: Exclude; + }; + +export type SendReportResponse = { + reportId: string; +}; + +export const sendReportFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/report/abuse-v2`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/reportabuse`), + }; + + /** + * Send report to Zalo + * + * @param options Report options + * @param threadId The threadID to report + * @param type Thread type, default direct + * + * @throws {ZaloApiError} + * + */ + return async function sendReport(options: SendReportOptions, threadId: string, type: ThreadType = ThreadType.User) { + const params = + type == ThreadType.User + ? { + idTo: threadId, + objId: "person.profile", + reason: options.reason.toString(), + content: options.reason == ReportReason.Other ? options.content : undefined, + } + : { + uidTo: threadId, + type: 14, + reason: options.reason, + content: options.reason == ReportReason.Other ? options.content : "", + imei: ctx.imei, + }; + + removeUndefinedKeys(params); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendSeenEvent.ts b/_libs/zca-js/src/apis/sendSeenEvent.ts new file mode 100644 index 0000000..b9724ca --- /dev/null +++ b/_libs/zca-js/src/apis/sendSeenEvent.ts @@ -0,0 +1,108 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { MAX_MESSAGES_PER_SEND } from "../context.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendSeenEventResponse = { + status: number; +}; + +export type SendSeenEventMessageParams = { + msgId: string; + cliMsgId: string; + uidFrom: string; + idTo: string; + msgType: string; + st: number; + at: number; + cmd: number; + ts: string | number; +}; + +type RequestMessageParams = { + gmi: string; + cmi: string; + si: string; + di: string; + mt: string; + st: number; + at: number; + cmd: number; + ts: number; +}; + +type MsgInfos = { + data: RequestMessageParams[]; + grid?: string; + senderId?: string; +}; + +export const sendSeenEventFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/seenv2`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/seenv2`, { + nretry: 0, + }), + }; + + /** + * Send message seen event + * + * @param messages List of messages to send seen event + * @param type Messages type (User or Group), defaults to User + * + * @throws {ZaloApiError} + */ + return async function sendSeenEvent( + messages: SendSeenEventMessageParams | SendSeenEventMessageParams[], + type: ThreadType = ThreadType.User, + ) { + if (!messages) throw new ZaloApiError("messages are missing or not in a valid array format."); + if (!Array.isArray(messages)) messages = [messages]; + if (messages.length === 0 || messages.length > MAX_MESSAGES_PER_SEND) + throw new ZaloApiError("messages must contain between 1 and 50 messages."); + + const isGroup = type === ThreadType.Group; + const threadId = isGroup ? messages[0].idTo : messages[0].uidFrom; + + const msgInfos: MsgInfos = { + data: messages.map((msg) => { + const curThreadId = isGroup ? msg.idTo : msg.uidFrom; + if (curThreadId !== threadId) { + throw new ZaloApiError("All messages must belong to the same thread."); + } + return { + cmi: msg.cliMsgId, + gmi: msg.msgId, + si: msg.uidFrom, + di: msg.idTo === ctx.uid ? "0" : msg.idTo, + mt: msg.msgType, + st: msg.st || 0 === msg.st ? 0 : -1, + at: msg.at || 0 === msg.at ? 0 : -1, + cmd: msg.cmd || 0 === msg.cmd ? 0 : -1, + ts: parseInt(`${msg.ts}`) || 0 === parseInt(`${msg.ts}`) ? 0 : -1, + }; + }), + [isGroup ? "grid" : "senderId"]: threadId, + }; + + const params = { + msgInfos: JSON.stringify(msgInfos), + ...(isGroup ? { imei: ctx.imei } : {}), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendSticker.ts b/_libs/zca-js/src/apis/sendSticker.ts new file mode 100644 index 0000000..6b1a330 --- /dev/null +++ b/_libs/zca-js/src/apis/sendSticker.ts @@ -0,0 +1,71 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory, removeUndefinedKeys } from "../utils.js"; + +export type SendStickerPayload = { + id: number; + cateId: number; + type: number; +} + +export type SendStickerResponse = { + msgId: number; +}; + +export const sendStickerFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/sticker`, { + nretry: "0", + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/sticker`, { + nretry: "0", + }), + }; + + /** + * Send a sticker to a thread + * + * @param sticker Sticker object + * @param threadId group or user id + * @param type Message type (User or GroupMessage) + * + * @throws {ZaloApiError} + */ + return async function sendSticker(sticker: SendStickerPayload, threadId: string, type: ThreadType = ThreadType.User) { + if (!sticker) throw new ZaloApiError("Missing sticker"); + if (!threadId) throw new ZaloApiError("Missing threadId"); + + if (!sticker.id) throw new ZaloApiError("Missing sticker id"); + + // Sometime sticker.cateId = 0, which is invalid. + if (sticker.cateId === undefined || sticker.cateId === null) throw new ZaloApiError("Missing sticker cateId"); + if (!sticker.type) throw new ZaloApiError("Missing sticker type"); + + const isGroupMessage = type === ThreadType.Group; + + const params = { + stickerId: sticker.id, + cateId: sticker.cateId, + type: sticker.type, + clientId: Date.now(), + imei: ctx.imei, + zsource: 101, + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + }; + + removeUndefinedKeys(params); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendTypingEvent.ts b/_libs/zca-js/src/apis/sendTypingEvent.ts new file mode 100644 index 0000000..21b06b8 --- /dev/null +++ b/_libs/zca-js/src/apis/sendTypingEvent.ts @@ -0,0 +1,49 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { DestType, ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendTypingEventResponse = { + status: number; +}; + +export const sendTypingEventFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/typing`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/typing`), + }; + + /** + * Send typing event + * + * @param threadId The ID of the User or Group to send the typing event to + * @param type The type of thread (User or Group) + * @param destType The destination type (User or Page), for User threads only, defaults to User + * + * @throws {ZaloApiError} + */ + return async function sendTypingEvent( + threadId: string, + type: ThreadType = ThreadType.User, + destType: DestType = DestType.User, + ) { + if (!threadId) throw new ZaloApiError("Missing threadId"); + + const params = { + [type === ThreadType.User ? "toid" : "grid"]: threadId, + ...(type === ThreadType.User ? { destType } : {}), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendVideo.ts b/_libs/zca-js/src/apis/sendVideo.ts new file mode 100644 index 0000000..3dd335f --- /dev/null +++ b/_libs/zca-js/src/apis/sendVideo.ts @@ -0,0 +1,161 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendVideoOptions = { + /** + * Optional message to send along with the video + */ + msg?: string; + /** + * URL of the video + */ + videoUrl: string; + /** + * URL of the thumbnail + */ + thumbnailUrl: string; + /** + * Video duration in milliseconds || Eg: video duration: 5.5s => 5.5 * 1000 = 5500 + */ + duration?: number; + /** + * Width of the video + */ + width?: number; + /** + * Height of the video + */ + height?: number; + /** + * Time to live in milliseconds (default: 0) + */ + ttl?: number; +}; + +export type SendVideoResponse = { + msgId: number; +}; + +export const sendVideoFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/forward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/forward`), + }; + + /** + * Send a video to a User - Group + * + * @param options send video options + * @param threadId ID of the user or group to send the video to + * @param type Type of thread (USER or GROUP) + * + * @throws {ZaloApiError} + * + * @examples Example Video Resolutions: + * - **Standard Videos**: + * - 3840x2160 (4K UHD): Width 3840px, Height 2160px. + * - 1920x1080 (Full HD): Width 1920px, Height 1080px. + * - 1280x720 (HD): Width 1280px, Height 720px. + * - **Document-Oriented Videos** (Portrait): + * - 3840x2160 (4K UHD): Width 3840px, Height 2160px. + * - 720x1280 (HD): Width 720px, Height 1280px. + * - 1440x2560 (2K): Width 1440px, Height 2560px. + * + */ + return async function sendVideo(options: SendVideoOptions, threadId: string, type: ThreadType = ThreadType.User) { + let fileSize: number = 0; + const clientId = Date.now(); + + try { + const headResponse = await utils.request(options.videoUrl, { method: "HEAD" }, true); + if (headResponse.ok) { + fileSize = parseInt(headResponse.headers.get("content-length") || "0"); + } + } catch (error: unknown) { + throw new ZaloApiError( + `Unable to get video content: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + const params = + type === ThreadType.User + ? { + toId: threadId, + clientId: String(clientId), + ttl: options.ttl ?? 0, + zsource: 704, + msgType: 5, + msgInfo: JSON.stringify({ + videoUrl: options.videoUrl, + thumbUrl: options.thumbnailUrl, + duration: options.duration ?? 0, + width: options.width ?? 1280, + height: options.height ?? 720, + fileSize: fileSize, + properties: { + color: -1, + size: -1, + type: 1003, + subType: 0, + ext: { + sSrcType: -1, + sSrcStr: "", + msg_warning_type: 0, + }, + }, + title: options.msg ?? "", + }), + imei: ctx.imei, + } + : { + grid: threadId, + visibility: 0, + clientId: String(clientId), + ttl: options.ttl ?? 0, + zsource: 704, + msgType: 5, + msgInfo: JSON.stringify({ + videoUrl: options.videoUrl, + thumbUrl: options.thumbnailUrl, + duration: options.duration ?? 0, + width: options.width ?? 1280, + height: options.height ?? 720, + fileSize: fileSize, + properties: { + color: -1, + size: -1, + type: 1003, + subType: 0, + ext: { + sSrcType: -1, + sSrcStr: "", + msg_warning_type: 0, + }, + }, + title: options.msg ?? "", + }), + imei: ctx.imei, + }; + + // @TODO: later + // if (typeof message !== "string" && message.mention) { + // params.mentionInfo = message.mention; + // } + if (type !== ThreadType.User && type !== ThreadType.Group) { + throw new ZaloApiError("Thread type is invalid"); + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendVoice.ts b/_libs/zca-js/src/apis/sendVoice.ts new file mode 100644 index 0000000..823338b --- /dev/null +++ b/_libs/zca-js/src/apis/sendVoice.ts @@ -0,0 +1,93 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendVoiceOptions = { + voiceUrl: string; + /** + * Time to live in milliseconds (default: 0) + */ + ttl?: number; +}; + +export type SendVoiceResponse = { + msgId: string; +}; + +export const sendVoiceFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/forward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/forward`), + }; + + /** + * Send a voice to a User - Group + * + * @param options voice options + * @param threadId ID of the user or group to send the voice to + * @param type Type of thread, default user + * + * @throws {ZaloApiError} + */ + return async function sendVoice(options: SendVoiceOptions, threadId: string, type: ThreadType = ThreadType.User) { + let fileSize = null; + const clientId = Date.now().toString(); + + try { + const headResponse = await utils.request(options.voiceUrl, { method: "HEAD" }, true); + if (headResponse.ok) { + fileSize = parseInt(headResponse.headers.get("content-length") || "0"); + } + } catch (error: unknown) { + throw new ZaloApiError( + `Unable to get voice content: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + const params = + type === ThreadType.User + ? { + toId: threadId, + ttl: options.ttl ?? 0, + zsource: -1, + msgType: 3, + clientId: clientId, + msgInfo: JSON.stringify({ + voiceUrl: options.voiceUrl, + m4aUrl: options.voiceUrl, + fileSize: fileSize ?? 0, + }), + imei: ctx.imei, + } + : { + grid: threadId, + visibility: 0, + ttl: options.ttl ?? 0, + zsource: -1, + msgType: 3, + clientId: clientId, + msgInfo: JSON.stringify({ + voiceUrl: options.voiceUrl, + m4aUrl: options.voiceUrl, + fileSize: fileSize ?? 0, + }), + imei: ctx.imei, + }; + + if (type !== ThreadType.User && type !== ThreadType.Group) { + throw new ZaloApiError("Thread type is invalid"); + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/setHiddenConversations.ts b/_libs/zca-js/src/apis/setHiddenConversations.ts new file mode 100644 index 0000000..b6ec740 --- /dev/null +++ b/_libs/zca-js/src/apis/setHiddenConversations.ts @@ -0,0 +1,51 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SetHiddenConversationsResponse = ""; + +export const setHiddenConversationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/add-remove`); + + /** + * Set hidden conversations + * + * @param hidden - Hide or unhide conversations + * @param threadId Thread ID(s) + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function setHiddenConversations( + hidden: boolean, + threadId: string | string[], + type: ThreadType = ThreadType.User, + ) { + threadId = Array.isArray(threadId) ? threadId : [threadId]; + if (threadId.length === 0) throw new ZaloApiError("threadId is required"); + + const is_group = type === ThreadType.Group ? 1 : 0; + const params = { + [hidden ? "add_threads" : "del_threads"]: JSON.stringify( + threadId.map((id) => ({ + thread_id: id, + is_group: is_group, + })), + ), + [hidden ? "del_threads" : "add_threads"]: "[]", + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/setMute.ts b/_libs/zca-js/src/apis/setMute.ts new file mode 100644 index 0000000..e1ae073 --- /dev/null +++ b/_libs/zca-js/src/apis/setMute.ts @@ -0,0 +1,82 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SetMuteParams = { + /** + * Mute duration in seconds or predefined durations + */ + duration?: MuteDuration | number; + action?: MuteAction; +}; + +export type SetMuteResponse = ""; + +export enum MuteDuration { + ONE_HOUR = 3600, + FOUR_HOURS = 14400, + FOREVER = -1, + UNTIL_8AM = "until8AM", +} + +export enum MuteAction { + MUTE = 1, + UNMUTE = 3, // khi tat thi duration = -1 +} + +export const setMuteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/setmute`); + + /** + * Set mute + * + * @param params - Mute parameters + * @param threadID - ID of the thread to mute + * @param type - Type of thread (User or Group) + * + * @throws {ZaloApiError} + */ + return async function setMute(params: SetMuteParams = {}, threadID: string, type: ThreadType = ThreadType.User) { + const { duration = MuteDuration.FOREVER, action = MuteAction.MUTE } = params; + let muteDuration: number; + + if (action === MuteAction.UNMUTE) { + muteDuration = -1; + } else if (duration === MuteDuration.FOREVER) { + muteDuration = -1; + } else if (duration === MuteDuration.UNTIL_8AM) { + const now = new Date(); + const next8AM = new Date(now); + next8AM.setHours(8, 0, 0, 0); + + if (now.getHours() >= 8) { + next8AM.setDate(next8AM.getDate() + 1); + } + + muteDuration = Math.floor((next8AM.getTime() - now.getTime()) / 1000); + } else { + muteDuration = duration; + } + + const requestParams = { + toid: threadID, + duration: muteDuration, + action: action, + startTime: Math.floor(Date.now() / 1000), + muteType: type === ThreadType.User ? 1 : 2, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/setPinnedConversations.ts b/_libs/zca-js/src/apis/setPinnedConversations.ts new file mode 100644 index 0000000..587e359 --- /dev/null +++ b/_libs/zca-js/src/apis/setPinnedConversations.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SetPinnedConversationsResponse = ""; + +export const setPinnedConversationsFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/pinconvers/updatev2`); + + /** + * Pin and unpin conversations of threads) + * + * @param pinned Should pin conversations + * @param threadId The ID(s) of the thread + * @param type Type of thread, default User + * + * @throws {ZaloApiError} + * + */ + return async function setPinnedConversations( + pinned: boolean, + threadId: string | string[], + type: ThreadType = ThreadType.User, + ) { + if (typeof threadId == "string") threadId = [threadId]; + + const params = { + actionType: pinned ? 1 : 2, + conversations: type == ThreadType.Group ? threadId.map((id) => `g${id}`) : threadId.map((id) => `u${id}`), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sharePoll.ts b/_libs/zca-js/src/apis/sharePoll.ts new file mode 100644 index 0000000..a689c96 --- /dev/null +++ b/_libs/zca-js/src/apis/sharePoll.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type SharePollResponse = ""; + +export const sharePollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/share`); + + /** + * Share a poll + * + * @param pollId poll id to share + * + * @throws {ZaloApiError} + */ + return async function sharePoll(pollId: number) { + const params = { + poll_id: pollId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/unblockUser.ts b/_libs/zca-js/src/apis/unblockUser.ts new file mode 100644 index 0000000..002f6a8 --- /dev/null +++ b/_libs/zca-js/src/apis/unblockUser.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UnBlockUserResponse = ""; + +export const unblockUserFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/unblock`); + + /** + * Unblock a User + * + * @param userId The ID of the User to unblock + * + * @throws {ZaloApiError} + */ + return async function unblockUser(userId: string) { + const params = { + fid: userId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/undo.ts b/_libs/zca-js/src/apis/undo.ts new file mode 100644 index 0000000..7873e0d --- /dev/null +++ b/_libs/zca-js/src/apis/undo.ts @@ -0,0 +1,53 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type UndoPayload = { + msgId: string | number; + cliMsgId: string | number; +}; + +export type UndoResponse = { + status: number; +}; + +export const undoFactory = apiFactory()((api, ctx, utils) => { + const URLType = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/undo`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/undomsg`), + }; + /** + * Undo a message + * + * @param payload Undo payload containing message ID and client message ID + * @param threadId group or user id + * @param type Message type (User or Group), default is User + * + * @throws {ZaloApiError} + */ + return async function undo(payload: UndoPayload, threadId: string, type: ThreadType = ThreadType.User) { + const params: Record = { + msgId: payload.msgId, + clientId: Date.now(), + cliMsgIdUndo: payload.cliMsgId, + }; + + if (type == ThreadType.Group) { + params["grid"] = threadId; + params["visibility"] = 0; + params["imei"] = ctx.imei; + } else params["toid"] = threadId; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(URLType[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/undoFriendRequest.ts b/_libs/zca-js/src/apis/undoFriendRequest.ts new file mode 100644 index 0000000..6fec1db --- /dev/null +++ b/_libs/zca-js/src/apis/undoFriendRequest.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UndoFriendRequestResponse = ""; + +export const undoFriendRequestFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/undo`); + + /** + * Undo send a friend request to a user. + * + * @param options + * + * @throws {ZaloApiError} + */ + return async function undoFriendRequest(friendId: string) { + const params = { + fid: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateActiveStatus.ts b/_libs/zca-js/src/apis/updateActiveStatus.ts new file mode 100644 index 0000000..3e4020d --- /dev/null +++ b/_libs/zca-js/src/apis/updateActiveStatus.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateActiveStatusResponse = { + status: boolean; +}; + +export const updateActiveStatusFactory = apiFactory()((api, ctx, utils) => { + const pingURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/ping`); + const deactiveURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/deactive`); + + /** + * Update active status? + * + * @param active + * + * @throws {ZaloApiError} + */ + return async function updateActiveStatus(active: boolean) { + const params = { + status: active ? 1 : 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const targetURL = active ? pingURL : deactiveURL; + const response = await utils.request(utils.makeURL(targetURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateArchivedChatList.ts b/_libs/zca-js/src/apis/updateArchivedChatList.ts new file mode 100644 index 0000000..7748248 --- /dev/null +++ b/_libs/zca-js/src/apis/updateArchivedChatList.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { ThreadType } from "../models/Enum.js"; + +export type UpdateArchivedChatListTarget = { id: string; type: ThreadType }; + +export type UpdateArchivedChatListResponse = { needResync: boolean; version: number }; + +export const updateArchivedChatListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/archivedchat/update`); + + /** + * Archive conversations + * + * @param isArchived - true to archive, false to unarchive + * @param conversations - List of conversations to archive/unarchive + * + * @throws {ZaloApiError} + */ + return async function updateArchivedChatList(isArchived: boolean, conversations: UpdateArchivedChatListTarget | UpdateArchivedChatListTarget[]) { + if (!Array.isArray(conversations)) { + conversations = [conversations]; + } + const params = { + actionType: isArchived ? 0 : 1, + ids: conversations, + imei: ctx.imei, + version: Date.now(), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateAutoDeleteChat.ts b/_libs/zca-js/src/apis/updateAutoDeleteChat.ts new file mode 100644 index 0000000..d4cd536 --- /dev/null +++ b/_libs/zca-js/src/apis/updateAutoDeleteChat.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export enum ChatTTL { + NO_DELETE = 0, + ONE_DAY = 86400000, + SEVEN_DAYS = 604800000, + FOURTEEN_DAYS = 1209600000, +} + +export type UpdateAutoDeleteChatResponse = ""; + +export const updateAutoDeleteChatFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/autodelete/updateConvers`); + + /** + * Auto delete chat + * + * @param ttl The time to live of the chat + * @param threadId The thread ID to auto delete chat + * @param type Type of thread (User or Group) + * + * @throws {ZaloApiError} + */ + return async function updateAutoDeleteChat(ttl: ChatTTL, threadId: string, type: ThreadType = ThreadType.User) { + const params = { + threadId: threadId, + isGroup: type === ThreadType.Group ? 1 : 0, + ttl: ttl, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateAutoReply.ts b/_libs/zca-js/src/apis/updateAutoReply.ts new file mode 100644 index 0000000..a64ada1 --- /dev/null +++ b/_libs/zca-js/src/apis/updateAutoReply.ts @@ -0,0 +1,60 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AutoReplyItem, AutoReplyScope } from "../models/index.js"; + +export type UpdateAutoReplyPayload = { + id: number; + content: string; + isEnable: boolean; + startTime: number; + endTime: number; + scope: AutoReplyScope; + uids?: string | string[]; +}; + +export type UpdateAutoReplyResponse = { + item: AutoReplyItem; + version: number; +}; + +export const updateAutoReplyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/update`); + + /** + * Update auto reply + * + * @param payload payload + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function updateAutoReply(payload: UpdateAutoReplyPayload) { + const uids = Array.isArray(payload.uids) ? payload.uids : [payload.uids]; + const resultUids = payload.scope === 2 || payload.scope === 3 ? uids : []; + + const params = { + cliLang: ctx.language, + id: payload.id, + enable: payload.isEnable, + content: payload.content, + startTime: payload.startTime, + endTime: payload.endTime, + recurrence: ["RRULE:FREQ=DAILY;"], + scope: payload.scope, + uids: resultUids, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateCatalog.ts b/_libs/zca-js/src/apis/updateCatalog.ts new file mode 100644 index 0000000..45b4c2a --- /dev/null +++ b/_libs/zca-js/src/apis/updateCatalog.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { CatalogItem } from "../models/index.js"; + +export type UpdateCatalogPayload = { + catalogId: string; + catalogName: string; +}; + +export type UpdateCatalogResponse = { + item: CatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const updateCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/update`); + + /** + * Update catalog? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function updateCatalog(payload: UpdateCatalogPayload) { + const params = { + catalog_id: payload.catalogId, + catalog_name: payload.catalogName, + catalog_photo: "", // "" is defaut don't upload photo + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateGroupSettings.ts b/_libs/zca-js/src/apis/updateGroupSettings.ts new file mode 100644 index 0000000..f3c94d2 --- /dev/null +++ b/_libs/zca-js/src/apis/updateGroupSettings.ts @@ -0,0 +1,97 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateGroupSettingsOptions = { + /** + * Disallow group members to change the group name and avatar + */ + blockName?: boolean; + /** + * Highlight messages from owner/admins + */ + signAdminMsg?: boolean; + /** + * Don't pin messages, notes, and polls to the top of a conversation + */ + setTopicOnly?: boolean; + /** + * Allow new members to read most recent messages + */ + enableMsgHistory?: boolean; + /** + * Membership approval + */ + joinAppr?: boolean; + /** + * Disallow group members to create notes & reminders + */ + lockCreatePost?: boolean; + /** + * Disallow group members to create polls + */ + lockCreatePoll?: boolean; + /** + * Disallow group members to send messages + */ + lockSendMsg?: boolean; + /** + * Disallow group members to view full member list (community only) + */ + lockViewMember?: boolean; + + // bannFeature?: boolean; // not see in UI, not implemented + // addMemberOnly?: boolean; // not see in UI, not implemented + // dirtyMedia?: boolean; // not see in UI, not implemented + // banDuration?: boolean | number; // not see in UI, not implemented +}; + +export type UpdateGroupSettingsResponse = ""; + +export const updateGroupSettingsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/setting/update`); + + /** + * Update group settings + * + * @param options Options + * @param groupId Group ID + * + * @note Zalo might throw an error with code 166 if you don't have enough permissions to change the settings. + * + * @throws {ZaloApiError} + */ + return async function updateGroupSettings(options: UpdateGroupSettingsOptions, groupId: string) { + const params = { + blockName: options.blockName ? 1 : 0, + signAdminMsg: options.signAdminMsg ? 1 : 0, + + // addMemberOnly: 0, // very tricky, any idea? + + setTopicOnly: options.setTopicOnly ? 1 : 0, + enableMsgHistory: options.enableMsgHistory ? 1 : 0, + joinAppr: options.joinAppr ? 1 : 0, + lockCreatePost: options.lockCreatePost ? 1 : 0, + lockCreatePoll: options.lockCreatePoll ? 1 : 0, + lockSendMsg: options.lockSendMsg ? 1 : 0, + lockViewMember: options.lockViewMember ? 1 : 0, + + // default values for not implemented options + bannFeature: 0, + dirtyMedia: 0, + banDuration: 0, + blocked_members: [], + + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateHiddenConversPin.ts b/_libs/zca-js/src/apis/updateHiddenConversPin.ts new file mode 100644 index 0000000..e5de815 --- /dev/null +++ b/_libs/zca-js/src/apis/updateHiddenConversPin.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory, encryptPin } from "../utils.js"; + +export type UpdateHiddenConversPinResponse = ""; + +export const updateHiddenConversPinFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/update-pin`); + const pinRegex = /^\d{4}$/; + + /** + * Update hidden conversation pin + * + * @param pin The pin to update (must be a 4-digit number between 0000-9999) + * + * @throws {ZaloApiError} + */ + return async function updateHiddenConversPin(pin: string) { + if (!pinRegex.test(pin)) { + throw new ZaloApiError("Pin must be a 4-digit number between 0000-9999"); + } + + const encryptedPin = encryptPin(pin); + const params = { + new_pin: encryptedPin, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateLabels.ts b/_libs/zca-js/src/apis/updateLabels.ts new file mode 100644 index 0000000..cdb8c1a --- /dev/null +++ b/_libs/zca-js/src/apis/updateLabels.ts @@ -0,0 +1,54 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { LabelData } from "../models/index.js"; + +export type UpdateLabelsPayload = { + labelData: LabelData[]; + version: number; +}; + +export type UpdateLabelsResponse = { + labelData: LabelData[]; + version: number; + lastUpdateTime: number; +}; + +export const updateLabelsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/convlabel/update`); + + /** + * Update labels + * + * @param label label data + * + * @throws {ZaloApiError} + */ + return async function updateLabels(payload: UpdateLabelsPayload) { + const params = { + labelData: JSON.stringify(payload.labelData), + version: payload.version, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + const unFormatted = (await utils.resolve(response)) as unknown as Omit & { + labelData: string; + }; + + return { + labelData: JSON.parse(unFormatted.labelData), + version: unFormatted.version, + lastUpdateTime: unFormatted.lastUpdateTime, + }; + }; +}); diff --git a/_libs/zca-js/src/apis/updateLang.ts b/_libs/zca-js/src/apis/updateLang.ts new file mode 100644 index 0000000..4d9f246 --- /dev/null +++ b/_libs/zca-js/src/apis/updateLang.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export enum UpdateLangAvailableLanguages { + VI = "VI", + EN = "EN", +} + +export type UpdateLangResponse = ""; + +export const updateLangFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/updatelang`); + + /** + * Update language? + * + * @param language language to update (VI, EN) + * + * @note Calling this API alone will not update the user's language. + * @throws {ZaloApiError} + */ + return async function updateLang(language: UpdateLangAvailableLanguages = UpdateLangAvailableLanguages.VI) { + const params = { + language: language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateProductCatalog.ts b/_libs/zca-js/src/apis/updateProductCatalog.ts new file mode 100644 index 0000000..40b6f07 --- /dev/null +++ b/_libs/zca-js/src/apis/updateProductCatalog.ts @@ -0,0 +1,89 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AttachmentSource, ProductCatalogItem } from "../models/index.js"; + +export type UpdateProductCatalogPayload = { + catalogId: string; + + productId: string; + productName: string; + price: string; + description: string; + createTime: number; + + /** + * Upto 5 media files are allowed, will be ignored if product_photos is provided + */ + files?: AttachmentSource[]; + /** + * List of product photo URLs, upto 5 + * + * You can manually get the URL using `uploadProductPhoto` api + */ + product_photos?: string[]; +}; + +export type UpdateProductCatalogResponse = { + item: ProductCatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const updateProductCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/update`); + + /** + * Update product catalog? + * + * @param payload payload + * + * @note this API is used for zBusiness - Maximum 5 media files are supported + * @throws {ZaloApiError} + */ + return async function updateProductCatalog(payload: UpdateProductCatalogPayload) { + const productPhoto = payload.product_photos || []; + + if (payload.files && payload.files.length == 0) { + if (payload.files.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + for (const mediaFile of payload.files) { + const uploadMedia = await api.uploadProductPhoto({ + file: mediaFile, + }); + + const url = uploadMedia.normalUrl || uploadMedia.hdUrl; + productPhoto.push(url); + } + } + + if (productPhoto.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + const params = { + product_id: payload.productId, + product_name: payload.productName, + price: payload.price, + description: payload.description, + product_photos: productPhoto, + catalog_id: payload.catalogId, + currency_unit: "โ‚ซ", + create_time: payload.createTime, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateProfile.ts b/_libs/zca-js/src/apis/updateProfile.ts new file mode 100644 index 0000000..05720f9 --- /dev/null +++ b/_libs/zca-js/src/apis/updateProfile.ts @@ -0,0 +1,67 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { BusinessCategory, Gender } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateProfilePayload = { + profile: { + name: string; + /** + * Date of birth in the format YYYY-MM-DD + */ + dob: `${string}-${string}-${string}`; + gender: Gender; + }; + biz?: Partial<{ + cate: BusinessCategory; + description: string; + address: string; + website: string; + email: string; + }>; +}; + +export type UpdateProfileResponse = ""; + +export const updateProfileFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/update`); + + /** + * Change account setting information + * + * @param payload payload + * + * @note If your account is a Business Account, include the biz.cate field; otherwise the category will be removed. + * You may leave the other biz fields empty if you donโ€™t want to change them. + * + * @throws {ZaloApiError} + */ + return async function updateProfile(payload: UpdateProfilePayload) { + const params = { + profile: JSON.stringify({ + name: payload.profile.name, + dob: payload.profile.dob, + gender: payload.profile.gender, + }), + biz: JSON.stringify({ + desc: payload.biz?.description, + cate: payload.biz?.cate, + addr: payload.biz?.address, + website: payload.biz?.website, + email: payload.biz?.email, + }), + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateProfileBio.ts b/_libs/zca-js/src/apis/updateProfileBio.ts new file mode 100644 index 0000000..22d9533 --- /dev/null +++ b/_libs/zca-js/src/apis/updateProfileBio.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateProfileBioResponse = ""; + +export const updateProfileBioFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/status`); + + /** + * Update profile bio + * + * @param status status for update bio + * + * @throws {ZaloApiError} + */ + return async function updateProfileBio(status: string) { + const params = { + status: status, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateQuickMessage.ts b/_libs/zca-js/src/apis/updateQuickMessage.ts new file mode 100644 index 0000000..98ffedc --- /dev/null +++ b/_libs/zca-js/src/apis/updateQuickMessage.ts @@ -0,0 +1,80 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { QuickMessage, AttachmentSource } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateQuickMessagePayload = { + keyword: string; + title: string; + media?: AttachmentSource; +}; + +export type UpdateQuickMessageResponse = { + item: QuickMessage; + version: number; +}; + +export const updateQuickMessageFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/update`); + + /** + * Update quick message + * + * @param updatePayload - The payload containing data to update the quick message + * @param itemId - The id of the quick message to update + * + * @note Zalo might throw an error with code 212 if the itemId does not exist. + * + * @throws {ZaloApiError} + */ + return async function updateQuickMessage(updatePayload: UpdateQuickMessagePayload, itemId: number) { + const isType = !updatePayload.media ? 0 : 1; + + const params: Record = { + itemId: itemId, + keyword: updatePayload.keyword, + message: { + title: updatePayload.title, + params: "", + }, + type: isType, + }; + + if (isType === 1) { + if (!updatePayload.media) throw new ZaloApiError("Media is required"); + const uploadMedia = await api.uploadProductPhoto({ + file: updatePayload.media, + }); + + const photoId = uploadMedia.photoId; + const thumbUrl = uploadMedia.thumbUrl; + const normalUrl = uploadMedia.normalUrl; + const hdUrl = uploadMedia.hdUrl; + + params.media = { + items: [ + { + type: 0, + photoId: photoId, + title: "", + width: "", + height: "", + previewThumb: thumbUrl, + rawUrl: normalUrl || hdUrl, + thumbUrl: thumbUrl, + normalUrl: normalUrl || hdUrl, + hdUrl: hdUrl || normalUrl, + }, + ], + }; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateSettings.ts b/_libs/zca-js/src/apis/updateSettings.ts new file mode 100644 index 0000000..06c263a --- /dev/null +++ b/_libs/zca-js/src/apis/updateSettings.ts @@ -0,0 +1,96 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateSettingsResponse = ""; + +export enum UpdateSettingsType { + ViewBirthday = "view_birthday", + ShowOnlineStatus = "show_online_status", + DisplaySeenStatus = "display_seen_status", + ReceiveMessage = "receive_message", + AcceptCall = "accept_stranger_call", + AddFriendViaPhone = "add_friend_via_phone", + AddFriendViaQR = "add_friend_via_qr", + AddFriendViaGroup = "add_friend_via_group", + AddFriendViaContact = "add_friend_via_contact", + DisplayOnRecommendFriend = "display_on_recommend_friend", + ArchivedChat = "archivedChatStatus", + QuickMessage = "quickMessageStatus", +} + +export const updateSettingsFactory = apiFactory()((_api, _ctx, utils) => { + const serviceURL = utils.makeURL(`https://wpa.chat.zalo.me/api/setting/update`); + + /** + * Set account settings + * + * @param type The type of setting to update + * @param value + * + * ViewBirthday + * * 0: hide + * * 1: show full day/month/year + * * 2: show day/month + * + * ShowOnlineStatus + * * 0: hide + * * 1: show + * + * DisplaySeenStatus + * * 0: hide + * * 1: show + * + * ReceiveMessage + * * 1: everyone + * * 2: only friends + * + * AcceptCall + * * 2: only friends + * * 3: everyone + * * 4: friends and person who contacted + * + * AddFriendViaPhone + * * 0: disable + * * 1: enable + * + * AddFriendViaQR + * * 0: disable + * * 1: enable + * + * AddFriendViaGroup + * * 0: disable + * * 1: enable + * + * AddFriendViaContact + * * 0: disable + * * 1: enable + * + * DisplayOnRecommendFriend + * * 0: disable + * * 1: enable + * + * ArchivedChat + * * 0: disable + * * 1: enable + * + * QuickMessage + * * 0: disable + * * 1: enable + * + * @throws {ZaloApiError} + */ + return async function updateSettings(type: UpdateSettingsType, value: number) { + const params = { + [type]: value, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/upgradeGroupToCommunity.ts b/_libs/zca-js/src/apis/upgradeGroupToCommunity.ts new file mode 100644 index 0000000..9ab57d1 --- /dev/null +++ b/_libs/zca-js/src/apis/upgradeGroupToCommunity.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpgradeGroupToCommunityResponse = ""; + +export const upgradeGroupToCommunityFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/upgrade/community`); + + /** + * Upgrade group to community + * + * @param groupId Group ID upgrade + * + * @note Zalo account identity must be verified and the account must be over 18 to use + * @code 185 The limit on the number of communities that can own has been reached + * @throws {ZaloApiError} + */ + return async function upgradeGroupToCommunity(groupId: string) { + const params = { + grId: groupId, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/uploadAttachment.ts b/_libs/zca-js/src/apis/uploadAttachment.ts new file mode 100644 index 0000000..9cffb9a --- /dev/null +++ b/_libs/zca-js/src/apis/uploadAttachment.ts @@ -0,0 +1,366 @@ +import FormData from "form-data"; +import fs from "node:fs"; +import type { UploadCallback } from "../context.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType, type AttachmentSource } from "../models/index.js"; +import { + apiFactory, + getFileExtension, + getFileName, + getFileSize, + getImageMetaData, + getMd5LargeFileObject, + resolveResponse, +} from "../utils.js"; + +export type UploadAttachmentImageResponse = { + normalUrl: string; + photoId: string; + finished: number | boolean; + hdUrl: string; + thumbUrl: string; + clientFileId: number; + chunkId: number; + + fileType: "image"; + width: number; + height: number; + totalSize: number; + hdSize: number; +}; + +export type UploadAttachmentVideoResponse = { + finished: number | boolean; + clientFileId: number; + chunkId: number; + + fileType: "video"; + fileUrl: string; + fileId: string; + checksum: string; + totalSize: number; + fileName: string; +}; + +export type UploadAttachmentFileResponse = { + finished: number | boolean; + clientFileId: number; + chunkId: number; + + fileType: "others"; + fileUrl: string; + fileId: string; + checksum: string; + totalSize: number; + fileName: string; +}; + +export type ImageData = { + fileName: string; + totalSize: number | undefined; + width: number | undefined; + height: number | undefined; +}; + +export type FileData = { + fileName: string; + totalSize: number; +}; + +export type UploadAttachmentType = UploadAttachmentImageResponse | UploadAttachmentVideoResponse | UploadAttachmentFileResponse; +export type UploadAttachmentResponse = UploadAttachmentType[]; + +type RawResponse = { + finished: number | boolean; + clientFileId: number; + chunkId: number; + + fileId?: string; + photoId?: string; + + normalUrl?: string; + hdUrl?: string; + thumbUrl?: string; +}; + +type AttachmentData = + | { + filePath: string; + fileType: "image"; + chunkContent: FormData[]; + fileData: ImageData; + params: { + toid?: string; + grid?: string; + totalChunk: number; + fileName: string; + clientId: number; + totalSize: number; + imei: string; + isE2EE: number; + jxl: number; + chunkId: number; + }; + source: AttachmentSource; + } + | { + filePath: string; + fileType: "video" | "others"; + chunkContent: FormData[]; + fileData: FileData; + params: { + toid?: string; + grid?: string; + totalChunk: number; + fileName: string; + clientId: number; + totalSize: number; + imei: string; + isE2EE: number; + jxl: number; + chunkId: number; + }; + source: AttachmentSource; + }; + +const urlType = { + image: "photo_original/upload", + video: "asyncfile/upload", + others: "asyncfile/upload", +}; + +export const uploadAttachmentFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = `${api.zpwServiceMap.file[0]}/api`; + const { sharefile } = ctx.settings!.features; + + function isExceedMaxFile(totalFile: number) { + return totalFile > sharefile.max_file; + } + + function isExceedMaxFileSize(fileSize: number) { + return fileSize > sharefile.max_size_share_file_v3 * 1024 * 1024; + } + + function isExtensionValid(ext: string) { + return sharefile.restricted_ext_file.indexOf(ext) == -1; + } + + /** + * Upload an attachment to a thread + * + * @param sources path to files or attachment sources + * @param threadId Group or User ID + * @param type Message type (User or Group) + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function uploadAttachment( + sources: AttachmentSource | AttachmentSource[], + threadId: string, + type: ThreadType = ThreadType.User, + ): Promise { + if (!sources) throw new ZaloApiError("Missing sources"); + if (!Array.isArray(sources)) sources = [sources]; + if (sources.length == 0) throw new ZaloApiError("Missing sources"); + if (isExceedMaxFile(sources.length)) throw new ZaloApiError("Exceed maximum file of " + sharefile.max_file); + if (!threadId) throw new ZaloApiError("Missing threadId"); + + const chunkSize = ctx.settings!.features.sharefile.chunk_size_file; + const isGroupMessage = type == ThreadType.Group; + const attachmentsData: AttachmentData[] = []; + const url = `${serviceURL}/${isGroupMessage ? "group" : "message"}/`; + const typeParam = isGroupMessage ? "11" : "2"; + + let clientId = Date.now(); + for (const source of sources) { + const isFilePath = typeof source == "string"; + const isBuffer = typeof source == "object" && source.data instanceof Buffer; + + if (!isFilePath && !isBuffer) throw new ZaloApiError("Invalid source type"); + if (!isFilePath && !source.filename) throw new ZaloApiError("Missing filename"); + + if (isFilePath && !fs.existsSync(source)) throw new ZaloApiError("File not found"); + + const extFile = getFileExtension(isFilePath ? source : source.filename).toLowerCase(); + const fileName = isFilePath ? getFileName(source) : source.filename; + + if (isExtensionValid(extFile) == false) + throw new ZaloApiError(`File extension "${extFile}" is not allowed`); + + const data: AttachmentData = { + filePath: isFilePath ? source : source.filename, + chunkContent: [] as FormData[], + params: {}, + source, + } as AttachmentData; + + if (isGroupMessage) data.params.grid = threadId; + else data.params.toid = threadId; + + switch (extFile) { + case "jpg": + case "jpeg": + case "png": + case "webp": { + const imageData = isFilePath ? await getImageMetaData(ctx, source) : { ...source.metadata, fileName }; + if (isExceedMaxFileSize(imageData.totalSize!)) + throw new ZaloApiError( + `File ${fileName} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + data.fileData = imageData; + data.fileType = "image"; + + data.params.totalChunk = Math.ceil(data.fileData.totalSize! / chunkSize); + data.params.fileName = fileName; + data.params.clientId = clientId++; + data.params.totalSize = imageData.totalSize!; + data.params.imei = ctx.imei; + data.params.isE2EE = 0; + data.params.jxl = 0; + data.params.chunkId = 1; + + break; + } + case "mp4": { + const videoSize = isFilePath ? await getFileSize(source) : source.metadata.totalSize; + if (isExceedMaxFileSize(videoSize)) + throw new ZaloApiError( + `File ${fileName} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + data.fileType = "video"; + data.fileData = { + fileName, + totalSize: videoSize, + }; + + data.params.totalChunk = Math.ceil(data.fileData.totalSize! / chunkSize); + data.params.fileName = fileName; + data.params.clientId = clientId++; + data.params.totalSize = videoSize; + data.params.imei = ctx.imei; + data.params.isE2EE = 0; + data.params.jxl = 0; + data.params.chunkId = 1; + + break; + } + default: { + const fileSize = isFilePath ? await getFileSize(source) : source.metadata.totalSize; + if (isExceedMaxFileSize(fileSize)) + throw new ZaloApiError( + `File ${fileName} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + data.fileType = "others"; + data.fileData = { + fileName, + totalSize: fileSize, + }; + + data.params.totalChunk = Math.ceil(data.fileData.totalSize! / chunkSize); + data.params.fileName = fileName; + data.params.clientId = clientId++; + data.params.totalSize = fileSize; + data.params.imei = ctx.imei; + data.params.isE2EE = 0; + data.params.jxl = 0; + data.params.chunkId = 1; + + break; + } + } + + const fileBuffer = isFilePath ? await fs.promises.readFile(source) : source.data; + for (let i = 0; i < data.params.totalChunk; i++) { + const formData = new FormData(); + const slicedBuffer = fileBuffer.subarray(i * chunkSize, (i + 1) * chunkSize); + formData.append("chunkContent", slicedBuffer, { + filename: fileName, + contentType: "application/octet-stream", + }); + + data.chunkContent[i] = formData; + } + attachmentsData.push(data); + } + + const requests = [], + results: UploadAttachmentType[] = []; + + for (let atmIndex = 0; atmIndex < attachmentsData.length; atmIndex++) { + const data = attachmentsData[atmIndex]; + for (let i = 0; i < data.params.totalChunk; i++) { + const encryptedParams = utils.encodeAES(JSON.stringify(data.params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + requests.push( + utils + .request( + utils.makeURL(url + urlType[data.fileType], { type: typeParam, params: encryptedParams }), + { + method: "POST", + headers: data.chunkContent[i].getHeaders(), + body: data.chunkContent[i].getBuffer(), + }, + ) + .then(async (response) => { + /** + * @TODO: better type rather than any + */ + const resData = await resolveResponse(ctx, response); + + if (resData && resData.fileId != "-1" && resData.photoId != "-1") + await new Promise((resolve) => { + if (data.fileType == "video" || data.fileType == "others") { + const uploadCallback: UploadCallback = async (wsData) => { + const result = { + fileType: data.fileType, + ...resData, + ...wsData, + totalSize: data.fileData.totalSize, + fileName: data.fileData.fileName, + checksum: ( + await getMd5LargeFileObject(data.source, data.fileData.totalSize) + ).data, + }; + results[atmIndex] = result; + resolve(); + }; + + ctx.uploadCallbacks.set(resData.fileId!.toString(), uploadCallback); + } + + if (data.fileType == "image") { + const result = { + fileType: "image" as const, + width: data.fileData.width!, + height: data.fileData.height!, + totalSize: data.fileData.totalSize!, + hdSize: data.fileData.totalSize!, + + finished: resData.finished, + normalUrl: resData.normalUrl!, + hdUrl: resData.hdUrl!, + thumbUrl: resData.thumbUrl!, + chunkId: resData.chunkId, + + photoId: resData.photoId!, + clientFileId: resData.clientFileId, + }; + results[atmIndex] = result; + resolve(); + } + }); + }), + ); + data.params.chunkId++; + } + } + + await Promise.all(requests); + + return results; + }; +}); diff --git a/_libs/zca-js/src/apis/uploadProductPhoto.ts b/_libs/zca-js/src/apis/uploadProductPhoto.ts new file mode 100644 index 0000000..3cdbb8d --- /dev/null +++ b/_libs/zca-js/src/apis/uploadProductPhoto.ts @@ -0,0 +1,76 @@ +import fs from "node:fs"; +import FormData from "form-data"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory, getImageMetaData } from "../utils.js"; + +export type UploadProductPhotoPayload = { + file: AttachmentSource; +}; + +export type UploadProductPhotoResponse = { + normalUrl: string; + photoId: string; + finished: number; + hdUrl: string; + thumbUrl: string; + clientFileId: number; + chunkId: number; +}; + +export const uploadProductPhotoFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/product/upload/photo`); + + /** + * Upload product photo for api quick message, product catalog or custom local storage + * + * @param payload file path or attachment source + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function uploadProductPhoto(payload: UploadProductPhotoPayload) { + const isSourceFilePath = typeof payload.file == "string"; + const fileMetaData = isSourceFilePath + ? await getImageMetaData(ctx, payload.file as string) + : (payload.file as { metadata: { totalSize: number; width?: number; height?: number } }).metadata; + + const fileSize = fileMetaData.totalSize || 0; + + const fileBuffer = isSourceFilePath + ? await fs.promises.readFile(payload.file as string) + : (payload.file as { data: Buffer }).data; + + const formData = new FormData(); + formData.append("chunkContent", fileBuffer, { + filename: "undefined", + contentType: "application/octet-stream", + }); + + const params = { + totalChunk: 1, + fileName: `Base64_Img_Picker_${Date.now()}.jpg`, + clientId: Date.now(), + totalSize: fileSize, + imei: ctx.imei, + chunkId: 1, + toid: ctx.loginInfo.send2me_id, + featureId: 1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/votePoll.ts b/_libs/zca-js/src/apis/votePoll.ts new file mode 100644 index 0000000..56208c1 --- /dev/null +++ b/_libs/zca-js/src/apis/votePoll.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { PollOptions } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type VotePollResponse = { + options: PollOptions[]; +}; + +export const votePollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/vote`); + + /** + * Vote on a poll + * + * @param pollId The ID of the poll to vote on + * @param optionId The ID(s) of the option to vote on + * + * @throws ZaloApiError + */ + return async function votePoll(pollId: number, optionId: number | number[]) { + if (!Array.isArray(optionId)) optionId = [optionId]; + + const params = { + poll_id: pollId, + option_ids: optionId, // unvote = empty array + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/context.ts b/_libs/zca-js/src/context.ts new file mode 100644 index 0000000..21f2b20 --- /dev/null +++ b/_libs/zca-js/src/context.ts @@ -0,0 +1,216 @@ +import type { Agent } from "http"; +import type { CookieJar } from "tough-cookie"; + +type UploadEventData = { + fileUrl: string; + fileId: string; +}; + +export type UploadCallback = (data: UploadEventData) => unknown; + +type ShareFileSettings = { + big_file_domain_list: string[]; + max_size_share_file_v2: number; + max_size_share_file_v3: number; + file_upload_show_icon_1GB: boolean; + restricted_ext: string; + next_file_time: number; + max_file: number; + max_size_photo: number; + max_size_share_file: number; + max_size_resize_photo: number; + max_size_gif: number; + max_size_original_photo: number; + chunk_size_file: number; + restricted_ext_file: string[]; +}; + +type SocketSettings = { + rotate_error_codes: number[]; + retries: { + [key: string]: { + max?: number; + times: number[] | number; + }; + }; + debug: { + enable: boolean; + }; + ping_interval: number; + reset_endpoint: number; + queue_ctrl_actionid_map: { + "611_0": string; + "610_1": string; + "610_0": string; + "603_0": string; + "611_1": string; + }; + close_and_retry_codes: number[]; + max_msg_size: number; + enable_ctrl_socket: boolean; + reconnect_after_fallback: boolean; + enable_chat_socket: boolean; + submit_wss_log: boolean; + disable_lp: boolean; + offline_monitor: { + enable: boolean; + }; +}; + +type LoginInfo = { + // eslint-disable-next-line + [key: string]: any; + haspcclient: number; + public_ip: string; + language: string; + send2me_id: string; + zpw_service_map_v3: { + other_contact: string[]; + chat_e2e: string[]; + workspace: string[]; + catalog: string[]; + boards: string[]; + downloadStickerUrl: string[]; + sp_contact: string[]; + zcloud_up_file: string[]; + media_store_send2me: string[]; + push_act: string[]; + aext: string[]; + zfamily: string[]; + group_poll: string[]; + group_cloud_message: string[]; + media_store: string[]; + file: string[]; + auto_reply: string[]; + sync_action: string[]; + friendLan: string[]; + friend: string[]; + alias: string[]; + zimsg: string[]; + group_board: string[]; + conversation: string[]; + group: string[]; + fallback_LP: string[]; + friend_board: string[]; + up_file: string[]; + zavi: string[]; + reaction: string[]; + voice_call: string[]; + profile: string[]; + sticker: string[]; + label: string[]; + consent: string[]; + zcloud: string[]; + chat: string[]; + todoUrl: string[]; + recent_search: string[]; + group_e2e: string[]; + quick_message: string[]; + }; +}; + +type ExtraVer = { + phonebook: number; + conv_label: string; + friend: string; + ver_sticker_giphy_suggest: number; + ver_giphy_cate: number; + alias: string; + ver_sticker_cate_list: number; + block_friend: string; +}; + +export type ZPWServiceMap = LoginInfo["zpw_service_map_v3"]; + +export type AppContextBase = { + uid: string; + imei: string; + cookie: CookieJar; + userAgent: string; + language: string; + secretKey: string | null; + zpwServiceMap: ZPWServiceMap; + settings: { + // eslint-disable-next-line + [key: string]: any; + features: { + // eslint-disable-next-line + [key: string]: any; + sharefile: ShareFileSettings; + socket: SocketSettings; + }; + keepalive: { + alway_keepalive: number; + keepalive_duration: number; + time_deactive: number; + }; + }; + loginInfo: LoginInfo; + extraVer: ExtraVer; +}; + +export type ImageMetadataGetterResponse = { width: number; height: number; size: number } | null; +export type ImageMetadataGetter = (filePath: string) => Promise; + +export type Options = { + selfListen: boolean; + checkUpdate: boolean; + logging: boolean; + + apiType: number; + apiVersion: number; + + agent?: Agent; + + /** + * Optional fetch implementation for polyfills in non-standard environments. + * If using proxy, `node-fetch` is highly recommended. + */ + polyfill: typeof fetch; + + imageMetadataGetter?: ImageMetadataGetter; +}; + +const _5_MINUTES = 5 * 60 * 1000; +class CallbacksMap extends Map { + /** + * @param ttl Time to live in milliseconds. Default is 5 minutes. + */ + set(key: string, value: UploadCallback, ttl: number = _5_MINUTES): this { + setTimeout(() => { + this.delete(key); + }, ttl); + return super.set(key, value); + } +} + +export type AppContextExtended = { + uploadCallbacks: CallbacksMap; + options: Options; + readonly API_TYPE: number; + readonly API_VERSION: number; +}; + +export type ContextBase = Partial & AppContextExtended; + +export const createContext = (apiType = 30, apiVersion = 671) => + ({ + API_TYPE: apiType, + API_VERSION: apiVersion, + uploadCallbacks: new CallbacksMap(), + options: { + selfListen: false, + checkUpdate: true, + logging: true, + polyfill: global.fetch, + }, + secretKey: null, + }) as ContextBase; + +export type ContextSession = AppContextBase & AppContextExtended & { secretKey: string }; + +export function isContextSession(ctx: ContextBase): ctx is ContextSession { + return !!ctx.secretKey; +} + +export const MAX_MESSAGES_PER_SEND = 50; diff --git a/_libs/zca-js/src/index.ts b/_libs/zca-js/src/index.ts new file mode 100644 index 0000000..ce84709 --- /dev/null +++ b/_libs/zca-js/src/index.ts @@ -0,0 +1,163 @@ +export * from "./Errors/index.js"; +export * from "./models/index.js"; +export * from "./zalo.js"; + +export type { ContextSession, ContextBase, AppContextExtended, AppContextBase, Options, ZPWServiceMap, ImageMetadataGetter,ImageMetadataGetterResponse } from "./context.js"; + +// API TYPES +export type { AcceptFriendRequestResponse } from "./apis/acceptFriendRequest.js"; +export type { AddGroupBlockedMemberResponse } from "./apis/addGroupBlockedMember.js"; +export type { AddGroupDeputyResponse } from "./apis/addGroupDeputy.js"; +export type { AddPollOptionsPayload, AddPollOptionsResponse, AddPollOptionsOption } from "./apis/addPollOptions.js"; +export type { AddQuickMessagePayload, AddQuickMessageResponse } from "./apis/addQuickMessage.js"; +export type { AddReactionDestination, AddReactionResponse, CustomReaction } from "./apis/addReaction.js"; +export type { AddUnreadMarkResponse } from "./apis/addUnreadMark.js"; +export type { AddUserToGroupResponse } from "./apis/addUserToGroup.js"; +export type { BlockUserResponse } from "./apis/blockUser.js"; +export type { BlockViewFeedResponse } from "./apis/blockViewFeed.js"; +export type { ChangeAccountAvatarResponse } from "./apis/changeAccountAvatar.js"; +export type { ChangeFriendAliasResponse } from "./apis/changeFriendAlias.js"; +export type { ChangeGroupAvatarResponse } from "./apis/changeGroupAvatar.js"; +export type { ChangeGroupNameResponse } from "./apis/changeGroupName.js"; +export type { ChangeGroupOwnerResponse } from "./apis/changeGroupOwner.js"; +export type { CreateAutoReplyPayload, CreateAutoReplyResponse } from "./apis/createAutoReply.js"; +export type { CreateCatalogResponse } from "./apis/createCatalog.js"; +export type { CreateGroupOptions, CreateGroupResponse } from "./apis/createGroup.js"; +export type { CreateNoteOptions, CreateNoteResponse } from "./apis/createNote.js"; +export type { CreatePollOptions, CreatePollResponse } from "./apis/createPoll.js"; +export type { CreateProductCatalogPayload, CreateProductCatalogResponse } from "./apis/createProductCatalog.js"; +export type { CreateReminderOptions, CreateReminderResponse, CreateReminderUser, CreateReminderGroup } from "./apis/createReminder.js"; +export type { DeleteAutoReplyResponse } from "./apis/deleteAutoReply.js"; +export type { DeleteAvatarResponse } from "./apis/deleteAvatar.js"; +export type { DeleteCatalogResponse } from "./apis/deleteCatalog.js"; +export type { DeleteChatLastMessage, DeleteChatResponse } from "./apis/deleteChat.js"; +export type { DeleteGroupInviteBoxResponse } from "./apis/deleteGroupInviteBox.js"; +export type { DeleteMessageDestination, DeleteMessageResponse } from "./apis/deleteMessage.js"; +export type { DeleteProductCatalogPayload, DeleteProductCatalogResponse } from "./apis/deleteProductCatalog.js"; +export type { DisableGroupLinkResponse } from "./apis/disableGroupLink.js"; +export type { DisperseGroupResponse } from "./apis/disperseGroup.js"; +export type { EditNoteOptions, EditNoteResponse } from "./apis/editNote.js"; +export type { EditReminderGroup, EditReminderUser, EditReminderOptions, EditReminderResponse } from "./apis/editReminder.js"; +export type { EnableGroupLinkResponse } from "./apis/enableGroupLink.js"; +export type { FetchAccountInfoResponse } from "./apis/fetchAccountInfo.js"; +export type { FindUserResponse } from "./apis/findUser.js"; +export type { FindUserByUsernameResponse } from "./apis/findUserByUsername.js"; +export type { ForwardMessageSuccess, ForwardMessageFail, ForwardMessagePayload, ForwardMessageResponse } from "./apis/forwardMessage.js"; +export type { GetAliasListResponse } from "./apis/getAliasList.js"; +export type { GetAllFriendsResponse } from "./apis/getAllFriends.js"; +export type { GetAllGroupsResponse } from "./apis/getAllGroups.js"; +export type { GetArchivedChatListResponse } from "./apis/getArchivedChatList.js"; +export type { GetAutoDeleteChatResponse } from "./apis/getAutoDeleteChat.js"; +export type { GetAutoReplyListResponse } from "./apis/getAutoReplyList.js"; +export type { GetAvatarListResponse } from "./apis/getAvatarList.js"; +export type { GetAvatarUrlProfileResponse } from "./apis/getAvatarUrlProfile.js"; +export type { GetBizAccountResponse } from "./apis/getBizAccount.js"; +export type { GetCatalogListPayload, GetCatalogListResponse } from "./apis/getCatalogList.js"; +export type { GetCloseFriendsResponse } from "./apis/getCloseFriends.js"; +export type { GetFriendBoardListResponse } from "./apis/getFriendBoardList.js"; +export type { GetFriendOnlinesResponse, GetFriendOnlinesStatus } from "./apis/getFriendOnlines.js"; +export type { GetFriendRecommendationsResponse, FriendRecommendationsCollapseMsgListConfig, FriendRecommendationsDataInfo, FriendRecommendationsRecommItem } from "./apis/getFriendRecommendations.js"; +export type { GetFriendRequestStatusResponse } from "./apis/getFriendRequestStatus.js"; +export type { GetFullAvatarResponse } from "./apis/getFullAvatar.js"; +export type { GetGroupBlockedMemberPayload, GetGroupBlockedMemberResponse } from "./apis/getGroupBlockedMember.js"; +export type { GetGroupChatHistoryResponse } from "./apis/getGroupChatHistory.js"; +export type { GroupInfoPendingApprove, GroupInfoResponse } from "./apis/getGroupInfo.js"; +export type { GetGroupInviteBoxInfoPayload, GetGroupInviteBoxInfoResponse } from "./apis/getGroupInviteBoxInfo.js"; +export type { GetGroupInviteBoxListPayload, GetGroupInviteBoxListResponse } from "./apis/getGroupInviteBoxList.js"; +export type { GetGroupLinkDetailResponse } from "./apis/getGroupLinkDetail.js"; +export type { GetGroupLinkInfoPayload, GetGroupLinkInfoResponse } from "./apis/getGroupLinkInfo.js"; +export type { GetGroupMembersInfoResponse, GroupMemberProfile } from "./apis/getGroupMembersInfo.js"; +export type { GetHiddenConversationsResponse } from "./apis/getHiddenConversations.js"; +export type { GetLabelsResponse } from "./apis/getLabels.js"; +export type { BoardItem, GetListBoardResponse, ListBoardOptions } from "./apis/getListBoard.js"; +export type { GetListReminderResponse, ListReminderOptions, ReminderListGroup, ReminderListUser } from "./apis/getListReminder.js"; +export type { GetMultiUsersByPhonesResponse } from "./apis/getMultiUsersByPhones.js"; +export type { GetMuteResponse, MuteEntriesInfo } from "./apis/getMute.js"; +export type { GetPendingGroupMembersResponse, GetPendingGroupMembersUserInfo } from "./apis/getPendingGroupMembers.js"; +export type { GetPinConversationsResponse } from "./apis/getPinConversations.js"; +export type { PollDetailResponse } from "./apis/getPollDetail.js"; +export type { GetProductCatalogListPayload, GetProductCatalogListResponse } from "./apis/getProductCatalogList.js"; +export type { GetQRResponse } from "./apis/getQR.js"; +export type { GetQuickMessageListResponse } from "./apis/getQuickMessageList.js"; +export type { GetRelatedFriendGroupResponse } from "./apis/getRelatedFriendGroup.js"; +export type { GetReminderResponse } from "./apis/getReminder.js"; +export type { GetReminderResponsesResponse } from "./apis/getReminderResponses.js"; +export type { GetSentFriendRequestResponse, SentFriendRequestInfo } from "./apis/getSentFriendRequest.js"; +export type { GetSettingsResponse } from "./apis/getSettings.js"; +export type { GetStickerCategoryDetailResponse } from "./apis/getStickerCategoryDetail.js"; +export type { StickerDetailResponse } from "./apis/getStickersDetail.js"; +export type { GetUnreadMarkResponse, UnreadMark } from "./apis/getUnreadMark.js"; +export type { ProfileInfo, UserInfoResponse } from "./apis/getUserInfo.js"; +export type { InviteUserToGroupsResponse } from "./apis/inviteUserToGroups.js"; +export type { JoinGroupInviteBoxResponse } from "./apis/joinGroupInviteBox.js"; +export type { JoinGroupLinkResponse } from "./apis/joinGroupLink.js"; +export type { KeepAliveResponse } from "./apis/keepAlive.js"; +export type { LastOnlineResponse } from "./apis/lastOnline.js"; +export type { LeaveGroupResponse } from "./apis/leaveGroup.js"; +export type { LockPollResponse } from "./apis/lockPoll.js"; +export type { LoginQRCallback, LoginQRCallbackEvent } from "./apis/loginQR.js"; +export type { ParseLinkErrorMaps, ParseLinkResponse } from "./apis/parseLink.js"; +export type { RejectFriendRequestResponse } from "./apis/rejectFriendRequest.js"; +export type { RemoveFriendResponse } from "./apis/removeFriend.js"; +export type { RemoveFriendAliasResponse } from "./apis/removeFriendAlias.js"; +export type { RemoveGroupBlockedMemberResponse } from "./apis/removeGroupBlockedMember.js"; +export type { RemoveGroupDeputyResponse } from "./apis/removeGroupDeputy.js"; +export type { RemoveQuickMessageResponse } from "./apis/removeQuickMessage.js"; +export type { RemoveReminderResponse } from "./apis/removeReminder.js"; +export type { RemoveUnreadMarkResponse } from "./apis/removeUnreadMark.js"; +export type { RemoveUserFromGroupResponse } from "./apis/removeUserFromGroup.js"; +export type { ResetHiddenConversPinResponse } from "./apis/resetHiddenConversPin.js"; +export type { ReuseAvatarResponse } from "./apis/reuseAvatar.js"; +export type { ReviewPendingMemberRequestPayload, ReviewPendingMemberRequestResponse } from "./apis/reviewPendingMemberRequest.js"; +export type { SearchStickerResponse } from "./apis/searchSticker.js"; +export type { SendBankCardPayload, SendBankCardResponse } from "./apis/sendBankCard.js"; +export type { SendCardOptions, SendCardResponse } from "./apis/sendCard.js"; +export type { SendDeliveredEventMessageParams, SendDeliveredEventResponse } from "./apis/sendDeliveredEvent.js"; +export type { SendFriendRequestResponse } from "./apis/sendFriendRequest.js"; +export type { SendLinkOptions, SendLinkResponse } from "./apis/sendLink.js"; +export type { Mention, MessageContent, SendMessageQuote, SendMessageResponse, SendMessageResult, Style } from "./apis/sendMessage.js"; +export type { SendReportOptions, SendReportResponse } from "./apis/sendReport.js"; +export type { SendSeenEventMessageParams, SendSeenEventResponse } from "./apis/sendSeenEvent.js"; +export type { SendStickerResponse, SendStickerPayload } from "./apis/sendSticker.js"; +export type { SendTypingEventResponse } from "./apis/sendTypingEvent.js"; +export type { SendVideoOptions, SendVideoResponse } from "./apis/sendVideo.js"; +export type { SendVoiceOptions, SendVoiceResponse } from "./apis/sendVoice.js"; +export type { SetHiddenConversationsResponse } from "./apis/setHiddenConversations.js"; +export type { SetMuteParams, SetMuteResponse } from "./apis/setMute.js"; +export type { SetPinnedConversationsResponse } from "./apis/setPinnedConversations.js"; +export type { SharePollResponse } from "./apis/sharePoll.js"; +export type { UnBlockUserResponse } from "./apis/unblockUser.js"; +export type { UndoPayload, UndoResponse } from "./apis/undo.js"; +export type { UndoFriendRequestResponse } from "./apis/undoFriendRequest.js"; +export type { UpdateActiveStatusResponse } from "./apis/updateActiveStatus.js"; +export type { UpdateArchivedChatListTarget, UpdateArchivedChatListResponse } from "./apis/updateArchivedChatList.js"; +export type { UpdateAutoDeleteChatResponse } from "./apis/updateAutoDeleteChat.js"; +export type { UpdateAutoReplyPayload, UpdateAutoReplyResponse } from "./apis/updateAutoReply.js"; +export type { UpdateCatalogPayload, UpdateCatalogResponse } from "./apis/updateCatalog.js"; +export type { UpdateGroupSettingsOptions, UpdateGroupSettingsResponse } from "./apis/updateGroupSettings.js"; +export type { UpdateHiddenConversPinResponse } from "./apis/updateHiddenConversPin.js"; +export type { UpdateLabelsPayload, UpdateLabelsResponse } from "./apis/updateLabels.js"; +export type { UpdateLangResponse } from "./apis/updateLang.js"; +export type { UpdateProductCatalogPayload, UpdateProductCatalogResponse } from "./apis/updateProductCatalog.js"; +export type { UpdateProfilePayload, UpdateProfileResponse } from "./apis/updateProfile.js"; +export type { UpdateProfileBioResponse } from "./apis/updateProfileBio.js"; +export type { UpdateQuickMessagePayload, UpdateQuickMessageResponse } from "./apis/updateQuickMessage.js"; +export type { UpdateSettingsResponse } from "./apis/updateSettings.js"; +export type { UpgradeGroupToCommunityResponse } from "./apis/upgradeGroupToCommunity.js"; +export type { FileData, ImageData, UploadAttachmentResponse, UploadAttachmentType, UploadAttachmentImageResponse, UploadAttachmentVideoResponse, UploadAttachmentFileResponse } from "./apis/uploadAttachment.js"; +export type { UploadProductPhotoPayload, UploadProductPhotoResponse } from "./apis/uploadProductPhoto.js"; +export type { VotePollResponse } from "./apis/votePoll.js"; + +export type { CustomAPICallback, CustomAPIProps } from "./apis/custom.js"; + +// Others (Enum) +export { CloseReason } from "./apis/listen.js"; +export { LoginQRCallbackEventType } from "./apis/loginQR.js"; +export { FriendRecommendationsType } from "./apis/getFriendRecommendations.js"; +export { ReviewPendingMemberRequestStatus } from "./apis/reviewPendingMemberRequest.js"; +export { TextStyle, Urgency } from "./apis/sendMessage.js"; +export { ReportReason } from "./apis/sendReport.js"; +export { MuteAction, MuteDuration } from "./apis/setMute.js"; +export { ChatTTL } from "./apis/updateAutoDeleteChat.js"; +export { UpdateLangAvailableLanguages } from "./apis/updateLang.js"; +export { UpdateSettingsType } from "./apis/updateSettings.js"; diff --git a/_libs/zca-js/src/models/Attachment.ts b/_libs/zca-js/src/models/Attachment.ts new file mode 100644 index 0000000..e65b27b --- /dev/null +++ b/_libs/zca-js/src/models/Attachment.ts @@ -0,0 +1,11 @@ +export type AttachmentSource = + | string + | { + data: Buffer; + filename: `${string}.${string}`; + metadata: { + totalSize: number; + width?: number; + height?: number; + }; + }; diff --git a/_libs/zca-js/src/models/AutoReply.ts b/_libs/zca-js/src/models/AutoReply.ts new file mode 100644 index 0000000..a59372d --- /dev/null +++ b/_libs/zca-js/src/models/AutoReply.ts @@ -0,0 +1,21 @@ +export type AutoReplyItem = { + id: number; + weight: number; + enable: boolean; + modifiedTime: number; + startTime: number; + endTime: number; + content: string; + scope: AutoReplyScope; + uids: string[] | null; + ownerId: number; + recurrence: string[]; + createdTime: number; +}; + +export enum AutoReplyScope { + Everyone = 0, + Stranger = 1, + SpecificFriends = 2, + FriendsExcept = 3 +} diff --git a/_libs/zca-js/src/models/Board.ts b/_libs/zca-js/src/models/Board.ts new file mode 100644 index 0000000..7f6a78f --- /dev/null +++ b/_libs/zca-js/src/models/Board.ts @@ -0,0 +1,64 @@ +export enum BoardType { + Note = 1, + PinnedMessage = 2, + Poll = 3, +} + +export type PollDetail = { + creator: string; + question: string; + options: PollOptions[]; + joined: boolean; + closed: boolean; + poll_id: number; + allow_multi_choices: boolean; + allow_add_new_option: boolean; + is_anonymous: boolean; + poll_type: number; + created_time: number; + updated_time: number; + expired_time: number; + is_hide_vote_preview: boolean; + num_vote: number; +}; + +export type PollOptions = { + content: string; + votes: number; + voted: boolean; + voters: string[]; + option_id: number; +}; + +export type NoteDetail = { + id: string; + type: number; + color: number; + emoji: string; + startTime: number; + duration: number; + params: { + title: string; + extra?: string; + }; + creatorId: string; + editorId: string; + createTime: number; + editTime: number; + repeat: number; +}; + +export type PinnedMessageDetail = { + id: string; + type: number; + color: number; + emoji: string; + startTime: number; + duration: number; + params: Record; // @TODO: Define a more specific type + creatorId: string; + editorId: string; + createTime: number; + editTime: number; + repeat: number; +}; diff --git a/_libs/zca-js/src/models/Catalog.ts b/_libs/zca-js/src/models/Catalog.ts new file mode 100644 index 0000000..8f98b22 --- /dev/null +++ b/_libs/zca-js/src/models/Catalog.ts @@ -0,0 +1,16 @@ +export type CatalogItem = { + id: string; + name: string; + version: number; + ownerId: string; + isDefault: boolean; + /** + * Relative path used to build the catalog URL. + * + * Example: `https://catalog.zalo.me/${path}` + */ + path: string; + catalogPhoto: string | null; + totalProduct: number; + created_time: number; +}; diff --git a/_libs/zca-js/src/models/DeliveredMessage.ts b/_libs/zca-js/src/models/DeliveredMessage.ts new file mode 100644 index 0000000..5ed3b29 --- /dev/null +++ b/_libs/zca-js/src/models/DeliveredMessage.ts @@ -0,0 +1,44 @@ +import { ThreadType } from "./Enum.js"; + +export type TDeliveredMessage = { + msgId: string; + seen: number; + deliveredUids: string[]; + seenUids: string[]; + realMsgId: string; + mSTs: number; +}; + +export type TGroupDeliveredMessage = TDeliveredMessage & { + groupId: string; +}; + +export class UserDeliveredMessage { + type: ThreadType.User = ThreadType.User; + + data: TDeliveredMessage; + threadId: string; + isSelf: false; + + constructor(data: TDeliveredMessage) { + this.data = data; + this.threadId = data.deliveredUids[0]; + this.isSelf = false; + } +} + +export class GroupDeliveredMessage { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupDeliveredMessage; + threadId: string; + isSelf: boolean; + + constructor(uid: string, data: TGroupDeliveredMessage) { + this.data = data; + this.threadId = data.groupId; + this.isSelf = data.deliveredUids.includes(uid); + } +} + +export type DeliveredMessage = UserDeliveredMessage | GroupDeliveredMessage; diff --git a/_libs/zca-js/src/models/Enum.ts b/_libs/zca-js/src/models/Enum.ts new file mode 100644 index 0000000..2f6cdbf --- /dev/null +++ b/_libs/zca-js/src/models/Enum.ts @@ -0,0 +1,255 @@ +export enum ThreadType { + User, + Group, +} + +export enum DestType { + Group = 1, + User = 3, + Page = 5, +} + +export enum Gender { + Male = 0, + Female = 1, +} + +export enum AvatarSize { + Small = 120, + Large = 240, +} + +/** + * @note Bank codes list after Mitm on Mobile and Bank's supported by Zalo + * @documents https://developers.zalo.me/docs/zalo-notification-service/phu-luc/danh-sach-bin-code - docs missing bin code and short_name bank + */ +export enum BinBankCard { + /** + * NH TMCP An Bรฌnh + */ + ABBank = 970425, + /** + * NH TMCP ร Chรขu + */ + ACB = 970416, + /** + * NH Nรดng nghiแป‡p vร  Phรกt triแปƒn Nรดng thรดn Viแป‡t Nam + */ + Agribank = 970405, + /** + * NH TMCP ฤแบงu tฦฐ vร  Phรกt triแปƒn Viแป‡t Nam + */ + BIDV = 970418, + /** + * NH TMCP Bแบฃn Viแป‡t + */ + BVBank = 970454, + /** + * NH TMCP Bแบฏc ร + */ + BacA_Bank = 970409, + /** + * NH TMCP Bแบฃo Viแป‡t + */ + BaoViet_Bank = 970438, + /** + * NH sแป‘ CAKE by VPBank - TMCP Viแป‡t Nam Thแป‹nh Vฦฐแปฃng + */ + CAKE = 546034, + /** + * NH Thฦฐฦกng mแบกi TNHH MTV Xรขy dแปฑng Viแป‡t Nam + */ + CB_Bank = 970444, + /** + * NH TNHH MTV CIMB Viแป‡t Nam + */ + CIMB_Bank = 422589, + /** + * NH Hแปฃp tรกc xรฃ Viแป‡t Nam + */ + Coop_Bank = 970446, + /** + * NH TNHH MTV Phรกt triแปƒn Singapore - CN TP. Hแป“ Chรญ Minh + */ + DBS_Bank = 796500, + /** + * NH TMCP ฤรดng ร + */ + DongA_Bank = 970406, + /** + * NH TMCP Xuแบฅt Nhแบญp khแบฉu Viแป‡t Nam + */ + Eximbank = 970431, + /** + * NH TMCP Dแบงu khรญ Toร n cแบงu + */ + GPBank = 970408, + /** + * NH TMCP Phรกt triแปƒn TP. Hแป“ Chรญ Minh + */ + HDBank = 970437, + /** + * NH TNHH MTV HSBC (Viแป‡t Nam) + */ + HSBC = 458761, + /** + * NH TNHH MTV Hong Leong Viแป‡t Nam + */ + HongLeong_Bank = 970442, + /** + * NH Cรดng nghiแป‡p Hร n Quแป‘c - CN TP. Hแป“ Chรญ Minh + */ + IBK_HCM = 970456, + /** + * NH Cรดng nghiแป‡p Hร n Quแป‘c - CN Hร  Nแป™i + */ + IBK_HN = 970455, + /** + * NH TNHH Indovina + */ + Indovina_Bank = 970434, + /** + * NH ฤแบกi chรบng TNHH Kasikornbank - CN TP. Hแป“ Chรญ Minh + */ + KBank = 668888, + /** + * NH TMCP Kiรชn Long + */ + KienlongBank = 970452, + /** + * NH Kookmin - CN TP. Hแป“ Chรญ Minh + */ + Kookmin_Bank_HCM = 970463, + /** + * NH Kookmin - CN Hร  Nแป™i + */ + Kookmin_Bank_HN = 970462, + /** + * NH TMCP Lแป™c Phรกt Viแป‡t Nam + */ + LPBank = 970449, + /** + * NH TMCP Quรขn ฤ‘แป™i + */ + MB_Bank = 970422, + /** + * NH TMCP Hร ng Hแบฃi + */ + MSB = 970426, + /** + * NH TMCP Quแป‘c Dรขn + */ + NCB = 970419, + /** + * NH TMCP Nam ร + */ + Nam_A_Bank = 970428, + /** + * NH Nonghyup - CN Hร  Nแป™i + */ + NongHyup_Bank = 801011, + /** + * NH TMCP Phฦฐฦกng ฤรดng + */ + OCB = 970448, + /** + * NH Thฦฐฦกng mแบกi TNHH MTV ฤแบกi Dฦฐฦกng + */ + Ocean_Bank = 970414, + /** + * NH TMCP Thแป‹nh vฦฐแปฃng vร  Phรกt triแปƒn + */ + PGBank = 970430, + /** + * NH TMCP ฤแบกi Chรบng Viแป‡t Nam + */ + PVcomBank = 970412, + /** + * NH TNHH MTV Public Viแป‡t Nam + */ + Public_Bank_Vietnam = 970439, + /** + * NH TMCP Sร i Gรฒn + */ + SCB = 970429, + /** + * NH TMCP Sร i Gรฒn - Hร  Nแป™i + */ + SHB = 970443, + /** + * NH TMCP Sร i Gรฒn Thฦฐฦกng Tรญn + */ + Sacombank = 970403, + /** + * NH TMCP Sร i Gรฒn Cรดng Thฦฐฦกng + */ + Saigon_Bank = 970400, + /** + * NH TMCP ฤรดng Nam ร + */ + SeABank = 970440, + /** + * NH TNHH MTV Shinhan Viแป‡t Nam + */ + Shinhan_Bank = 970424, + /** + * NH TNHH MTV Standard Chartered Bank Viแป‡t Nam + */ + Standard_Chartered_Vietnam = 970410, + /** + * NH sแป‘ TNEX + */ + TNEX = 9704261, + /** + * NH TMCP Tiรชn Phong + */ + TPBank = 970423, + /** + * NH TMCP Kแปน thฦฐฦกng Viแป‡t Nam + */ + Techcombank = 970407, + /** + * NH sแป‘ Timo by Bแบฃn Viแป‡t Bank + */ + Timo = 963388, + /** + * NH sแป‘ UBank by VPBank + */ + UBank = 546035, + /** + * NH United Overseas Bank Viแป‡t Nam + */ + United_Overseas_Bank_Vietnam = 970458, + /** + * NH TMCP Quแป‘c tแบฟ Viแป‡t Nam + */ + VIB = 970441, + /** + * NH TMCP Viแป‡t Nam Thแป‹nh Vฦฐแปฃng + */ + VPBank = 970432, + /** + * NH Liรชn doanh Viแป‡t - Nga + */ + VRB = 970421, + /** + * NH TMCP Viแป‡t ร + */ + VietABank = 970427, + /** + * NH TMCP Viแป‡t Nam Thฦฐฦกng Tรญn + */ + VietBank = 970433, + /** + * NH TMCP Ngoแบกi Thฦฐฦกng Viแป‡t Nam + */ + Vietcombank = 970436, + /** + * NH TMCP Cรดng thฦฐฦกng Viแป‡t Nam + */ + VietinBank = 970415, + /** + * NH TNHH MTV Woori Viแป‡t Nam + */ + Woori_Bank = 970457, +} diff --git a/_libs/zca-js/src/models/FriendEvent.ts b/_libs/zca-js/src/models/FriendEvent.ts new file mode 100644 index 0000000..c87cfa7 --- /dev/null +++ b/_libs/zca-js/src/models/FriendEvent.ts @@ -0,0 +1,205 @@ +export enum FriendEventType { + ADD, + REMOVE, + + REQUEST, + UNDO_REQUEST, + REJECT_REQUEST, + + SEEN_FRIEND_REQUEST, + + BLOCK, + UNBLOCK, + BLOCK_CALL, + UNBLOCK_CALL, + + PIN_UNPIN, + PIN_CREATE, + + UNKNOWN, +} + +export type TFriendEventBase = string; + +export type TFriendEventRejectUndo = { + toUid: string; + fromUid: string; +}; + +export type TFriendEventRequest = { + toUid: string; + fromUid: string; + src: number; + message: string; +}; + +export type TFriendEventSeenRequest = string[]; + +export type TFriendEventPinCreateTopicParams = { + senderUid: string; + senderName: string; + client_msg_id: string; + global_msg_id: string; + msg_type: number; + title: string; +}; + +export type TFriendEventPinTopic = { + topicId: string; + topicType: number; +}; + +export type TFriendEventPinCreateTopic = { + type: number; + color: number; + emoji: string; + startTime: number; + duration: number; + params: TFriendEventPinCreateTopicParams; + id: string; + creatorId: string; + createTime: number; + editorId: string; + editTime: number; + repeat: number; + action: number; +}; + +export type TFriendEventPinCreate = { + oldTopic?: TFriendEventPinTopic; + topic: TFriendEventPinCreateTopic; + actorId: string; + oldVersion: number; + version: number; + conversationId: string; +}; + +export type TFriendEventPinUnpin = { + topic: TFriendEventPinTopic; + actorId: string; + oldVersion: number; + version: number; + conversationId: string; +}; + +export type TFriendEvent = + | TFriendEventBase + | TFriendEventRequest + | TFriendEventRejectUndo + | TFriendEventSeenRequest + | TFriendEventPinUnpin + | TFriendEventPinCreate; + +export type FriendEvent = + | { + type: + | FriendEventType.ADD + | FriendEventType.REMOVE + | FriendEventType.BLOCK + | FriendEventType.UNBLOCK + | FriendEventType.BLOCK_CALL + | FriendEventType.UNBLOCK_CALL; + data: TFriendEventBase; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.REJECT_REQUEST | FriendEventType.UNDO_REQUEST; + data: TFriendEventRejectUndo; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.REQUEST; + data: TFriendEventRequest; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.SEEN_FRIEND_REQUEST; + data: TFriendEventSeenRequest; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.PIN_CREATE; + data: TFriendEventPinCreate; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.PIN_UNPIN; + data: TFriendEventPinUnpin; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.UNKNOWN; + data: string; + threadId: string; + isSelf: boolean; + }; + +export function initializeFriendEvent(uid: string, data: TFriendEvent, type: FriendEventType): FriendEvent { + if ( + type == FriendEventType.ADD || + type == FriendEventType.REMOVE || + type == FriendEventType.BLOCK || + type == FriendEventType.UNBLOCK || + type == FriendEventType.BLOCK_CALL || + type == FriendEventType.UNBLOCK_CALL + ) { + return { + type, + data: data as TFriendEventBase, + threadId: data as string, + isSelf: ![FriendEventType.ADD, FriendEventType.REMOVE].includes(type), + }; + } else if (type == FriendEventType.REJECT_REQUEST || type == FriendEventType.UNDO_REQUEST) { + const threadId = (data as TFriendEventRejectUndo).toUid; + return { + type, + data: data as TFriendEventRejectUndo, + threadId, + isSelf: (data as TFriendEventRequest).fromUid == uid, + }; + } else if (type == FriendEventType.REQUEST) { + const threadId = (data as TFriendEventRequest).toUid; + return { + type, + data: data as TFriendEventRequest, + threadId, + isSelf: (data as TFriendEventRequest).fromUid == uid, + }; + } else if (type == FriendEventType.SEEN_FRIEND_REQUEST) { + return { + type, + data: data as TFriendEventSeenRequest, + threadId: uid, + isSelf: true, + }; + } else if (type == FriendEventType.PIN_CREATE) { + const threadId = (data as TFriendEventPinCreate).conversationId; + return { + type, + data: data as TFriendEventPinCreate, + threadId, + isSelf: (data as TFriendEventPinCreate).actorId == uid, + }; + } else if (type == FriendEventType.PIN_UNPIN) { + const threadId = (data as TFriendEventPinUnpin).conversationId; + return { + type, + data: data as TFriendEventPinUnpin, + threadId, + isSelf: (data as TFriendEventPinUnpin).actorId == uid, + }; + } else { + return { + type: FriendEventType.UNKNOWN, + data: JSON.stringify(data), + threadId: "", + isSelf: false, + }; + } +} diff --git a/_libs/zca-js/src/models/Group.ts b/_libs/zca-js/src/models/Group.ts new file mode 100644 index 0000000..6eb5f04 --- /dev/null +++ b/_libs/zca-js/src/models/Group.ts @@ -0,0 +1,146 @@ +export type GroupSetting = { + blockName: number; + signAdminMsg: number; + addMemberOnly: number; + setTopicOnly: number; + enableMsgHistory: number; + joinAppr: number; + lockCreatePost: number; + lockCreatePoll: number; + lockSendMsg: number; + lockViewMember: number; + bannFeature: number; + dirtyMedia: number; + banDuration: number; +}; + +export enum GroupTopicType { + Note = 0, + Message = 2, + Poll = 3, +} + +export type GroupTopicNoteParams = { + client_msg_id: string; + global_msg_id: string; + title: string; +}; + +export type GroupTopicTextMessageParams = { + senderUid: string; + senderName: string; + client_msg_id: string; + global_msg_id: string; + msg_type: 1; + title: string; +}; + +export type GroupTopicVoiceMessageParams = GroupTopicImageMessageParams & { + msg_type: 31; +}; + +export type GroupTopicImageMessageParams = GroupTopicTextMessageParams & { + msg_type: 32; + thumb: string; +}; + +export type GroupTopicVideoMessageParams = GroupTopicTextMessageParams & { + msg_type: 44; + thumb: string; +}; + +export type GroupTopicFileMessageParams = GroupTopicTextMessageParams & { + msg_type: 46; + extra: { + fileSize: string; + checksum: string; + checksumSha: unknown; + fileExt: string; + fdata: string; + fType: number; + }; +}; + +export type GroupTopicGifMessageParams = GroupTopicTextMessageParams & { + msg_type: 49; + thumb: string; +}; + +export type GroupTopicMessageParams = + | GroupTopicTextMessageParams + | GroupTopicVoiceMessageParams + | GroupTopicImageMessageParams + | GroupTopicVideoMessageParams + | GroupTopicFileMessageParams + | GroupTopicGifMessageParams; + +export type GroupTopicPollParams = { + pollId: number; + title: string; +}; + +export type GroupTopicOtherParams = { + [key: string]: unknown; +}; + +export type GroupTopic = { + type: GroupTopicType; + color: number; + emoji: string; + startTime: number; + duration: number; + params: GroupTopicNoteParams | GroupTopicMessageParams | GroupTopicPollParams | GroupTopicOtherParams; + id: string; + creatorId: string; + createTime: number; + editorId: string; + editTime: number; + repeat: number; + action: number; +}; + +export enum GroupType { + Group = 1, + Community = 2, +} + +export type GroupCurrentMem = { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; +}; + +export type GroupInfo = { + groupId: string; + name: string; + desc: string; + type: GroupType; + creatorId: string; + version: string; + avt: string; + fullAvt: string; + memberIds: string[]; + adminIds: string[]; + currentMems: GroupCurrentMem[]; + updateMems: unknown[]; + admins: unknown[]; + hasMoreMember: number; + subType: number; + totalMember: number; + maxMember: number; + setting: GroupSetting; + createdTime: number; + visibility: number; + globalId: string; + /** + * 1: True, 0: False + */ + e2ee: number; + extraInfo: { + enable_media_store: number; + }; +}; diff --git a/_libs/zca-js/src/models/GroupEvent.ts b/_libs/zca-js/src/models/GroupEvent.ts new file mode 100644 index 0000000..80270dd --- /dev/null +++ b/_libs/zca-js/src/models/GroupEvent.ts @@ -0,0 +1,285 @@ +import type { GroupSetting, GroupTopic } from "./Group.js"; +import type { ReminderGroup } from "./Reminder.js"; + +export enum GroupEventType { + JOIN_REQUEST = "join_request", + JOIN = "join", + LEAVE = "leave", + REMOVE_MEMBER = "remove_member", + BLOCK_MEMBER = "block_member", + + UPDATE_SETTING = "update_setting", + UPDATE = "update", + NEW_LINK = "new_link", + + ADD_ADMIN = "add_admin", + REMOVE_ADMIN = "remove_admin", + + NEW_PIN_TOPIC = "new_pin_topic", + UPDATE_PIN_TOPIC = "update_pin_topic", + REORDER_PIN_TOPIC = "reorder_pin_topic", + + UPDATE_BOARD = "update_board", + REMOVE_BOARD = "remove_board", + + UPDATE_TOPIC = "update_topic", + UNPIN_TOPIC = "unpin_topic", + REMOVE_TOPIC = "remove_topic", + + ACCEPT_REMIND = "accept_remind", + REJECT_REMIND = "reject_remind", + REMIND_TOPIC = "remind_topic", + + UPDATE_AVATAR = "update_avatar", + + UNKNOWN = "unknown", +} + +export type GroupEventUpdateMember = { + id: string; + dName: string; + avatar: string; + type: number; + avatar_25: string; +}; + +export type GroupEventGroupInfo = { + group_link?: string; + link_expired_time?: number; + [key: string]: unknown; +}; + +export type GroupEventExtraData = { + featureId?: number; + field?: string; + [key: string]: unknown; +}; + +export type TGroupEventBase = { + subType: number; + groupId: string; + creatorId: string; + groupName: string; + sourceId: string; + updateMembers: GroupEventUpdateMember[]; + groupSetting: GroupSetting | null; + groupTopic: GroupTopic | null; + info: GroupEventGroupInfo | null; + extraData: GroupEventExtraData | null; + time: string; + avt: string | null; + fullAvt: string | null; + isAdd: number; + hideGroupInfo: number; + version: string; + groupType: number; + clientId?: number; + errorMap?: Record; + e2ee?: number; +}; + +export type TGroupEventJoinRequest = { + uids: string[]; + totalPending: number; + groupId: string; + time: string; +}; + +export type TGroupEventPinTopic = { + oldBoardVersion: number; + boardVersion: number; + topic: GroupTopic; + actorId: string; + groupId: string; +}; + +export type TGroupEventReorderPinTopic = { + oldBoardVersion: number; + actorId: string; + topics: { + topicId: string; + topicType: number; + }[]; + groupId: string; + boardVersion: number; + topic: null; +}; + +export type TGroupEventBoard = { + sourceId: string; + groupName: string; + groupTopic: (GroupTopic | ReminderGroup) & { params: string }; + groupId: string; + creatorId: string; + + subType?: number; + updateMembers?: GroupEventUpdateMember[]; + groupSetting?: GroupSetting; + info?: GroupEventGroupInfo; + extraData?: GroupEventExtraData; + time?: string; + avt?: null; + fullAvt?: null; + isAdd?: number; + hideGroupInfo?: number; + version?: string; + groupType?: number; +}; + +export type TGroupEventRemindRespond = { + topicId: string; + updateMembers: string[]; + groupId: string; + time: string; +}; + +export type TGroupEventRemindTopic = { + msg: string; + editorId: string; + color: string; + emoji: string; + creatorId: string; + editTime: number; + type: number; + duration: number; + group_id: string; + createTime: number; + repeat: number; + startTime: number; + time: number; + remindType: number; +}; + +export type TGroupEvent = + | TGroupEventBase + | TGroupEventJoinRequest + | TGroupEventPinTopic + | TGroupEventReorderPinTopic + | TGroupEventBoard + | TGroupEventRemindRespond + | TGroupEventRemindTopic; + +export type GroupEvent = + | { + type: GroupEventType.JOIN_REQUEST; + data: TGroupEventJoinRequest; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.NEW_PIN_TOPIC | GroupEventType.UNPIN_TOPIC | GroupEventType.UPDATE_PIN_TOPIC; + data: TGroupEventPinTopic; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.REORDER_PIN_TOPIC; + data: TGroupEventReorderPinTopic; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.UPDATE_BOARD | GroupEventType.REMOVE_BOARD; + data: TGroupEventBoard; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.ACCEPT_REMIND | GroupEventType.REJECT_REMIND; + data: TGroupEventRemindRespond; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.REMIND_TOPIC; + data: TGroupEventRemindTopic; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: Exclude< + GroupEventType, + | GroupEventType.JOIN_REQUEST + | GroupEventType.NEW_PIN_TOPIC + | GroupEventType.UNPIN_TOPIC + | GroupEventType.UPDATE_PIN_TOPIC + | GroupEventType.REORDER_PIN_TOPIC + | GroupEventType.UPDATE_BOARD + | GroupEventType.REMOVE_BOARD + | GroupEventType.ACCEPT_REMIND + | GroupEventType.REJECT_REMIND + | GroupEventType.REMIND_TOPIC + >; + data: TGroupEventBase; + act: string; + threadId: string; + isSelf: boolean; + }; + +export function initializeGroupEvent(uid: string, data: TGroupEvent, type: GroupEventType, act: string): GroupEvent { + const threadId = "group_id" in data ? data.group_id : data.groupId; + + if (type == GroupEventType.JOIN_REQUEST) { + return { type, act, data: data as TGroupEventJoinRequest, threadId, isSelf: false }; + } else if ( + type == GroupEventType.NEW_PIN_TOPIC || + type == GroupEventType.UNPIN_TOPIC || + type == GroupEventType.UPDATE_PIN_TOPIC + ) { + return { + type, + act, + data: data as TGroupEventPinTopic, + threadId, + isSelf: (data as TGroupEventPinTopic).actorId == uid, + }; + } else if (type == GroupEventType.REORDER_PIN_TOPIC) { + return { + type, + act, + data: data as TGroupEventReorderPinTopic, + threadId, + isSelf: (data as TGroupEventPinTopic).actorId == uid, + }; + } else if (type == GroupEventType.UPDATE_BOARD || type == GroupEventType.REMOVE_BOARD) { + return { + type, + act, + data: data as TGroupEventBoard, + threadId, + isSelf: (data as TGroupEventBoard).sourceId == uid, + }; + } else if (type == GroupEventType.ACCEPT_REMIND || type == GroupEventType.REJECT_REMIND) { + return { + type, + act, + data: data as TGroupEventRemindRespond, + threadId, + isSelf: (data as TGroupEventRemindRespond).updateMembers.some((memberId) => memberId == uid), + }; + } else if (type == GroupEventType.REMIND_TOPIC) { + return { + type, + act, + data: data as TGroupEventRemindTopic, + threadId, + isSelf: (data as TGroupEventRemindTopic).creatorId == uid, + }; + } else { + const baseData = data as TGroupEventBase; + + return { + type, + act, + data: baseData, + threadId, + isSelf: baseData.updateMembers?.some((member) => member.id == uid) || baseData.sourceId == uid, + }; + } +} diff --git a/_libs/zca-js/src/models/Label.ts b/_libs/zca-js/src/models/Label.ts new file mode 100644 index 0000000..605a5dc --- /dev/null +++ b/_libs/zca-js/src/models/Label.ts @@ -0,0 +1,10 @@ +export type LabelData = { + id: number; + text: string; + textKey: string; + conversations: string[]; + color: string; + offset: number; + emoji: string; + createTime: number; +}; diff --git a/_libs/zca-js/src/models/Message.ts b/_libs/zca-js/src/models/Message.ts new file mode 100644 index 0000000..7d996ad --- /dev/null +++ b/_libs/zca-js/src/models/Message.ts @@ -0,0 +1,125 @@ +import { ThreadType } from "./Enum.js"; + +export type TAttachmentContent = { + title: string; + description: string; + href: string; + thumb: string; + childnumber: number; + action: string; + params: string; + type: string; +}; + +export type TOtherContent = { + [key: string]: unknown; +}; + +export type TMessage = { + actionId: string; + msgId: string; + cliMsgId: string; + msgType: string; + uidFrom: string; + idTo: string; + dName: string; + ts: string; + status: number; + content: string | TAttachmentContent | TOtherContent; + notify: string; + ttl: number; + userId: string; + uin: string; + topOut: string; + topOutTimeOut: string; + topOutImprTimeOut: string; + propertyExt: + | { + color: number; + size: number; + type: number; + subType: number; + ext: string; + } + | undefined; + paramsExt: { + countUnread: number; + containType: number; + platformType: number; + }; + cmd: number; + st: number; + at: number; + realMsgId: string; + quote: TQuote | undefined; +}; + +export type TGroupMessage = TMessage & { + mentions: TMention[] | undefined; +}; + +export type TQuote = { + ownerId: string; + cliMsgId: number; + globalMsgId: number; + cliMsgType: number; + ts: number; + msg: string; + attach: string; + fromD: string; + ttl: number; +}; + +export type TMention = { + uid: string; + pos: number; + len: number; + type: 0 | 1; +}; + +export class UserMessage { + type: ThreadType.User = ThreadType.User; + + data: TMessage; + threadId: string; + /** + * true if the message is sent by the logged in account + */ + isSelf: boolean; + + constructor(uid: string, data: TMessage) { + this.data = data; + this.threadId = data.uidFrom == "0" ? data.idTo : data.uidFrom; + this.isSelf = data.uidFrom == "0"; + + if (data.idTo == "0") data.idTo = uid; + if (data.uidFrom == "0") data.uidFrom = uid; + if (data.quote) { + data.quote.ownerId = String(data.quote.ownerId); + } + } +} + +export class GroupMessage { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupMessage; + threadId: string; + /** + * true if the message is sent by the logged in account + */ + isSelf: boolean; + + constructor(uid: string, data: TGroupMessage) { + this.data = data; + this.threadId = data.idTo; + this.isSelf = data.uidFrom == "0"; + + if (data.uidFrom == "0") data.uidFrom = uid; + if (data.quote) { + data.quote.ownerId = String(data.quote.ownerId); + } + } +} + +export type Message = UserMessage | GroupMessage; diff --git a/_libs/zca-js/src/models/ProductCatalog.ts b/_libs/zca-js/src/models/ProductCatalog.ts new file mode 100644 index 0000000..8dd01a4 --- /dev/null +++ b/_libs/zca-js/src/models/ProductCatalog.ts @@ -0,0 +1,17 @@ +export type ProductCatalogItem = { + price: string; + description: string; + /** + * Relative path used to build the product URL. + * + * Example: https://catalog.zalo.me/${path} + */ + path: string; + product_id: string; + product_name: string; + currency_unit: string; + product_photos: string[]; + create_time: number; + catalog_id: string; + owner_id: string; +}; diff --git a/_libs/zca-js/src/models/QuickMessage.ts b/_libs/zca-js/src/models/QuickMessage.ts new file mode 100644 index 0000000..e79d4c3 --- /dev/null +++ b/_libs/zca-js/src/models/QuickMessage.ts @@ -0,0 +1,25 @@ +export type QuickMessage = { + id: number; + keyword: string; + type: number; + createdTime: number; + lastModified: number; + message: { + title: string; + params: string | null; + }; + media: { + items: { + type: number; + photoId: number; + title: string; + width: number; + height: number; + previewThumb: string; + rawUrl: string; + thumbUrl: string; + normalUrl: string; + hdUrl: string; + }[]; + } | null; +}; diff --git a/_libs/zca-js/src/models/Reaction.ts b/_libs/zca-js/src/models/Reaction.ts new file mode 100644 index 0000000..619f584 --- /dev/null +++ b/_libs/zca-js/src/models/Reaction.ts @@ -0,0 +1,92 @@ +export enum Reactions { + HEART = "/-heart", + LIKE = "/-strong", + HAHA = ":>", + WOW = ":o", + CRY = ":-((", + ANGRY = ":-h", + KISS = ":-*", + TEARS_OF_JOY = ":')", + SHIT = "/-shit", + ROSE = "/-rose", + BROKEN_HEART = "/-break", + DISLIKE = "/-weak", + LOVE = ";xx", + CONFUSED = ";-/", + WINK = ";-)", + FADE = "/-fade", + SUN = "/-li", + BIRTHDAY = "/-bd", + BOMB = "/-bome", + OK = "/-ok", + PEACE = "/-v", + THANKS = "/-thanks", + PUNCH = "/-punch", + SHARE = "/-share", + PRAY = "_()_", + NO = "/-no", + BAD = "/-bad", + LOVE_YOU = "/-loveu", + SAD = "--b", + VERY_SAD = ":((", + COOL = "x-)", + NERD = "8-)", + BIG_SMILE = ";-d", + SUNGLASSES = "b-)", + NEUTRAL = ":--|", + SAD_FACE = "p-(", + BYE = ":-bye", + SLEEPY = "|-)", + WIPE = ":wipe", + DIG = ":-dig", + ANGUISH = "&-(", + HANDCLAP = ":handclap", + ANGRY_FACE = ">-|", + F_CHAIR = ":-f", + L_CHAIR = ":-l", + R_CHAIR = ":-r", + SILENT = ";-x", + SURPRISE = ":-o", + EMBARRASSED = ";-s", + AFRAID = ";-a", + SAD2 = ":-<", + BIG_LAUGH = ":))", + RICH = "$-)", + BEER = "/-beer", + NONE = "", // more... +} + +export type TReaction = { + actionId: string; + msgId: string; + cliMsgId: string; + msgType: string; + uidFrom: string; + idTo: string; + dName?: string; + content: { + rMsg: { gMsgID: string; cMsgID: string; msgType: number }[]; + rIcon: Reactions; + rType: number; + source: number; + }; + ts: string; + ttl: number; +}; + +export class Reaction { + data: TReaction; + threadId: string; + isSelf: boolean; + isGroup: boolean; + + constructor(uid: string, data: TReaction, isGroup: boolean) { + this.data = data; + this.threadId = isGroup || data.uidFrom == "0" ? data.idTo : data.uidFrom; + this.isSelf = data.uidFrom == "0"; + this.isGroup = isGroup; + + if (data.idTo == "0") data.idTo = uid; + if (data.uidFrom == "0") data.uidFrom = uid; + } +} diff --git a/_libs/zca-js/src/models/Reminder.ts b/_libs/zca-js/src/models/Reminder.ts new file mode 100644 index 0000000..3e1ea33 --- /dev/null +++ b/_libs/zca-js/src/models/Reminder.ts @@ -0,0 +1,53 @@ +export enum ReminderRepeatMode { + None = 0, + Daily = 1, + Weekly = 2, + Monthly = 3, +} + +export type ReminderUser = { + creatorUid: string; + toUid: string; + emoji: string; + color: number; + reminderId: string; + createTime: number; + repeat: ReminderRepeatMode; + startTime: number; + editTime: number; + endTime: number; + params: { + title: string; + setTitle: boolean; + }; + type: number; +}; + +export type ReminderGroup = { + editorId: string; + emoji: string; + color: number; + groupId: string; + creatorId: string; + editTime: number; + eventType: number; + responseMem: { + rejectMember: number; + myResp: number; + acceptMember: number; + }; + params: { + title: string; + setTitle?: boolean; + }; + type: number; + duration: number; + repeatInfo: { + list_ts: unknown[]; + } | null; + repeatData: unknown[]; + createTime: number; + repeat: ReminderRepeatMode; + startTime: number; + id: string; +}; diff --git a/_libs/zca-js/src/models/SeenMessage.ts b/_libs/zca-js/src/models/SeenMessage.ts new file mode 100644 index 0000000..c337d7a --- /dev/null +++ b/_libs/zca-js/src/models/SeenMessage.ts @@ -0,0 +1,43 @@ +import { ThreadType } from "./Enum.js"; + +export type TUserSeenMessage = { + idTo: string; + msgId: string; + realMsgId: string; +}; + +export type TGroupSeenMessage = { + msgId: string; + groupId: string; + seenUids: string[]; +}; + +export class UserSeenMessage { + type: ThreadType.User = ThreadType.User; + + data: TUserSeenMessage; + threadId: string; + isSelf: false; + + constructor(data: TUserSeenMessage) { + this.data = data; + this.threadId = data.idTo; + this.isSelf = false; + } +} + +export class GroupSeenMessage { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupSeenMessage; + threadId: string; + isSelf: boolean; + + constructor(uid: string, data: TGroupSeenMessage) { + this.data = data; + this.threadId = data.groupId; + this.isSelf = data.seenUids.includes(uid); + } +} + +export type SeenMessage = UserSeenMessage | GroupSeenMessage; diff --git a/_libs/zca-js/src/models/Sticker.ts b/_libs/zca-js/src/models/Sticker.ts new file mode 100644 index 0000000..2f0c5b4 --- /dev/null +++ b/_libs/zca-js/src/models/Sticker.ts @@ -0,0 +1,28 @@ +export type StickerDetail = { + id: number; + cateId: number; + type: number; + text: string; + uri: string; + fkey: number; + status: number; + stickerUrl: string; + stickerSpriteUrl: string; + stickerWebpUrl: string | null; + totalFrames: number; + duration: number; + effectId: number; + checksum: string; + ext: number; + source: number; + fss: unknown; + fssInfo: unknown; + version: number; + extInfo: unknown; +}; + +export type StickerBasic = { + type: number; + cate_id: number; + sticker_id: number; +}; diff --git a/_libs/zca-js/src/models/Typing.ts b/_libs/zca-js/src/models/Typing.ts new file mode 100644 index 0000000..275f896 --- /dev/null +++ b/_libs/zca-js/src/models/Typing.ts @@ -0,0 +1,41 @@ +import { ThreadType } from "./Enum.js"; + +export type TTyping = { + uid: string; + ts: string; + isPC: 0 | 1; +}; + +export type TGroupTyping = TTyping & { + gid: string; +}; + +export class UserTyping { + type: ThreadType.User = ThreadType.User; + + data: TTyping; + threadId: string; + isSelf: false; + + constructor(data: TTyping) { + this.data = data; + this.threadId = data.uid; + this.isSelf = false; + } +} + +export class GroupTyping { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupTyping; + threadId: string; + isSelf: false; + + constructor(data: TGroupTyping) { + this.data = data; + this.threadId = data.gid; + this.isSelf = false; + } +} + +export type Typing = UserTyping | GroupTyping; diff --git a/_libs/zca-js/src/models/Undo.ts b/_libs/zca-js/src/models/Undo.ts new file mode 100644 index 0000000..cadd3fc --- /dev/null +++ b/_libs/zca-js/src/models/Undo.ts @@ -0,0 +1,45 @@ +export type TUndoContent = { + globalMsgId: number; + cliMsgId: number; + deleteMsg: number; + srcId: number; + destId: number; +}; + +export type TUndo = { + actionId: string; + msgId: string; + cliMsgId: string; + msgType: string; + uidFrom: string; + idTo: string; + dName: string; + ts: string; + status: number; + content: TUndoContent; + notify: string; + ttl: number; + userId: string; + uin: string; + cmd: number; + st: number; + at: number; + realMsgId: string; +}; + +export class Undo { + data: TUndo; + threadId: string; + isSelf: boolean; + isGroup: boolean; + + constructor(uid: string, data: TUndo, isGroup: boolean) { + this.data = data; + this.threadId = isGroup || data.uidFrom == "0" ? data.idTo : data.uidFrom; + this.isSelf = data.uidFrom == "0"; + this.isGroup = isGroup; + + if (data.idTo == "0") data.idTo = uid; + if (data.uidFrom == "0") data.uidFrom = uid; + } +} diff --git a/_libs/zca-js/src/models/User.ts b/_libs/zca-js/src/models/User.ts new file mode 100644 index 0000000..03f52f1 --- /dev/null +++ b/_libs/zca-js/src/models/User.ts @@ -0,0 +1,65 @@ +import type { Gender } from "./Enum.js"; +import type { ZBusinessPackage } from "./ZBusiness.js"; + +export type User = { + userId: string; + username: string; + displayName: string; + zaloName: string; + avatar: string; + bgavatar: string; + cover: string; + gender: Gender; + dob: number; + sdob: string; + status: string; + phoneNumber: string; + isFr: number; + isBlocked: number; + lastActionTime: number; + lastUpdateTime: number; + isActive: number; + key: number; + type: number; + isActivePC: number; + isActiveWeb: number; + isValid: number; + userKey: string; + accountStatus: number; + oaInfo: unknown; + user_mode: number; + globalId: string; + bizPkg: ZBusinessPackage; + createdTs: number; + oa_status: unknown; +}; + +export type UserBasic = { + avatar: string; + cover: string; + status: string; + gender: Gender; + dob: number; + sdob: string; + globalId: string; + bizPkg: ZBusinessPackage; + uid: string; + zalo_name: string; + display_name: string; +}; + +export type UserSetting = { + add_friend_via_contact: number; + display_on_recommend_friend: number; + add_friend_via_group: number; + add_friend_via_qr: number; + quick_message_status: number; + show_online_status: boolean; + accept_stranger_call: number; + archived_chat_status: number; + receive_message: number; + add_friend_via_phone: number; + display_seen_status: number; + view_birthday: number; + setting_2FA_status: number; +}; diff --git a/_libs/zca-js/src/models/ZBusiness.ts b/_libs/zca-js/src/models/ZBusiness.ts new file mode 100644 index 0000000..9c1ea24 --- /dev/null +++ b/_libs/zca-js/src/models/ZBusiness.ts @@ -0,0 +1,40 @@ +export type ZBusinessPackage = { + label?: Record | null; + pkgId: number; // @TODO: what is this? +}; + +export enum BusinessCategory { + Other = 0, + RealEstate = 1, + TechnologyAndDevices = 2, + TravelAndHospitality = 3, + EducationAndTraining = 4, + ShoppingAndRetail = 5, + CosmeticsAndBeauty = 6, + RestaurantAndCafe = 7, + AutoAndMotorbike = 8, + FashionAndApparel = 9, + FoodAndBeverage = 10, + MediaAndEntertainment = 11, + InternalCommunications = 12, + Transportation = 13, + Telecommunications = 14, +} + +export const BusinessCategoryName: Record = { + [BusinessCategory.Other]: "Dแป‹ch vแปฅ khรกc (Khรดng hiแปƒn thแป‹)", + [BusinessCategory.RealEstate]: "Bแบฅt ฤ‘แป™ng sแบฃn", + [BusinessCategory.TechnologyAndDevices]: "Cรดng nghแป‡ & Thiแบฟt bแป‹", + [BusinessCategory.TravelAndHospitality]: "Du lแป‹ch & Lฦฐu trรบ", + [BusinessCategory.EducationAndTraining]: "Giรกo dแปฅc & ฤร o tแบกo", + [BusinessCategory.ShoppingAndRetail]: "Mua sแบฏm & Bรกn lแบป", + [BusinessCategory.CosmeticsAndBeauty]: "Mแปน phแบฉm & Lร m ฤ‘แบนp", + [BusinessCategory.RestaurantAndCafe]: "Nhร  hร ng & Quรกn", + [BusinessCategory.AutoAndMotorbike]: "ร” tรด & Xe mรกy", + [BusinessCategory.FashionAndApparel]: "Thแปi trang & May mแบทc", + [BusinessCategory.FoodAndBeverage]: "Thแปฑc phแบฉm & ฤแป“ uแป‘ng", + [BusinessCategory.MediaAndEntertainment]: "Truyแปn thรดng & Giแบฃi trรญ", + [BusinessCategory.InternalCommunications]: "Truyแปn thรดng nแป™i bแป™", + [BusinessCategory.Transportation]: "Vแบญn tแบฃi", + [BusinessCategory.Telecommunications]: "Viแป…n thรดng", +}; diff --git a/_libs/zca-js/src/models/index.ts b/_libs/zca-js/src/models/index.ts new file mode 100644 index 0000000..46f087d --- /dev/null +++ b/_libs/zca-js/src/models/index.ts @@ -0,0 +1,21 @@ +export * from "./Attachment.js"; +export * from "./AutoReply.js"; +export * from "./Board.js"; +export * from "./Catalog.js"; +export * from "./DeliveredMessage.js"; +export * from "./Enum.js"; +export * from "./FriendEvent.js"; +export * from "./Group.js"; +export * from "./GroupEvent.js"; +export * from "./Message.js"; +export * from "./ProductCatalog.js"; +export * from "./QuickMessage.js"; +export * from "./Reaction.js"; +export * from "./Reminder.js"; +export * from "./SeenMessage.js"; +export * from "./Typing.js"; +export * from "./Undo.js"; +export * from "./User.js"; +export * from "./ZBusiness.js"; +export * from "./Label.js"; +export * from "./Sticker.js"; diff --git a/_libs/zca-js/src/update.ts b/_libs/zca-js/src/update.ts new file mode 100644 index 0000000..7a8e9d8 --- /dev/null +++ b/_libs/zca-js/src/update.ts @@ -0,0 +1,32 @@ +import { compare } from "semver"; +import { isBun, logger } from "./utils.js"; +import type { ContextBase } from "./context.js"; + +const VERSION = "2.1.2"; +const NPM_REGISTRY = "https://registry.npmjs.org/zca-js"; + +export async function checkUpdate(ctx: ContextBase) { + if (!ctx.options.checkUpdate) return; + + const _options = { + ...(isBun + ? { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + proxy: ctx.options.agent?.proxy?.href, + } + : { agent: ctx.options.agent }), + }; + const response = await ctx.options.polyfill(NPM_REGISTRY, _options as RequestInit).catch(() => null); + if (!response || !response.ok) return; + + const data = await response.json().catch(() => null); + if (!data) return; + + const latestVersion = data["dist-tags"].latest; + if (compare(VERSION, latestVersion) === -1) { + logger(ctx).info(`A new version of zca-js is available: ${latestVersion}`); + } else { + logger(ctx).info("zca-js is up to date"); + } +} diff --git a/_libs/zca-js/src/utils.ts b/_libs/zca-js/src/utils.ts new file mode 100644 index 0000000..6fcbeb7 --- /dev/null +++ b/_libs/zca-js/src/utils.ts @@ -0,0 +1,791 @@ +import cryptojs from "crypto-js"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import pako from "pako"; +import SparkMD5 from "spark-md5"; +import toughCookie from "tough-cookie"; +import JSONBig from "json-bigint"; + +import { isContextSession, type ContextSession, type ContextBase } from "./context.js"; +import { ZaloApiError, ZaloApiMissingImageMetadataGetter } from "./Errors/index.js"; +import { FriendEventType } from "./models/FriendEvent.js"; +import { GroupEventType } from "./models/GroupEvent.js"; +import type { API } from "./zalo.js"; +import type { AttachmentSource } from "./models/Attachment.js"; + +export const isBun = typeof Bun !== "undefined"; + +export function hasOwn(obj: Record, key: string): key is keyof typeof obj { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +/** + * Get signed key for API requests. + * + * @param type + * @param params + * @returns MD5 hash + * + */ +export function getSignKey(type: string, params: Record) { + const n = []; + for (const s in params) { + if (hasOwn(params, s)) { + n.push(s); + } + } + + n.sort(); + let a = "zsecure" + type; + for (let s = 0; s < n.length; s++) a += params[n[s]]; + return cryptojs.MD5(a).toString(); +} + +/** + * + * @param baseURL + * @param params + * @param apiVersion automatically add zalo api version to url params + * @returns + * + */ +export function makeURL( + ctx: ContextBase, + baseURL: string, + params: Record = {}, + apiVersion: boolean = true, +) { + const url = new URL(baseURL); + for (const key in params) { + if (hasOwn(params, key)) { + url.searchParams.append(key, params[key].toString()); + } + } + + if (apiVersion) { + if (!url.searchParams.has("zpw_ver")) url.searchParams.set("zpw_ver", ctx.API_VERSION.toString()); + if (!url.searchParams.has("zpw_type")) url.searchParams.set("zpw_type", ctx.API_TYPE.toString()); + } + + return url.toString(); +} + +export class ParamsEncryptor { + private zcid: string | null = null; + private enc_ver: string; + private zcid_ext: string; + private encryptKey: string | null; + constructor({ type, imei, firstLaunchTime }: { type: number; imei: string; firstLaunchTime: number }) { + this.enc_ver = "v2"; + this.zcid = null; + this.encryptKey = null; + + this.createZcid(type, imei, firstLaunchTime); + this.zcid_ext = ParamsEncryptor.randomString(); + this.createEncryptKey(); + } + + getEncryptKey() { + if (!this.encryptKey) throw new ZaloApiError("getEncryptKey: didn't create encryptKey yet"); + return this.encryptKey; + } + + createZcid(type: number, imei: string, firstLaunchTime: number) { + if (!type || !imei || !firstLaunchTime) throw new ZaloApiError("createZcid: missing params"); + const msg = `${type},${imei},${firstLaunchTime}`; + const s = ParamsEncryptor.encodeAES("3FC4F0D2AB50057BCE0D90D9187A22B1", msg, "hex", true); + this.zcid = s; + } + + createEncryptKey(e = 0) { + const t = (e: string, t: string) => { + const { even: n } = ParamsEncryptor.processStr(e), + { even: a, odd: s } = ParamsEncryptor.processStr(t); + if (!n || !a || !s) return !1; + const i = n.slice(0, 8).join("") + a.slice(0, 12).join("") + s.reverse().slice(0, 12).join(""); + return (this.encryptKey = i), !0; + }; + if (!this.zcid || !this.zcid_ext) throw new ZaloApiError("createEncryptKey: zcid or zcid_ext is null"); + try { + const n = cryptojs.MD5(this.zcid_ext).toString().toUpperCase(); + if (t(n, this.zcid) || !(e < 3)) return !1; + this.createEncryptKey(e + 1); + } catch { + if (e < 3) this.createEncryptKey(e + 1); + } + return !0; + } + + getParams() { + return this.zcid + ? { + zcid: this.zcid, + zcid_ext: this.zcid_ext, + enc_ver: this.enc_ver, + } + : null; + } + + static processStr(e: string) { + if (!e || "string" != typeof e) + return { + even: null, + odd: null, + }; + const [t, n] = [...e].reduce((e, t, n) => (e[n % 2].push(t), e), [[], []] as string[][]); + return { + even: t, + odd: n, + }; + } + + static randomString(e?: number, t?: number) { + const n = e || 6, + a = t && e && t > e ? t : 12; + let s = Math.floor(Math.random() * (a - n + 1)) + n; + if (s > 12) { + let e = ""; + for (; s > 0; ) { + e += Math.random() + .toString(16) + .substr(2, s > 12 ? 12 : s); + s -= 12; + } + return e; + } + return Math.random().toString(16).substr(2, s); + } + + static encodeAES(e: string, message: string, type: "hex" | "base64", uppercase: boolean, s = 0): string | null { + if (!message) return null; + try { + { + const encoder = "hex" == type ? cryptojs.enc.Hex : cryptojs.enc.Base64; + const key = cryptojs.enc.Utf8.parse(e); + + const cfg = { + words: [0, 0, 0, 0], + sigBytes: 16, + } as cryptojs.lib.WordArray; + const encrypted = cryptojs.AES.encrypt(message, key, { + iv: cfg, + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }).ciphertext.toString(encoder); + + return uppercase ? encrypted.toUpperCase() : encrypted; + } + } catch { + return s < 3 ? ParamsEncryptor.encodeAES(e, message, type, uppercase, s + 1) : null; + } + } +} + +export function decryptResp(key: string, data: string): Record | null | string { + let n = null; + try { + n = decodeRespAES(key, data); + const parsed = JSON.parse(n); + return parsed; + } catch { + return n; + } +} + +function decodeRespAES(key: string, data: string) { + data = decodeURIComponent(data); + const parsedKey = cryptojs.enc.Utf8.parse(key); + const n = { + words: [0, 0, 0, 0], + sigBytes: 16, + } as cryptojs.lib.WordArray; + + return cryptojs.AES.decrypt( + { + ciphertext: cryptojs.enc.Base64.parse(data), + } as cryptojs.lib.CipherParams, + parsedKey, + { + iv: n, + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }, + ).toString(cryptojs.enc.Utf8); +} + +export function decodeBase64ToBuffer(data: string) { + return Buffer.from(data, "base64"); +} + +export function decodeUnit8Array(data: Uint8Array) { + try { + return new TextDecoder().decode(data); + } catch { + return null; + } +} + +export function encodeAES(secretKey: string, data: cryptojs.lib.WordArray | string, t = 0): string | null { + try { + const key = cryptojs.enc.Base64.parse(secretKey); + return cryptojs.AES.encrypt(data, key, { + iv: cryptojs.enc.Hex.parse("00000000000000000000000000000000"), + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }).ciphertext.toString(cryptojs.enc.Base64); + } catch { + return t < 3 ? encodeAES(secretKey, data, t + 1) : null; + } +} + +export function decodeAES(secretKey: string, data: string, t = 0): string | null { + try { + data = decodeURIComponent(data); + const key = cryptojs.enc.Base64.parse(secretKey); + return cryptojs.AES.decrypt( + { + ciphertext: cryptojs.enc.Base64.parse(data), + } as cryptojs.lib.CipherParams, + key, + { + iv: cryptojs.enc.Hex.parse("00000000000000000000000000000000"), + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }, + ).toString(cryptojs.enc.Utf8); + } catch { + return t < 3 ? decodeAES(secretKey, data, t + 1) : null; + } +} + +export async function getDefaultHeaders(ctx: ContextBase, origin: string = "https://chat.zalo.me") { + if (!ctx.cookie) throw new ZaloApiError("Cookie is not available"); + if (!ctx.userAgent) throw new ZaloApiError("User agent is not available"); + + return { + Accept: "application/json, text/plain, */*", + "Accept-Encoding": "gzip, deflate, br, zstd", + "Accept-Language": "en-US,en;q=0.9", + "content-type": "application/x-www-form-urlencoded", + Cookie: await ctx.cookie.getCookieString(origin), + Origin: "https://chat.zalo.me", + Referer: "https://chat.zalo.me/", + "User-Agent": ctx.userAgent, + }; +} + +export async function request(ctx: ContextBase, url: string, options?: RequestInit, raw = false) { + if (!ctx.cookie) ctx.cookie = new toughCookie.CookieJar(); + const origin = new URL(url).origin; + + const defaultHeaders = await getDefaultHeaders(ctx, origin); + if (!raw) { + if (options) { + options.headers = Object.assign(defaultHeaders, options.headers || {}); + } else options = { headers: defaultHeaders }; + } + + const _options = { + ...(options ?? {}), + ...(isBun ? { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + proxy: ctx.options.agent?.proxy?.href + } : { agent: ctx.options.agent }), + }; + + const response = await ctx.options.polyfill(url, _options); + const setCookieRaw = response.headers.get("set-cookie"); + if (setCookieRaw && !raw) { + // Use getSetCookie() (Node.js 18+) to properly parse multi-cookie headers. + // The split(", ") method breaks cookies containing Expires dates with commas + // (e.g., "Expires=Wed, 18 Mar 2026..."), causing critical cookies like + // zpsid and zpw_sek to be lost during QR login flow. + let cookieStrings: string[]; + if (typeof response.headers.getSetCookie === "function") { + cookieStrings = response.headers.getSetCookie(); + } else { + cookieStrings = setCookieRaw.split(", "); + } + for (const cookie of cookieStrings) { + const parsed = toughCookie.Cookie.parse(cookie); + try { + if (parsed) await ctx.cookie.setCookie(parsed, parsed.domain != "zalo.me" ? `https://${parsed.domain}` : origin); + } catch (error: unknown) { + logger(ctx).error(error); + } + } + } + + const redirectURL = response.headers.get("location"); + + if (redirectURL) { + const redirectOptions = { ...options }; + redirectOptions.method = "GET"; + if (!raw) { + redirectOptions.headers = new Headers(redirectOptions.headers); + redirectOptions.headers.set("Referer", "https://id.zalo.me/"); + } + return await request(ctx, redirectURL, redirectOptions); + } + + return response; +} + +export async function getImageMetaData(ctx: ContextBase, filePath: string) { + if (!ctx.options.imageMetadataGetter) { + throw new ZaloApiMissingImageMetadataGetter(); + } + + const imageData = await ctx.options.imageMetadataGetter(filePath); + if (!imageData) { + throw new ZaloApiError("Failed to get image metadata"); + } + + const fileName = filePath.split("/").pop()!; + + return { + fileName, + totalSize: imageData.size, + width: imageData.width, + height: imageData.height, + }; +} + +export async function getFileSize(filePath: string) { + return fs.promises.stat(filePath).then((s) => s.size); +} + +export async function getGifMetaData(ctx: ContextBase, filePath: string) { + if (!ctx.options.imageMetadataGetter) { + throw new ZaloApiMissingImageMetadataGetter(); + } + + const gifData = await ctx.options.imageMetadataGetter(filePath); + if (!gifData) { + throw new ZaloApiError("Failed to get gif metadata"); + } + + const fileName = path.basename(filePath); + + return { + fileName, + totalSize: gifData.size, + width: gifData.width, + height: gifData.height, + }; +} + +export async function decodeEventData(parsed: Record, cipherKey?: string) { + if (typeof parsed.data !== "string") + throw new ZaloApiError(`Invalid data, expected string but got ${typeof parsed.data}`); + if (typeof parsed.encrypt !== "number") + throw new ZaloApiError(`Invalid encrypt type, expected number but got ${typeof parsed.encrypt}`); + if (parsed.encrypt < 0 || parsed.encrypt > 3) + throw new ZaloApiError(`Invalid encrypt type, expected 0-3 but got ${parsed.encrypt}`); + + const rawData = parsed.data; + const encryptType = parsed.encrypt as 0 | 1 | 2 | 3; + + if (encryptType === 0) return JSON.parse(rawData); + + const decodedBuffer = decodeBase64ToBuffer(encryptType === 1 ? rawData : decodeURIComponent(rawData)); + + let decryptedBuffer: ArrayBuffer | Buffer = decodedBuffer; + if (encryptType !== 1) { + if (cipherKey && decodedBuffer.length >= 48) { + const algorithm = { + name: "AES-GCM", + iv: decodedBuffer.subarray(0, 16), + tagLength: 128, + additionalData: decodedBuffer.subarray(16, 32), + }; + const dataSource = decodedBuffer.subarray(32); + + const cryptoKey = await crypto.subtle.importKey("raw", decodeBase64ToBuffer(cipherKey), algorithm, false, [ + "decrypt", + ]); + decryptedBuffer = await crypto.subtle.decrypt(algorithm, cryptoKey, dataSource); + } else { + throw new ZaloApiError("Invalid data length or missing cipher key"); + } + } + + const decompressedBuffer = + encryptType === 3 ? new Uint8Array(decryptedBuffer) : pako.inflate(decryptedBuffer as ArrayBuffer); + const decodedData = decodeUnit8Array(decompressedBuffer); + + if (!decodedData) return; + return JSONBig.parse(decodedData); +} + +export async function getMd5LargeFileObject(source: AttachmentSource, fileSize: number) { + const buffer = typeof source == "string" ? await fs.promises.readFile(source) : source.data; + return new Promise<{ + currentChunk: number; + data: string; + }>((resolve) => { + let currentChunk = 0; + const chunkSize = 2097152, // Read in chunks of 2MB + chunks = Math.ceil(fileSize / chunkSize), + spark = new SparkMD5.ArrayBuffer(); + + function loadNext() { + const start = currentChunk * chunkSize, + end = start + chunkSize >= fileSize ? fileSize : start + chunkSize; + + spark.append(new Uint8Array(buffer.subarray(start, end)).buffer as ArrayBuffer); + currentChunk++; + + if (currentChunk < chunks) { + loadNext(); + } else { + resolve({ + currentChunk, + data: spark.end(), + }); + } + } + + loadNext(); + }); +} + +export const logger = (ctx: { options: { logging?: boolean } }) => ({ + verbose: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[35m๐Ÿš€ VERBOSE\x1b[0m", ...args); + }, + info: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[34mINFO\x1b[0m", ...args); + }, + warn: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[33mWARN\x1b[0m", ...args); + }, + error: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[31mERROR\x1b[0m", ...args); + }, + success: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[32mSUCCESS\x1b[0m", ...args); + }, + timestamp: (...args: unknown[]) => { + const now = new Date().toISOString(); + if (ctx.options.logging) console.log(`\x1b[90m[${now}]\x1b[0m`, ...args); + }, +}); + +export function getClientMessageType(msgType: string) { + if (msgType === "webchat") return 1; + if (msgType === "chat.voice") return 31; + if (msgType === "chat.photo") return 32; + if (msgType === "chat.sticker") return 36; + if (msgType === "chat.doodle") return 37; + if (msgType === "chat.recommended") return 38; + + if (msgType === "chat.link") return 38; // don't know || if (msgType === "chat.link") return 1; + if (msgType === "chat.video.msg") return 44; // not sure + + if (msgType === "share.file") return 46; + if (msgType === "chat.gif") return 49; + if (msgType === "chat.location.new") return 43; + + return 1; +} + +export function strPadLeft(e: number | string, t: string, n: number) { + const a = (e = "" + e).length; + return a === n ? e : a > n ? e.slice(-n) : t.repeat(n - a) + e; +} + +export function formatTime(format: string, timestamp: number = Date.now()): string { + const date = new Date(timestamp); + + // using lib Intl + const options: Intl.DateTimeFormatOptions = { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + // hour12: false, // true or false is same <(") + }; + + const formatted = new Intl.DateTimeFormat("vi-VN", options).format(date); + + if (format.includes("%H") || format.includes("%d")) { + return format + .replace("%H", date.getHours().toString().padStart(2, "0")) + .replace("%M", date.getMinutes().toString().padStart(2, "0")) + .replace("%S", date.getSeconds().toString().padStart(2, "0")) + .replace("%d", date.getDate().toString().padStart(2, "0")) + .replace("%m", (date.getMonth() + 1).toString().padStart(2, "0")) + .replace("%Y", date.getFullYear().toString()); + } + + return formatted; +} + +export function getFullTimeFromMillisecond(e: number) { + const t = new Date(e); + return ( + strPadLeft(t.getHours(), "0", 2) + + ":" + + strPadLeft(t.getMinutes(), "0", 2) + + " " + + strPadLeft(t.getDate(), "0", 2) + + "/" + + strPadLeft(t.getMonth() + 1, "0", 2) + + "/" + + t.getFullYear() + ); +} + +export function getFileExtension(e: string) { + return path.extname(e).slice(1); +} + +export function getFileName(e: string) { + return path.basename(e); +} + +export function removeUndefinedKeys(e: Record) { + for (const t in e) if (e[t] === undefined) delete e[t]; + return e; +} + +export function getGroupEventType(act: string) { + if (act == "join_request") return GroupEventType.JOIN_REQUEST; + if (act == "join") return GroupEventType.JOIN; + if (act == "leave") return GroupEventType.LEAVE; + if (act == "remove_member") return GroupEventType.REMOVE_MEMBER; + if (act == "block_member") return GroupEventType.BLOCK_MEMBER; + if (act == "update_setting") return GroupEventType.UPDATE_SETTING; + if (act == "update_avatar") return GroupEventType.UPDATE_AVATAR; + if (act == "update") return GroupEventType.UPDATE; + if (act == "new_link") return GroupEventType.NEW_LINK; + if (act == "add_admin") return GroupEventType.ADD_ADMIN; + if (act == "remove_admin") return GroupEventType.REMOVE_ADMIN; + + if (act == "new_pin_topic") return GroupEventType.NEW_PIN_TOPIC; + if (act == "update_pin_topic") return GroupEventType.UPDATE_PIN_TOPIC; + if (act == "update_topic") return GroupEventType.UPDATE_TOPIC; + if (act == "update_board") return GroupEventType.UPDATE_BOARD; + if (act == "remove_board") return GroupEventType.REMOVE_BOARD; + if (act == "reorder_pin_topic") return GroupEventType.REORDER_PIN_TOPIC; + if (act == "unpin_topic") return GroupEventType.UNPIN_TOPIC; + if (act == "remove_topic") return GroupEventType.REMOVE_TOPIC; + if (act == "accept_remind") return GroupEventType.ACCEPT_REMIND; + if (act == "reject_remind") return GroupEventType.REJECT_REMIND; + if (act == "remind_topic") return GroupEventType.REMIND_TOPIC; + + return GroupEventType.UNKNOWN; +} + +export function getFriendEventType(act: string) { + if (act == "add") return FriendEventType.ADD; + if (act == "remove") return FriendEventType.REMOVE; + if (act == "block") return FriendEventType.BLOCK; + if (act == "unblock") return FriendEventType.UNBLOCK; + if (act == "block_call") return FriendEventType.BLOCK_CALL; + if (act == "unblock_call") return FriendEventType.UNBLOCK_CALL; + + if (act == "req_v2") return FriendEventType.REQUEST; + if (act == "reject") return FriendEventType.REJECT_REQUEST; + if (act == "undo_req") return FriendEventType.UNDO_REQUEST; + + if (act == "seen_fr_req") return FriendEventType.SEEN_FRIEND_REQUEST; + + if (act == "pin_unpin") return FriendEventType.PIN_UNPIN; + if (act == "pin_create") return FriendEventType.PIN_CREATE; + + return FriendEventType.UNKNOWN; +} + +type ZaloResponse = { + data: T | null; + error: { + message: string; + code?: number; + } | null; +}; + +export async function handleZaloResponse(ctx: ContextSession, response: Response, isEncrypted = true) { + const result: ZaloResponse = { + data: null, + error: null, + }; + + if (!response.ok) { + result.error = { + message: "Request failed with status code " + response.status, + }; + return result; + } + + try { + const jsonData: { + error_code: number; + error_message: string; + data: string; + } = await response.json(); + + if (jsonData.error_code != 0) { + result.error = { + message: jsonData.error_message, + code: jsonData.error_code, + }; + return result; + } + + const decodedData: { + error_code: number; + error_message: string; + data: T; + } = isEncrypted ? JSON.parse(decodeAES(ctx.secretKey!, jsonData.data)!) : jsonData; + + if (decodedData.error_code != 0) { + result.error = { + message: decodedData.error_message, + code: decodedData.error_code, + }; + return result; + } + + result.data = decodedData.data; + } catch (error) { + logger(ctx).error("Failed to parse response data:", error); + result.error = { + message: "Failed to parse response data", + }; + } + + return result; +} + +export async function resolveResponse( + ctx: ContextSession, + res: Response, + cb?: (result: ZaloResponse) => T, + isEncrypted?: boolean, +) { + const result = await handleZaloResponse(ctx, res, isEncrypted); + if (result.error) throw new ZaloApiError(result.error.message, result.error.code); + if (cb) return cb(result); + + return result.data as T; +} + +export type FactoryUtils = { + makeURL: ( + baseURL: string, + params?: Record, + apiVersion?: boolean, + ) => ReturnType; + encodeAES: (data: cryptojs.lib.WordArray | string, t?: number) => ReturnType; + request: (url: string, options?: RequestInit, raw?: boolean) => ReturnType; + logger: ReturnType; + resolve: ( + res: Response, + cb?: (result: ZaloResponse) => T, + isEncrypted?: boolean, + ) => ReturnType>; +}; + +export function apiFactory() { + return ) => unknown>(callback: K) => { + return (ctx: ContextBase, api: API) => { + if (!isContextSession(ctx)) throw new ZaloApiError("Invalid context " + JSON.stringify(ctx, null, 2)); + + const utils = { + makeURL(baseURL: string, params?: Record, apiVersion?: boolean) { + return makeURL(ctx, baseURL, params, apiVersion); + }, + encodeAES(data: cryptojs.lib.WordArray | string, t?: number) { + return encodeAES(ctx.secretKey, data, t); + }, + request(url: string, options?: RequestInit, raw?: boolean) { + return request(ctx, url, options, raw); + }, + logger: logger(ctx), + resolve: (res: Response, cb?: (result: ZaloResponse) => T, isEncrypted?: boolean) => + resolveResponse(ctx, res, cb, isEncrypted), + }; + + return callback(api, ctx, utils) as ReturnType; + }; + }; +} + +export function generateZaloUUID(userAgent: string) { + return crypto.randomUUID() + "-" + cryptojs.MD5(userAgent).toString(); +} + +/** + * Encrypts a 4-digit PIN to a 32-character hex string + * @param pin 4-digit PIN number + * @returns 32-character hex string + */ +export function encryptPin(pin: string): string { + return crypto.createHash("md5").update(pin).digest("hex"); +} + +/** + * Decrypts a 32-character hex string back to 4-digit PIN + * Note: This is a one-way hash, so we can only verify if a PIN matches the hash + * @param encryptedPin 32-character hex string + * @param pin 4-digit PIN to verify + * @returns true if the PIN matches the hash + * + * @example + * const encryptedPin = (await api.getHiddenConversations()).pin; + * checking pin created.. + * const isValid = validatePin(encryptedPin, "1234"); // true if pin created is 1234 + * const isInvalid = validatePin(encryptedPin, "5678"); // false if not pin created is 5678 + */ +export function validatePin(encryptedPin: string, pin: string): boolean { + const hash = crypto.createHash("md5").update(pin).digest("hex"); + return hash === encryptedPin; +} + +/** + * Converts a hex color code to a negative color number used by Zalo API + * @param hex Hex color code (e.g. '#00FF00' or '00FF00') + * @returns Negative color number (e.g. -16711936) + * + * @example + * const negativeColor = hexToNegativeColor('#00FF00'); // Result: -16711936 + */ +export function hexToNegativeColor(hex: string): number { + if (!hex.startsWith("#")) { + hex = "#" + hex; + } + + // rgb no alpha + // const decimal = parseInt(hex.slice(1), 16); + // return decimal - 4294967296; + + // rgb with alpha + let hexValue = hex.slice(1); + if (hexValue.length === 6) { + hexValue = "FF" + hexValue; + } + const decimal = parseInt(hexValue, 16); + return decimal > 0x7fffffff ? decimal - 4294967296 : decimal; +} + +/** + * Converts a negative color number from Zalo API to hex color code + * @param negativeColor Negative color number (e.g. -16711936) + * @returns Hex color code (e.g. '#00FF00') + * + * @example + * const hexColor = negativeColorToHex(-16711936); // Result: '#00FF00' + */ +export function negativeColorToHex(negativeColor: number): string { + // Add 2^32 to get positive number + const positiveColor = negativeColor + 4294967296; + + // return "#" + positiveColor.toString(16).padStart(6, "0"); // rgb no alpha + return "#" + positiveColor.toString(16).slice(-6).padStart(6, "0"); // rgb with alpha +} diff --git a/_libs/zca-js/src/zalo.ts b/_libs/zca-js/src/zalo.ts new file mode 100644 index 0000000..ab0daa5 --- /dev/null +++ b/_libs/zca-js/src/zalo.ts @@ -0,0 +1,163 @@ +import { loginQR, LoginQRCallbackEventType, type LoginQRCallback } from "./apis/loginQR.js"; +import { getServerInfo, login } from "./apis/login.js"; +import { + createContext, + isContextSession, + type ContextBase, + type Options, +} from "./context.js"; +import { generateZaloUUID, logger } from "./utils.js"; + +import toughCookie from "tough-cookie"; + +import { ZaloApiError } from "./Errors/ZaloApiError.js"; +import { checkUpdate } from "./update.js"; +import { API } from "./apis.js"; + +export type Cookie = { + domain: string; + expirationDate: number; + hostOnly: boolean; + httpOnly: boolean; + name: string; + path: string; + sameSite: string; + secure: boolean; + session: boolean; + storeId: string; + value: string; +}; + +export type Credentials = { + imei: string; + cookie: Cookie[] | toughCookie.SerializedCookie[] | { url: string; cookies: Cookie[] }; + userAgent: string; + language?: string; +}; + +export class Zalo { + private enableEncryptParam = true; + + constructor(private options: Partial = {}) {} + + private parseCookies(cookie: Credentials["cookie"]): toughCookie.CookieJar { + const cookieArr = Array.isArray(cookie) ? cookie : cookie.cookies; + + cookieArr.forEach((e, i) => { + if (typeof e.domain == "string" && e.domain.startsWith(".")) cookieArr[i].domain = e.domain.slice(1); + }); + + const jar = new toughCookie.CookieJar(); + for (const each of cookieArr) { + try { + const cookieObj = toughCookie.Cookie.fromJSON({ + ...each, + key: (each as toughCookie.SerializedCookie).key || each.name, + }); + if (cookieObj) { + const domain = cookieObj.domain || "chat.zalo.me"; + const url = `https://${domain.startsWith(".") ? domain.slice(1) : domain}`; + jar.setCookieSync(cookieObj, url); + } + } catch (error: unknown) { + logger({ + options: { + logging: this.options.logging, + }, + }).error("Failed to set cookie:", error); + } + } + return jar; + } + + private validateParams(credentials: Credentials) { + if (!credentials.imei || !credentials.cookie || !credentials.userAgent) { + throw new ZaloApiError("Missing required params"); + } + } + + public async login(credentials: Credentials) { + const ctx = createContext(this.options.apiType, this.options.apiVersion); + Object.assign(ctx.options, this.options); + + return this.loginCookie(ctx, credentials); + } + + private async loginCookie(ctx: ContextBase, credentials: Credentials) { + await checkUpdate(ctx); + + this.validateParams(credentials); + + ctx.imei = credentials.imei; + ctx.cookie = this.parseCookies(credentials.cookie); + ctx.userAgent = credentials.userAgent; + ctx.language = credentials.language || "vi"; + + const loginData = await login(ctx, this.enableEncryptParam); + const serverInfo = await getServerInfo(ctx, this.enableEncryptParam); + + const loginInfo = loginData?.data as typeof ctx.loginInfo; + + if (!loginData || !loginInfo || !serverInfo) throw new ZaloApiError("ฤฤƒng nhแบญp thแบฅt bแบกi"); + + ctx.secretKey = loginInfo.zpw_enk; + ctx.uid = loginInfo.uid; + + // Zalo currently responds with setttings instead of settings + // they might fix this in the future, so we should have a fallback just in case + ctx.settings = serverInfo.setttings || serverInfo.settings; + + ctx.extraVer = serverInfo.extra_ver; + ctx.loginInfo = loginInfo; + + if (!isContextSession(ctx)) throw new ZaloApiError("KhแปŸi tแบกo ngแปฏ cแบฃnh thแบฅt bแบกi."); + + logger(ctx).info("Logged in as", loginInfo.uid); + + return new API(ctx, loginInfo.zpw_service_map_v3, loginInfo.zpw_ws); + } + + public async loginQR( + options?: { userAgent?: string; language?: string; qrPath?: string }, + callback?: LoginQRCallback, + ) { + if (!options) options = {}; + if (!options.userAgent) + options.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"; + if (!options.language) options.language = "vi"; + + const ctx = createContext(this.options.apiType, this.options.apiVersion); + Object.assign(ctx.options, this.options); + + const loginQRResult = await loginQR( + ctx, + options as { userAgent: string; language: string; qrPath?: string }, + callback, + ); + if (!loginQRResult) throw new ZaloApiError("Unable to login with QRCode"); + + const imei = generateZaloUUID(options.userAgent); + + if (callback) { + // Thanks to @YanCastle for this great suggestion! + callback({ + type: LoginQRCallbackEventType.GotLoginInfo, + data: { + cookie: loginQRResult.cookies, + imei, + userAgent: options.userAgent, + }, + actions: null, + }); + } + + return this.loginCookie(ctx, { + cookie: loginQRResult.cookies, + imei, + userAgent: options.userAgent, + language: options.language, + }); + } +} + +export { API }; diff --git a/_libs/zca-js/test/a.png b/_libs/zca-js/test/a.png new file mode 100644 index 0000000..ec6fc5c Binary files /dev/null and b/_libs/zca-js/test/a.png differ diff --git a/_libs/zca-js/test/credentials.example.json b/_libs/zca-js/test/credentials.example.json new file mode 100644 index 0000000..c1a7f99 --- /dev/null +++ b/_libs/zca-js/test/credentials.example.json @@ -0,0 +1,5 @@ +{ + "imei": "", + "userAgent": "", + "cookie": {} +} diff --git a/_libs/zca-js/test/feat.ts b/_libs/zca-js/test/feat.ts new file mode 100644 index 0000000..b551abd --- /dev/null +++ b/_libs/zca-js/test/feat.ts @@ -0,0 +1,176 @@ +/** + * Xem hฦฐแป›ng dแบซn sแปญ dแปฅng tแบกi: + * @tutorial https://drive.google.com/file/d/1xuGHYCu-5H28er4bXZEFjTCSGKcOekhi/view?usp=sharing + */ + +import fs from "node:fs"; +import { createInterface } from "node:readline"; +import { Zalo, type Credentials } from "../src/index.js"; +import { decodeAES, decodeEventData } from "../src/utils.js"; + +const CREDENTIALS_PATH = "./test/credentials.json"; +const SECRET_PATH = "./test/secret.test.txt"; + +let gotFresh = false; + +const credentials: Credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8")); +let secretKey = await getSecretKey(), + cipher_key = ""; + +const rl = createInterface(process.stdin, process.stdout); +const cannotDecodeAlert = () => + console.log( + "\n\x1b[31mKhรดng thแปƒ giแบฃi mรฃ, ฤ‘แบฃm bแบฃo bแบกn lแบฅy dแปฏ liแป‡u tแปซ ฤ‘รบng tร i khoแบฃn vร  trรฌnh duyแป‡t cแปงa tร i khoแบฃn ฤ‘รฃ ฤ‘ฤƒng nhแบญp\x1b[0m\n", + ); +const exit = () => { + console.log(); + process.exit(); +}; + +main(); + +async function main() { + console.log(); + if (secretKey && !gotFresh) { + const shouldGetFreshKey = await promptForFreshKey(); + if (shouldGetFreshKey) { + secretKey = await getFreshKey(); + } + } + + prompt(); +} + +function prompt() { + console.log(); + rl.question( + "Bแบกn muแป‘n giแบฃi mรฃ hรณa loแบกi nร o? \x1b[32m[1] HTTP(s)\x1b[0m, \x1b[38;5;165m[2] Websocket\x1b[0m, \x1b[38;5;15m[3] thoรกt?\x1b[0m: ", + (answer) => { + if (answer == "1") { + console.clear(); + console.log("\x1b[32m[ HTTP(s) ] Bแบฏt ฤ‘แบงu khแปŸi ฤ‘แป™ng!\x1b[0m"); + decodeHTTP(); + } else if (answer == "2") { + console.clear(); + console.log("\x1b[38;5;165m[ WEBSOCKET ] Bแบฏt ฤ‘แบงu khแปŸi ฤ‘แป™ng!\x1b[0m"); + decodeWebsocket(); + } else if (answer == "3") exit(); + else { + prompt(); + } + }, + ); +} + +function decodeHTTP() { + rl.question("\n\x1b[32m[ HTTP(s) ] Dรกn ฤ‘oแบกn dแปฏ liแป‡u bแป‹ mรฃ hรณa hoแบทc gรต \x1b[37mexit\x1b[0m \x1b[32mฤ‘แปƒ thoรกt:\x1b[0m\n\n", (encoded) => { + if (encoded.length == 0) return decodeHTTP(); + if (encoded == "exit") return prompt(); + + if (encoded.startsWith('"') && encoded.endsWith('"')) encoded = JSON.parse(encoded); + + try { + const decoded = decodeAES(secretKey, encoded); + if (decoded == null) { + cannotDecodeAlert(); + return decodeHTTP(); + } + + const parsed = JSON.stringify(JSON.parse(decoded), null, 2); + + console.log(); + console.log("\x1b[32m=========== HTTP(s) ===========\x1b[0m"); + console.log(parsed); + console.log("\x1b[32m===============================\x1b[0m"); + console.log(); + + return decodeHTTP(); + } catch { + cannotDecodeAlert(); + return decodeHTTP(); + } + }); +} + +async function decodeWebsocket() { + if (!cipher_key) { + console.log( + "MแปŸ devtools trรชn trรฌnh duyแป‡t tแบกi https://chat.zalo.me/, vร o tab Network -> (WS/Socket)\nMแปŸ tin nhแบฏn ฤ‘แบงu tiรชn, ฤ‘แป•i sang ฤ‘แป‹nh dแบกng UTF-8", + ); + cipher_key = await promptForCipherKey(); + } + + rl.question( + "\n\x1b[38;5;165m[ WEBSOCKET ] Dรกn ฤ‘oแบกn dแปฏ liแป‡u bแป‹ mรฃ hรณa hoแบทc gรต \x1b[37mexit\x1b[0m \x1b[38;5;165mฤ‘แปƒ thoรกt:\x1b[0m\n\n", + async (encoded) => { + if (encoded.length == 0) return decodeWebsocket(); + if (encoded == "exit") return prompt(); + + try { + const decoded = await decodeEventData(JSON.parse(encoded), cipher_key); + if (decoded == null) { + cannotDecodeAlert(); + return decodeWebsocket(); + } + + const parsed = JSON.stringify(decoded, null, 2); + + console.log(); + console.log("\x1b[38;5;165m=========== WEBSOCKET ===========\x1b[0m"); + console.log(parsed); + console.log("\x1b[38;5;165m=================================\x1b[0m"); + console.log(); + + return decodeWebsocket(); + } catch { + cannotDecodeAlert(); + return decodeWebsocket(); + } + }, + ); +} + +function promptForFreshKey() { + return new Promise((resolve) => { + rl.question("\x1b[38;5;204m[ !? ] Tรฌm thแบฅy khรณa bรญ mแบญt, bแบกn cรณ muแป‘n tรกi sแปญ dแปฅng? (y/n):\x1b[0m ", (answer) => { + if (answer.toLowerCase() == "y") resolve(false); + else if (answer.toLowerCase() == "n") resolve(true); + else resolve(promptForFreshKey()); + }); + }); +} + +async function getSecretKey(): Promise { + if (!fs.existsSync(SECRET_PATH)) { + return await getFreshKey(); + } else { + const localKey = fs.readFileSync(SECRET_PATH, "utf-8").trim(); + + if (!localKey) return await getFreshKey(); + return localKey; + } +} + +async function getFreshKey() { + gotFresh = true; + const zalo = new Zalo({ + selfListen: true, + logging: true, + }); + + console.log(); + const api = await zalo.login(credentials); + fs.writeFileSync(SECRET_PATH, api.getContext().secretKey, "utf-8"); + + return api.getContext().secretKey; +} + +async function promptForCipherKey() { + return new Promise((resolve) => { + rl.question("\n\x1b[38;5;81mSao chรฉp khรณa vร  dรกn vร o ฤ‘รขy:\x1b[0m ", (answer) => { + if (answer.length == 0) resolve(promptForCipherKey()); + + resolve(answer); + }); + }); +} diff --git a/_libs/zca-js/test/test.ts b/_libs/zca-js/test/test.ts new file mode 100644 index 0000000..a73b890 --- /dev/null +++ b/_libs/zca-js/test/test.ts @@ -0,0 +1,100 @@ +import path from "node:path"; +import fs from "node:fs"; + +import { Reactions, Zalo } from "../src/index.js"; +import { ThreadType } from "../src/models/index.js"; +const zalo = new Zalo({ + selfListen: true, + logging: true, +}); + +const api = await zalo.login(JSON.parse(fs.readFileSync(path.resolve("./test/credentials.json"), "utf-8"))); + +const { listener } = api; + +listener.on("connected", () => { + console.log("Connected"); +}); + +listener.on("closed", (code, reason) => { + console.log("Closed:", code, reason); +}); + +listener.on("error", (error) => { + console.error("Error:", error); +}); + +listener.on("message", (message) => { + console.log("Message:", message.threadId, message.data.content); + switch (message.type) { + case ThreadType.User: + api.addReaction(Reactions.HAHA, message).then(console.log); + if (!message.data.content || typeof message.data.content != "string") return; + if (!message.isSelf) { + switch (message.data.content) { + case "reply": { + api.sendMessage( + { + msg: "reply", + quote: message.data, + }, + message.threadId, + message.type, + ).then(console.log); + break; + } + case "ping": { + api.sendMessage("pong", message.threadId).then(console.log); + break; + } + default: { + const args = message.data.content.split(/\s+/); + if (args[0] == "sticker" && args[1]) { + api.getStickers(args[1]).then(async (stickerIds) => { + const random = stickerIds[Math.floor(Math.random() * stickerIds.length)]; + const sticker = await api.getStickersDetail(random); + console.log("Sending sticker:", sticker[0]); + + if (random) api.sendSticker(sticker[0], message.threadId).then(console.log); + else api.sendMessage("No sticker found", message.threadId).then(console.log); + }); + } + break; + } + } + } else { + const args = message.data.content.split(/\s+/); + if (args[0] == "find" && args[1]) { + api.findUser(args[1]).then(console.log); + } else if (args[0] == "get") { + api.sendMessage( + { + msg: "hi", + attachments: [path.resolve("./test/a.png")], + }, + message.threadId, + message.type, + ).then(console.log); + } + } + break; + + case ThreadType.Group: + if (!message.isSelf) { + switch (message.data.content) { + case "ping": { + api.sendMessage("pong", message.threadId, message.type).then(console.log); + break; + } + default: + break; + } + } + break; + + default: + break; + } +}); + +listener.start(); diff --git a/_libs/zca-js/tsconfig.json b/_libs/zca-js/tsconfig.json new file mode 100644 index 0000000..399af5d --- /dev/null +++ b/_libs/zca-js/tsconfig.json @@ -0,0 +1,104 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ES2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "NodeNext" /* Specify what module code is generated. */, + "rootDir": "./src" /* Specify the root folder within your source files. */, + "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "ts-node": { + "esm": true + }, + "exclude": ["examples", "test", "dist"] +} diff --git a/data/monitor.db b/data/monitor.db new file mode 100644 index 0000000..578853c Binary files /dev/null and b/data/monitor.db differ diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..134c2ba --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7552 @@ +{ + "name": "tmp_next", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tmp_next", + "version": "0.1.0", + "dependencies": { + "better-sqlite3": "^12.9.0", + "clsx": "^2.1.1", + "lucide-react": "^1.14.0", + "next": "16.2.6", + "node-cron": "^4.2.1", + "nodemailer": "^8.0.7", + "pdfkit": "^0.18.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "tailwind-merge": "^3.5.0", + "zca-js": "^2.1.2", + "zod": "^4.4.3" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/better-sqlite3": "^7.6.13", + "@types/node": "^20", + "@types/node-cron": "^3.0.11", + "@types/nodemailer": "^8.0.0", + "@types/pdfkit": "^0.17.6", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "tailwindcss": "^4", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz", + "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz", + "integrity": "sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz", + "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz", + "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz", + "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz", + "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz", + "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz", + "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz", + "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz", + "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.40.tgz", + "integrity": "sha512-xxx6M2IpSTnnKcR0cMvIiohkiCx20/oRPtWGbenFygKCGl3zqUzdNjQ/1V4solq1LU+dgv0nQzeGOuqkqZGg0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-cron": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-3.0.11.tgz", + "integrity": "sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/nodemailer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.0.tgz", + "integrity": "sha512-fyf8jWULsCo0d0BuoQ75i6IeoHs47qcqxWc7yUdUcV0pOZGjUTTOvwdG1PRXUDqN/8A64yQdQdnA2pZgcdi+cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pdfkit": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.17.6.tgz", + "integrity": "sha512-tIwzxk2uWKp0Cq9JIluQXJid77lYhF52EsIOwhsMF4iWLA6YneoBR1xVKYYdAysHuepUB0OX4tdwMiUDdGKmig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", + "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/better-sqlite3": { + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.9.0.tgz", + "integrity": "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.21.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz", + "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.6.tgz", + "integrity": "sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "16.2.6", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.14.0.tgz", + "integrity": "sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", + "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", + "license": "MIT", + "dependencies": { + "@next/env": "16.2.6", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.6", + "@next/swc-darwin-x64": "16.2.6", + "@next/swc-linux-arm64-gnu": "16.2.6", + "@next/swc-linux-arm64-musl": "16.2.6", + "@next/swc-linux-x64-gnu": "16.2.6", + "@next/swc-linux-x64-musl": "16.2.6", + "@next/swc-win32-arm64-msvc": "16.2.6", + "@next/swc-win32-x64-msvc": "16.2.6", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-cron": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", + "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemailer": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.7.tgz", + "integrity": "sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pdfkit": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.18.0.tgz", + "integrity": "sha512-NvUwSDZ0eYEzqAiWwVQkRkjYUkZ48kcsHuCO31ykqPPIVkwoSDjDGiwIgHHNtsiwls3z3P/zy4q00hl2chg2Ug==", + "license": "MIT", + "dependencies": { + "@noble/ciphers": "^1.0.0", + "@noble/hashes": "^1.6.0", + "fontkit": "^2.0.4", + "js-md5": "^0.8.3", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/png-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.1.0.tgz", + "integrity": "sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q==", + "dependencies": { + "browserify-zlib": "^0.2.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zca-js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/zca-js/-/zca-js-2.1.2.tgz", + "integrity": "sha512-82+zCqoIXnXEF6C9YuN3Kf7WKlyyujY/6Ejl2n8PkwazYkBK0k7kiPd8S7nHvC5Wl7vjwGRhDYeAM8zTHyoRxQ==", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/zca-js/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, + "node_modules/zca-js/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cb0e84a --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "tmp_next", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "better-sqlite3": "^12.9.0", + "clsx": "^2.1.1", + "lucide-react": "^1.14.0", + "next": "16.2.6", + "node-cron": "^4.2.1", + "nodemailer": "^8.0.7", + "pdfkit": "^0.18.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "tailwind-merge": "^3.5.0", + "zca-js": "^2.1.2", + "zod": "^4.4.3" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/better-sqlite3": "^7.6.13", + "@types/node": "^20", + "@types/node-cron": "^3.0.11", + "@types/nodemailer": "^8.0.0", + "@types/pdfkit": "^0.17.6", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..aa39580 --- /dev/null +++ b/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Zalo Monitor Run Script + +echo "๐Ÿš€ Starting Zalo Monitor & Reporter..." + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + echo "๐Ÿ“ฆ Dependencies not found. Installing..." + npm install +fi + +# Check if data directory exists +if [ ! -d "data" ]; then + echo "๐Ÿ“ Creating data directory..." + mkdir -p data +fi + +# Run the development server +echo "๐ŸŒ Launching dashboard at http://localhost:3000" +npm run dev diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts new file mode 100644 index 0000000..8fcc1dd --- /dev/null +++ b/src/app/api/login/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from "next/server"; +import { loginWithQR } from "@/lib/zalo"; + +export async function POST() { + return new Promise((resolve) => { + loginWithQR((qr) => { + resolve(NextResponse.json({ qr })); + }).catch(err => { + console.error(err); + resolve(NextResponse.json({ error: "Failed to initialize login" }, { status: 500 })); + }); + }); +} diff --git a/src/app/api/logs/route.ts b/src/app/api/logs/route.ts new file mode 100644 index 0000000..0674c5e --- /dev/null +++ b/src/app/api/logs/route.ts @@ -0,0 +1,7 @@ +import { NextResponse } from "next/server"; +import db from "@/lib/db"; + +export async function GET() { + const logs = db.prepare("SELECT * FROM messages ORDER BY timestamp DESC LIMIT 100").all(); + return NextResponse.json({ logs }); +} diff --git a/src/app/api/settings/route.ts b/src/app/api/settings/route.ts new file mode 100644 index 0000000..ca553e4 --- /dev/null +++ b/src/app/api/settings/route.ts @@ -0,0 +1,20 @@ +import { NextResponse } from "next/server"; +import db, { getSettings, saveSetting } from "@/lib/db"; + +export async function GET() { + return NextResponse.json({ + smtp_host: getSettings("smtp_host") || "", + smtp_port: getSettings("smtp_port") || "465", + smtp_user: getSettings("smtp_user") || "", + smtp_pass: getSettings("smtp_pass") || "", + report_email: getSettings("report_email") || "" + }); +} + +export async function POST(req: Request) { + const data = await req.json(); + for (const [key, value] of Object.entries(data)) { + saveSetting(key, String(value)); + } + return NextResponse.json({ success: true }); +} diff --git a/src/app/api/status/route.ts b/src/app/api/status/route.ts new file mode 100644 index 0000000..46e2138 --- /dev/null +++ b/src/app/api/status/route.ts @@ -0,0 +1,6 @@ +import { NextResponse } from "next/server"; +import { isConnected } from "@/lib/zalo"; + +export async function GET() { + return NextResponse.json({ connected: isConnected() }); +} diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/src/app/favicon.ico differ diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..1813872 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,36 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Zalo Monitor & Reporter", + description: "Real-time monitoring and daily reporting for Zalo messages", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..adf5f2f --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,401 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Shield, MessageSquare, Settings, Mail, Bell, Play, Square, RefreshCcw, FileText } from "lucide-react"; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export default function Dashboard() { + const [activeTab, setActiveTab] = useState("monitor"); + const [status, setStatus] = useState({ connected: false, loading: true }); + const [qrCode, setQrCode] = useState(null); + const [logs, setLogs] = useState([]); + const [isLoggingIn, setIsLoggingIn] = useState(false); + + useEffect(() => { + fetchStatus(); + fetchLogs(); + const interval = setInterval(fetchLogs, 5000); + return () => clearInterval(interval); + }, []); + + const fetchStatus = async () => { + try { + const res = await fetch("/api/status"); + const text = await res.text(); + if (!res.ok) throw new Error(`Status error: ${res.status}`); + try { + const data = JSON.parse(text); + setStatus({ connected: data.connected, loading: false }); + } catch (e) { + console.error("Status JSON Parse Error:", text); + throw new Error("Invalid status response"); + } + } catch (e) { + console.error("Status Check Error:", e); + setStatus({ connected: false, loading: false }); + } + }; + + const fetchLogs = async () => { + try { + const res = await fetch("/api/logs"); + const text = await res.text(); + if (!res.ok) throw new Error(`Logs error: ${res.status}`); + try { + const data = JSON.parse(text); + setLogs(data.logs || []); + } catch (e) { + console.error("Logs JSON Parse Error:", text); + } + } catch (e) { + console.error("Logs Fetch Error:", e); + } + }; + + const handleLogin = async () => { + if (isLoggingIn) return; + setIsLoggingIn(true); + setQrCode(null); + try { + const res = await fetch("/api/login", { method: "POST" }); + const text = await res.text(); + + if (!res.ok) { + console.error("Server Error Body:", text); + throw new Error(`Server returned ${res.status}: ${res.statusText}`); + } + + try { + const data = JSON.parse(text); + if (data.qr) { + setQrCode(data.qr); + } + } catch (parseError) { + console.error("JSON Parse Error. Body was:", text); + throw new Error("Invalid response format from server"); + } + } catch (e) { + console.error("Login Error:", e); + alert(e instanceof Error ? e.message : "Failed to initialize login. Please try again."); + } finally { + setIsLoggingIn(false); + } + }; + + return ( +
+ {/* Background Glow */} +
+
+
+
+ +
+ {/* Sidebar */} + + + {/* Main Content */} +
+
+ {activeTab === "monitor" && ( +
+
+
+

System Monitor

+

Real-time surveillance and status control.

+
+ {!status.connected && ( + + )} +
+ +
+ } /> + } /> + } /> +
+ + {!status.connected && qrCode && ( +
+

Scan QR Code to Login

+
+ Zalo QR +
+

+ Open Zalo on your phone, go to Menu {'>'} QR Code, and scan this code to link your account. +

+
+ )} + +
+
+

Recent Messages

+ +
+
+ {logs.slice(0, 5).map((log, i) => ( +
+
+ {log.senderName?.[0] || "?"} +
+
+
+ {log.senderName} + {new Date(log.timestamp).toLocaleTimeString()} +
+

{log.content}

+
+
+ ))} + {logs.length === 0 && ( +
+ No activity recorded yet. +
+ )} +
+
+
+ )} + + {activeTab === "logs" && } + {activeTab === "settings" && } +
+
+
+
+ ); +} + +function NavItem({ icon, label, active, onClick }: any) { + return ( + + ); +} + +function StatCard({ label, value, icon }: any) { + return ( +
+
+
{icon}
+
+
{value}
+
{label}
+
+ ); +} + +function ActivityLogs({ logs }: any) { + return ( +
+

Activity Logs

+
+ + + + + + + + + + + {logs.map((log: any, i: number) => ( + + + + + + + ))} + +
TimestampSenderTypeContent
+ {new Date(log.timestamp).toLocaleString()} + {log.senderName} + + {log.threadType} + + + {log.content} +
+
+
+ ); +} + +function SettingsPage() { + const [settings, setSettings] = useState({ + smtp_host: "", + smtp_port: "465", + smtp_user: "", + smtp_pass: "", + report_email: "" + }); + + useEffect(() => { + fetch("/api/settings").then(res => res.json()).then(data => setSettings(data)); + }, []); + + const handleSave = async (e: React.FormEvent) => { + e.preventDefault(); + await fetch("/api/settings", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(settings) + }); + alert("Settings saved!"); + }; + + return ( +
+

Configuration

+
+
+

+ + Email Settings +

+
+ setSettings({...settings, report_email: v})} + placeholder="your@email.com" + /> + setSettings({...settings, smtp_host: v})} + placeholder="smtp.gmail.com" + /> + setSettings({...settings, smtp_port: v})} + placeholder="465" + /> +
+
+ +
+

+ + Authentication +

+
+ setSettings({...settings, smtp_user: v})} + placeholder="your@gmail.com" + /> + setSettings({...settings, smtp_pass: v})} + type="password" + placeholder="App Password" + /> +
+
+ +
+
+
+
+ ); +} + +function Input({ label, value, onChange, placeholder, type = "text" }: any) { + return ( +
+ + onChange(e.target.value)} + placeholder={placeholder} + className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-slate-200 placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500/40 transition-all" + /> +
+ ); +} diff --git a/src/instrumentation.ts b/src/instrumentation.ts new file mode 100644 index 0000000..1ef40b2 --- /dev/null +++ b/src/instrumentation.ts @@ -0,0 +1,13 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === "node") { + const { initCron } = await import("./lib/cron"); + const { getZaloApi } = await import("./lib/zalo"); + + initCron(); + + // Attempt to login if credentials exist + getZaloApi().catch(err => { + console.error("Initial Zalo login failed:", err); + }); + } +} diff --git a/src/lib/cron.ts b/src/lib/cron.ts new file mode 100644 index 0000000..37a62e8 --- /dev/null +++ b/src/lib/cron.ts @@ -0,0 +1,12 @@ +import cron from "node-cron"; +import { processDailyReport } from "./reporting"; + +export function initCron() { + // Run every day at 00:01 AM + cron.schedule("1 0 * * *", () => { + console.log("Running daily report job..."); + processDailyReport(); + }); + + console.log("Cron jobs initialized"); +} diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..98cc69a --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,106 @@ +import Database from "better-sqlite3"; +import path from "node:path"; +import fs from "node:fs"; + +const DB_PATH = path.join(process.cwd(), "data/monitor.db"); + +// Ensure data directory exists +if (!fs.existsSync(path.join(process.cwd(), "data"))) { + fs.mkdirSync(path.join(process.cwd(), "data")); +} + +const db = new Database(DB_PATH); + +// Initialize schema +db.exec(` + CREATE TABLE IF NOT EXISTS messages ( + id TEXT PRIMARY KEY, + threadId TEXT NOT NULL, + senderId TEXT NOT NULL, + senderName TEXT, + content TEXT, + timestamp INTEGER NOT NULL, + threadType TEXT NOT NULL, + isSelf INTEGER DEFAULT 0 + ); + + CREATE TABLE IF NOT EXISTS threads ( + id TEXT PRIMARY KEY, + name TEXT, + type TEXT NOT NULL + ); + + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT + ); + + CREATE TABLE IF NOT EXISTS credentials ( + id INTEGER PRIMARY KEY CHECK (id = 1), + cookie TEXT, + imei TEXT, + userAgent TEXT, + updatedAt INTEGER + ); +`); + +export default db; + +export function saveMessage(message: any) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO messages (id, threadId, senderId, senderName, content, timestamp, threadType, isSelf) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `); + + let content = "[Unknown Content]"; + try { + content = typeof message.data.content === "string" + ? message.data.content + : JSON.stringify(message.data.content); + } catch (e) { + content = "[Complex Object Content]"; + } + + stmt.run( + message.data.msgId || `tmp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + message.threadId, + message.data.uidFrom, + message.data.senderName || message.data.uidFrom, + content, + message.data.timestamp || Date.now(), + message.type, + message.isSelf ? 1 : 0 + ); +} + +export function getSettings(key: string): string | null { + const stmt = db.prepare("SELECT value FROM settings WHERE key = ?"); + const row = stmt.get(key) as { value: string } | undefined; + return row ? row.value : null; +} + +export function saveSetting(key: string, value: string) { + const stmt = db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)"); + stmt.run(key, value); +} + +export function saveCredentials(cookie: any, imei: string, userAgent: string) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO credentials (id, cookie, imei, userAgent, updatedAt) + VALUES (1, ?, ?, ?, ?) + `); + stmt.run(JSON.stringify(cookie), imei, userAgent, Date.now()); +} + +export function getCredentials() { + const stmt = db.prepare("SELECT * FROM credentials WHERE id = 1"); + const row = stmt.get() as any; + if (row) { + return { + cookie: JSON.parse(row.cookie), + imei: row.imei, + userAgent: row.userAgent + }; + } + return null; +} diff --git a/src/lib/reporting.ts b/src/lib/reporting.ts new file mode 100644 index 0000000..99a7f64 --- /dev/null +++ b/src/lib/reporting.ts @@ -0,0 +1,119 @@ +import PDFDocument from "pdfkit"; +import nodemailer from "nodemailer"; +import db, { getSettings } from "./db"; +import fs from "node:fs"; +import path from "node:path"; + +export async function generateDailyReport(date: Date) { + const startOfDay = new Date(date); + startOfDay.setHours(0, 0, 0, 0); + const endOfDay = new Date(date); + endOfDay.setHours(23, 59, 59, 999); + + const messages = db.prepare(` + SELECT * FROM messages + WHERE timestamp >= ? AND timestamp <= ? + ORDER BY timestamp ASC + `).all(startOfDay.getTime(), endOfDay.getTime()) as any[]; + + if (messages.length === 0) { + console.log("No messages to report for", date.toDateString()); + return null; + } + + const doc = new PDFDocument(); + const fileName = `report-${date.toISOString().split("T")[0]}.pdf`; + const filePath = path.join(process.cwd(), "data", fileName); + const stream = fs.createWriteStream(filePath); + + doc.pipe(stream); + + // Title + doc.fontSize(20).text(`Zalo Daily Report - ${date.toDateString()}`, { align: "center" }); + doc.moveDown(); + + // Group by thread + const threads: Record = {}; + messages.forEach(msg => { + if (!threads[msg.threadId]) threads[msg.threadId] = []; + threads[msg.threadId].push(msg); + }); + + for (const [threadId, msgs] of Object.entries(threads)) { + const threadName = msgs[0].threadType === "Group" ? `Group: ${threadId}` : `User: ${msgs[0].senderName}`; + doc.fontSize(16).text(threadName, { underline: true }); + doc.moveDown(0.5); + + msgs.forEach(msg => { + const time = new Date(msg.timestamp).toLocaleTimeString(); + doc.fontSize(10).text(`[${time}] ${msg.senderName}:`, { continued: true }); + doc.fontSize(11).text(` ${msg.content}`); + }); + doc.moveDown(); + } + + doc.end(); + + return new Promise((resolve) => { + stream.on("finish", () => resolve(filePath)); + }); +} + +export async function sendEmail(filePath: string, recipient: string) { + const smtpHost = getSettings("smtp_host") || "smtp.gmail.com"; + const smtpPort = parseInt(getSettings("smtp_port") || "465"); + const smtpUser = getSettings("smtp_user"); + const smtpPass = getSettings("smtp_pass"); + + if (!smtpUser || !smtpPass) { + throw new Error("SMTP credentials not configured"); + } + + const transporter = nodemailer.createTransport({ + host: smtpHost, + port: smtpPort, + secure: smtpPort === 465, + auth: { + user: smtpUser, + pass: smtpPass, + }, + }); + + const info = await transporter.sendMail({ + from: `"Zalo Monitor" <${smtpUser}>`, + to: recipient, + subject: `Zalo Daily Report - ${new Date().toDateString()}`, + text: "Please find the attached daily report for your Zalo messages.", + attachments: [ + { + filename: path.basename(filePath), + path: filePath, + }, + ], + }); + + console.log("Email sent: %s", info.messageId); + return info; +} + +export async function processDailyReport() { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + + const recipient = getSettings("report_email"); + if (!recipient) { + console.error("Report email not configured"); + return; + } + + try { + const filePath = await generateDailyReport(yesterday); + if (filePath) { + await sendEmail(filePath, recipient); + // Optional: delete file after sending + // fs.unlinkSync(filePath); + } + } catch (error) { + console.error("Failed to process daily report:", error); + } +} diff --git a/src/lib/zalo.ts b/src/lib/zalo.ts new file mode 100644 index 0000000..68cf175 --- /dev/null +++ b/src/lib/zalo.ts @@ -0,0 +1,110 @@ +import { Zalo, ThreadType, LoginQRCallbackEventType } from "zca-js"; +import db, { saveMessage, saveCredentials, getCredentials } from "./db"; + +let zaloInstance: Zalo | null = null; +let apiInstance: any = null; +let isInitializing = false; + +function getZaloInstance() { + if (!zaloInstance) { + zaloInstance = new Zalo(); + } + return zaloInstance; +} + +export async function getZaloApi() { + if (apiInstance) return apiInstance; + + const creds = getCredentials(); + if (creds) { + try { + console.log("Attempting to login with saved credentials..."); + const zalo = getZaloInstance(); + const api = await zalo.login(creds); + apiInstance = api; + setupListener(api); + return api; + } catch (error) { + console.error("Failed to login with saved credentials:", error); + } + } + return null; +} + +export async function loginWithQR(onQrGenerated: (qrData: string) => void) { + if (isInitializing) { + console.log("Already initializing Zalo login..."); + return; + } + + isInitializing = true; + console.log("Initializing Zalo QR login..."); + const zalo = getZaloInstance(); + + try { + console.log("Calling zalo.loginQR..."); + const api = await zalo.loginQR({}, (qrData: any) => { + console.log("Zalo login event type:", qrData.type); + if (qrData.type === LoginQRCallbackEventType.QRCodeGenerated) { + console.log("QR Code generated successfully"); + let image = qrData.data.image; + if (!image.startsWith("data:image")) { + image = `data:image/png;base64,${image}`; + } + onQrGenerated(image); + } + }); + + console.log("Zalo login scan completed successfully"); + const context = api.getContext(); + saveCredentials( + context.cookie.toJSON()?.cookies, + context.imei, + context.userAgent + ); + + apiInstance = api; + setupListener(api); + return api; + } catch (error) { + console.error("Zalo loginQR process failed:", error); + throw error; + } finally { + isInitializing = false; + } +} + +function setupListener(api: any) { + api.listener.on("message", (message: any) => { + let contentStr = "[Unknown Content]"; + try { + contentStr = typeof message.data.content === "string" + ? message.data.content + : JSON.stringify(message.data.content); + } catch (e) { + contentStr = "[Complex Object Content]"; + } + + console.log(`Received message from ${message.data.senderName || message.data.uidFrom}:`, contentStr); + saveMessage(message); + }); + + api.listener.onConnected(() => { + console.log("Zalo listener connected"); + }); + + api.listener.onClosed(() => { + console.log("Zalo listener closed"); + apiInstance = null; + }); + + api.listener.onError((error: any) => { + console.error("Zalo listener error:", error); + }); + + api.listener.start(); +} + +export function isConnected() { + return !!apiInstance; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +}