├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── docs ├── .vitepress │ └── config.mts ├── contribute │ ├── contribution.md │ └── report-bugs.md ├── getting-started │ ├── changelog.md │ ├── installation.md │ └── introduction.md ├── index.md ├── public │ ├── css │ │ └── style.css │ └── img │ │ ├── laravel-red.svg │ │ ├── laravel_black.svg │ │ ├── logo-full-scream.png │ │ ├── logo-github.png │ │ └── logo.png └── usage │ ├── blade-directives.md │ └── class-methods.md ├── package-lock.json ├── package.json ├── src ├── Agent.php ├── Designators │ └── Identifiers.php ├── Exceptions │ └── AgentException.php ├── Providers │ └── AgentProvider.php ├── Response │ └── Browser.php ├── Server │ └── UserAgent.php └── Tools │ └── Utilities.php └── test └── AgentTest.php /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy VitePress site to Pages 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 17 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 18 | concurrency: 19 | group: pages 20 | cancel-in-progress: false 21 | 22 | jobs: 23 | # Build job 24 | build: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 0 # Not needed if lastUpdated is not enabled 31 | # - uses: pnpm/action-setup@v2 # Uncomment this if you're using pnpm 32 | # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun 33 | - name: Setup Node 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 18 37 | cache: npm # or pnpm / yarn 38 | - name: Setup Pages 39 | uses: actions/configure-pages@v3 40 | - name: Install dependencies 41 | run: npm ci # or pnpm install / yarn install / bun install 42 | - name: Build with VitePress 43 | run: | 44 | npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build 45 | touch docs/.vitepress/dist/.nojekyll 46 | - name: Upload artifact 47 | uses: actions/upload-pages-artifact@v2 48 | with: 49 | path: docs/.vitepress/dist 50 | 51 | # Deployment job 52 | deploy: 53 | environment: 54 | name: github-pages 55 | url: ${{ steps.deployment.outputs.page_url }} 56 | needs: build 57 | runs-on: ubuntu-latest 58 | name: Deploy 59 | steps: 60 | - name: Deploy to GitHub Pages 61 | id: deployment 62 | uses: actions/deploy-pages@v2 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore common macOS files and directories 2 | **/.DS_Store 3 | 4 | # Ignore automatically generated files and directories 5 | /vendor/ 6 | /.fleet 7 | /.idea 8 | /.vscode 9 | 10 | # Ignore Composer generated directory and files 11 | /vendor 12 | 13 | # Ignore development tool generated files and directories 14 | .fleet 15 | .idea 16 | .vscode 17 | composer.lock 18 | 19 | # Node 20 | node_modules 21 | docs/.vitepress/cache 22 | docs/.vitepress/dist 23 | docs/.vitepress/.temp -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Raúl Mauricio Uñate Castro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agent Detection 2 | 3 | ## 🚀 Discovery and Analysis of Connection Agent Information 🚀 4 | 5 | This simple library allows you to obtain specific details about the connection agent, which will enable you to enhance the user experience based on connection data. For example, if you detect that it's a mobile connection, you can invite the user to download the mobile application if your system has one. Similarly, you can offer a more personalized experience for Android or iPhone users, differentiate options, menus, and other elements between desktop and mobile device users. 6 | 7 | Additionally, this library allows you to determine if it is a bot, crawler, or spider. In essence, you have endless possibilities at your disposal.ss methods! 💻✨ 8 | 9 | ## Documentation 10 | [![📖📖📖 **FULL DOCUMENTATION** 📖📖📖](https://img.shields.io/badge/FULL%20DOCUMENTATION-Visit%20Here-blue?style=for-the-badge)](https://rmunate.github.io/AgentDetection/) 11 | 12 | ## Installation 13 | To install the dependency via Composer. 14 | 15 | ```shell 16 | composer require rmunate/agent-detection 17 | ``` 18 | 19 | ## License 20 | This project is under the [MIT License](https://choosealicense.com/licenses/mit/). 21 | 22 | 🌟 Support My Projects! 🚀 23 | 24 | [![Become a Sponsor](https://img.shields.io/badge/-Become%20a%20Sponsor-blue?style=for-the-badge&logo=github)](https://github.com/sponsors/rmunate) 25 | 26 | Make any contributions you see fit; the code is entirely yours. Together, we can do amazing things and improve the world of development. Your support is invaluable. ✨ 27 | 28 | If you have ideas, suggestions, or just want to collaborate, we are open to everything! Join our community and be part of our journey to success! 🌐👩‍💻👨‍💻 29 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rmunate/agent-detection", 3 | "description": "The Agent Detection library offers a wide variety of methods that allow you to explore and analyze connection agent data in your application.", 4 | "keywords": [ 5 | "laravel", 6 | "php", 7 | "agent" 8 | ], 9 | "homepage": "https://github.com/rmunate/AgentDetection", 10 | "type": "library", 11 | "license": "MIT", 12 | "autoload": { 13 | "psr-4": { 14 | "Rmunate\\AgentDetection\\": "src/" 15 | } 16 | }, 17 | "authors": [ 18 | { 19 | "name": "Raul Mauricio Uñate Castro", 20 | "email": "raulmauriciounate@gmail.com", 21 | "homepage": "https://github.com/rmunate", 22 | "role": "owner" 23 | } 24 | ], 25 | "require": { 26 | "php": "^7.4|^8.0", 27 | "illuminate/support": "^8.0|^9.0|^10.0|^11.0", 28 | "jaybizzle/crawler-detect": "^1.2", 29 | "mobiledetect/mobiledetectlib": "^4.8" 30 | }, 31 | "require-dev": { 32 | "phpstan/phpstan": "^1.10", 33 | "phpunit/phpunit": "^10.4|^11.0" 34 | }, 35 | "extra" : { 36 | "branch-alias" : { 37 | "dev-main": "v1.0.x-dev" 38 | }, 39 | "laravel": { 40 | "providers": [ 41 | "Rmunate\\AgentDetection\\Providers\\AgentProvider" 42 | ] 43 | } 44 | }, 45 | "minimum-stability": "dev", 46 | "prefer-stable": true 47 | } 48 | -------------------------------------------------------------------------------- /docs/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vitepress' 2 | 3 | export default defineConfig({ 4 | title: "Laravel Agent Detection", 5 | description: "Discovery and Analysis of Connection Agent Information!.", 6 | lang: 'en-US', 7 | lastUpdated: false, 8 | base: '/AgentDetection', 9 | themeConfig: { 10 | footer: { 11 | message: 'Released under the MIT License.', 12 | copyright: 'Copyright © 2021-2024 Raul Mauricio Uñate' 13 | }, 14 | editLink: { 15 | pattern: 'https://github.com/rmunate/AgentDetection/tree/main/docs/:path' 16 | }, 17 | logo: '/img/logo.png', 18 | nav: [ 19 | {text: 'v1.6.0', link: '/'}, 20 | ], 21 | sidebar: [ 22 | { 23 | text: 'Getting Started', 24 | collapsed: false, 25 | items: [ 26 | {text: 'Introduction', link: '/getting-started/introduction'}, 27 | {text: 'Installation', link: '/getting-started/installation'}, 28 | {text: 'Release Notes', link: '/getting-started/changelog'}, 29 | ] 30 | }, { 31 | text: 'Usage', 32 | collapsed: false, 33 | items: [ 34 | {text: 'Class Methods', link: '/usage/class-methods'}, 35 | {text: 'Blade Directives', link: '/usage/blade-directives'}, 36 | ] 37 | }, { 38 | text: 'Contribute', 39 | collapsed: false, 40 | items: [ 41 | {text: 'Bug Report', link: 'contribute/report-bugs'}, 42 | {text: 'Contribution', link: 'contribute/contribution'} 43 | ] 44 | } 45 | ], 46 | 47 | socialLinks: [ 48 | {icon: 'github', link: 'https://github.com/rmunate/AgentDetection'} 49 | ], 50 | search: { 51 | provider: 'local' 52 | } 53 | }, 54 | head: [ 55 | ['link', { 56 | rel: 'stylesheet', 57 | href: '/AgentDetection/css/style.css' 58 | } 59 | ], 60 | ['link', { 61 | rel: 'icon', 62 | href: '/AgentDetection/img/logo.png', 63 | type: 'image/png' 64 | } 65 | ], 66 | ['meta', { 67 | property: 'og:image', 68 | content: '/AgentDetection/img/logo-github.png' 69 | } 70 | ], 71 | ['meta', { 72 | property: 'og:image:secure_url', 73 | content: '/AgentDetection/img/logo-github.png' 74 | } 75 | ], 76 | ['meta', { 77 | property: 'og:image:width', 78 | content: '600' 79 | } 80 | ], 81 | ['meta', { 82 | property: 'og:image:height', 83 | content: '400' 84 | } 85 | ], 86 | ['meta', { 87 | property: 'og:title', 88 | content: 'AgentDetection' 89 | } 90 | ], 91 | ['meta', { 92 | property: 'og:description', 93 | content: 'Discovery and Analysis of Connection Agent Information! 🚀' 94 | } 95 | ], 96 | ['meta', { 97 | property: 'og:url', 98 | content: 'https://rmunate.github.io/AgentDetection/' 99 | } 100 | ], 101 | ['meta', { 102 | property: 'og:type', 103 | content: 'website' 104 | } 105 | ], 106 | ], 107 | }) 108 | -------------------------------------------------------------------------------- /docs/contribute/contribution.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | 32 | 33 | # Contributing 34 | 35 | If you want to add support for a new language or want to develop new features, you can submit your requests to the main branch of the repository. 36 | 37 | ## Contributors 38 | 39 | To date, the following world-class developers have contributed their knowledge. 40 | 41 | To whom we thank for supporting making programming easier. 42 | 43 | 44 | 45 | ## License 46 | This project is under the [MIT License](https://choosealicense.com/licenses/mit/). 47 | 48 | 🌟 Support My Projects! 🚀 49 | 50 | [![Become a Sponsor](https://img.shields.io/badge/-Become%20a%20Sponsor-blue?style=for-the-badge&logo=github)](https://github.com/sponsors/rmunate) 51 | 52 | Make any contributions you see fit; the code is entirely yours. Together, we can do amazing things and improve the world of development. Your support is invaluable. ✨ 53 | 54 | If you have ideas, suggestions, or just want to collaborate, we are open to everything! Join our community and be part of our journey to success! 🌐👩‍💻👨‍💻 -------------------------------------------------------------------------------- /docs/contribute/report-bugs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bug Report 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Bug Report 8 | 9 | If you find errors or opportunities within the package, you can create an incident that we will attend to in the shortest time possible. 10 | 11 | Here!: 12 | [https://github.com/rmunate/AgentDetection/issues/new](https://github.com/rmunate/AgentDetection/issues/new) 13 | 14 | Remember that you can also contribute as a collaborator of this solution. 15 | -------------------------------------------------------------------------------- /docs/getting-started/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Release Notes 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Release Notes 8 | 9 | ## [1.4.0] - 2023-12-10 10 | 11 | ### Added 12 | 13 | - **Static Access**: Now, the use of the `detected` method is no longer necessary to query connection agent data. 14 | 15 | Example 16 | 17 | ```php 18 | use Rmunate\AgentDetection\Agent; 19 | 20 | #Now 21 | Agent::isDesktop(); 22 | 23 | #Before (Still works) 24 | Agent::detect()->isDesktop(); 25 | ``` 26 | 27 | ### Changed 28 | 29 | - **Blade Directive Update**: Blade directives have been updated to be shorter and more concise. 30 | 31 | Example 32 | 33 | ```blade 34 | @agentMobile 35 |

Is Mobile

36 | @endAgentMobile 37 | ``` -------------------------------------------------------------------------------- /docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Installation 8 | 9 | ## Requirements 10 | 11 | To use the package in the current version, you must have at least PHP version 8.0. 12 | 13 | ## Installation 14 | 15 | ### Composer 16 | 17 | To install the dependency using Composer, execute the following command: 18 | 19 | ```bash 20 | composer require rmunate/agent-detection 21 | ``` 22 | 23 | **That's all!** -------------------------------------------------------------------------------- /docs/getting-started/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | ![logo-spell-number](/img/logo-full-scream.png) 8 | 9 | ## Introduction 10 | 11 | This straightforward library empowers you to extract specific details about the connection agent, enabling you to enhance the user experience based on connection data. For instance, if you identify a mobile connection, you can prompt the user to download the mobile application if your system supports it. Similarly, you can provide a more tailored experience for Android or iPhone users, distinguish options, menus, and other elements between desktop and mobile users. 12 | 13 | Additionally, this library allows you to determine if it is a bot, crawler, or spider. In essence, you have endless possibilities at your disposal.ss methods -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: Laravel 6 | text: Agent Detection 7 | tagline: Discovery and Analysis of Connection Agent Information! 🚀 8 | image: 9 | src: /img/logo.png 10 | alt: AgentDetection 11 | actions: 12 | - theme: brand 13 | text: Get Started 14 | link: /getting-started/introduction 15 | - theme: alt 16 | text: View on GitHub 17 | link: https://github.com/rmunate/AgentDetection 18 | 19 | features: 20 | - icon: 💻 21 | title: User Operating System 22 | details: Retrieve the user's operating system information to tailor the user experience accordingly. 23 | - icon: 🌐 24 | title: Browser Information 25 | details: Identify the browser users are using, check its version, and take specific actions based on the browser type. 26 | - icon: 🕷️ 27 | title: Crawler Detection 28 | details: Easily identify crawler connections for distinguishing them from regular user connections. 29 | -------------------------------------------------------------------------------- /docs/public/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-home-hero-name-color: red; 3 | --vp-button-brand-border: #B42121; 4 | --vp-button-brand-bg: #B42121; 5 | --vp-button-brand-hover-bg: #4E0404; 6 | --vp-c-brand-1: red; 7 | } 8 | 9 | .tagline{ 10 | font-size: 22px !important; 11 | } -------------------------------------------------------------------------------- /docs/public/img/laravel-red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/public/img/laravel_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/img/logo-full-scream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmunate/AgentDetection/c0b66e5cdef7d9baf381fe46ed1d24a62dfadb9a/docs/public/img/logo-full-scream.png -------------------------------------------------------------------------------- /docs/public/img/logo-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmunate/AgentDetection/c0b66e5cdef7d9baf381fe46ed1d24a62dfadb9a/docs/public/img/logo-github.png -------------------------------------------------------------------------------- /docs/public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmunate/AgentDetection/c0b66e5cdef7d9baf381fe46ed1d24a62dfadb9a/docs/public/img/logo.png -------------------------------------------------------------------------------- /docs/usage/blade-directives.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Blade Directives 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Blade Directives 8 | 9 | If you're using the package to conditionally render HTML based on the connection agent, you can do so in a simple and elegant way. Here are all the directives included in the package. 10 | 11 | ## Is Mobile Connection 12 | 13 | This directive validates if the connection is from a mobile device (tablet, smartphone, etc.). You can use it directly in the Blade views. 14 | 15 | ```blade 16 | @agentMobile 17 |

Mobile Connection Detected

18 | @endAgentMobile 19 | ``` 20 | 21 | ## Is Desktop Connection 22 | 23 | This directive validates if the connection is from a desktop device. You can use it directly in the Blade views. 24 | 25 | ```blade 26 | @agentDesktop 27 |

Desktop Connection Detected

28 | @endAgentDesktop 29 | ``` 30 | 31 | ## From iPhone 32 | 33 | This directive validates if the connection is from an iPhone specifically, not validating iPad or similar devices; it is limited to checking if the connecting device is an iPhone. 34 | 35 | ```blade 36 | @agentIPhone 37 |

Connection from iPhone Detected

38 | @endAgentIPhone 39 | ``` 40 | 41 | ## From Macintosh 42 | 43 | This directive validates if the connection is from a Macintosh device specifically, not validating iMac or similar devices. 44 | 45 | ```blade 46 | @agentMacintosh 47 |

Connection from Macintosh Detected

48 | @endAgentMacintosh 49 | ``` 50 | 51 | ## From iMac 52 | 53 | This directive validates if the connection is from an iMac specifically, not validating Macintosh or similar devices. 54 | 55 | ```blade 56 | @agentIMac 57 |

Connection from iMac Detected

58 | @endAgentIMac 59 | ``` 60 | 61 | ## From iPod 62 | 63 | This directive validates if the connection is from an iPod specifically, not validating similar devices. 64 | 65 | ```blade 66 | @agentIpod 67 |

Connection from iPod Detected

68 | @endAgentIpod 69 | ``` 70 | 71 | ## From iPad 72 | 73 | This directive validates if the connection is from an iPad specifically, not validating similar devices. 74 | 75 | ```blade 76 | @agentIpad 77 |

Connection from iPad Detected

78 | @endAgentIpad 79 | ``` 80 | 81 | ## From Linux 82 | 83 | This directive validates if the connection is from a device with the Linux operating system (regardless of the distribution). 84 | 85 | ```blade 86 | @agentLinux 87 |

Connection from Linux Device Detected

88 | @endAgentLinux 89 | ``` 90 | 91 | ## From Android 92 | 93 | This directive validates if the connection is from a device with the Android operating system, commonly working on Linux but determining if an Android is in use. 94 | 95 | ```blade 96 | @agentAndroid 97 |

Connection from Android Device Detected

98 | @endAgentAndroid 99 | ``` 100 | 101 | ## From Windows 102 | 103 | This directive validates if the connection is from a device with the Windows operating system, specifically Windows Desktop. 104 | 105 | ```blade 106 | @agentWindows 107 |

Connection from Windows Device Detected

108 | @endAgentWindows 109 | ``` 110 | 111 | ## From Windows Phone 112 | 113 | Although rare, if there are still users with this technology, we can identify them. 114 | 115 | ```blade 116 | @agentWindowsPhone 117 |

Connection from Windows Phone Detected

118 | @endAgentWindowsPhone 119 | ``` 120 | 121 | ## From a Tablet 122 | 123 | Sometimes we need to adjust content for tablets, so with this method, you can determine if the connection is from a tablet. 124 | 125 | ```blade 126 | @agentTablet 127 |

Connection from Tablet Detected

128 | @endAgentTablet 129 | ``` 130 | 131 | ## Is Crawler 132 | 133 | If we need to validate if the agent is a crawler, we can do so extremely simply: 134 | 135 | ```blade 136 | @agentCrawler 137 |

Connection from Crawler Detected

138 | @endAgentCrawler 139 | ``` 140 | 141 | ## Validate by Match 142 | 143 | If you want to have a dynamic method of validating the agent, you may find it convenient to use the following method supplied by the package: 144 | 145 | ```blade 146 | @agentMatch('Mac') 147 |

Connection from Mac Detected

148 | @endAgentMatch 149 | ``` 150 | 151 | ## Connection IP: 152 | 153 | You can also find out through which IP the Agent connected to the application: 154 | 155 | ```blade 156 | @agentRemoteIp('181.10.10.23') 157 |

Connection from IP: 181.10.10.23 Detected

158 | @endAgentRemoteIp 159 | ``` 160 | 161 | ## Connection Port 162 | 163 | It may be useful to know through which port the connection occurred, so here it is: 164 | 165 | ```blade 166 | @agentRemotePort('8080') 167 |

Connection from Port: 8080 Detected

168 | @endAgentRemotePort 169 | ``` -------------------------------------------------------------------------------- /docs/usage/class-methods.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Class Methods 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Class Methods 8 | 9 | On this page, you will learn about all the methods currently available within the package. You may be interested in adding new features, so we invite you to contribute to this useful solution. 10 | 11 | ## Get 12 | 13 | This method returns the complete user connection agent based on the information gathered from the PHP global variable $_SERVER. 14 | 15 | ```php 16 | use Rmunate\AgentDetection\Agent; 17 | 18 | Agent::get(); 19 | // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ... Safari/537.36" 20 | ``` 21 | 22 | ## Set 23 | 24 | At times, you may need the package to analyze an agent different from the one identified by the server. In such cases, you can use the `set` method, passing a `string` with the agent value: 25 | 26 | ```php 27 | use Rmunate\AgentDetection\Agent; 28 | 29 | Agent::set("Mozilla/5.0...")->isMobile(); 30 | // True Or False 31 | ``` 32 | 33 | After the `set` method, you can chain any of the methods available in this guide. 34 | 35 | ## Detected 36 | 37 | This method instructs the class to take the value identified by the server and then chain any other method from this guide. 38 | 39 | ```php 40 | use Rmunate\AgentDetection\Agent; 41 | 42 | Agent::detected()->isMobile(); 43 | // True Or False 44 | ``` 45 | 46 | Since version 1.4.0, its use is not mandatory; now you can pass the final method directly to the class for querying. 47 | 48 | ## Is Mobile Connection 49 | 50 | This method validates if the connection is from a mobile device (tablet, smartphone, etc.). You can use it directly on the Agent class. 51 | 52 | ```php 53 | use Rmunate\AgentDetection\Agent; 54 | 55 | Agent::isMobile(); 56 | // True Or False 57 | ``` 58 | 59 | ## Is Desktop Connection 60 | 61 | This method validates if the connection is from a desktop device. You can use it directly on the Agent class. 62 | 63 | ```php 64 | use Rmunate\AgentDetection\Agent; 65 | 66 | Agent::isDesktop(); 67 | // True Or False 68 | ``` 69 | 70 | ## From iPhone 71 | 72 | This method validates if the connection is from an iPhone specifically, not validating iPad or similar devices; it is limited to checking if the connecting device is an iPhone. 73 | 74 | ```php 75 | use Rmunate\AgentDetection\Agent; 76 | 77 | Agent::isIPhone(); 78 | // True Or False 79 | ``` 80 | 81 | ## From Macintosh 82 | 83 | This method validates if the connection is from a Macintosh device specifically, not validating iMac or similar devices. 84 | 85 | ```php 86 | use Rmunate\AgentDetection\Agent; 87 | 88 | Agent::isMacintosh(); 89 | // True Or False 90 | ``` 91 | 92 | ## From iMac 93 | 94 | This method validates if the connection is from an iMac specifically, not validating Macintosh or similar devices. 95 | 96 | ```php 97 | use Rmunate\AgentDetection\Agent; 98 | 99 | Agent::isIMac(); 100 | // True Or False 101 | ``` 102 | 103 | ## From iPod 104 | 105 | This method validates if the connection is from an iPod specifically, not validating similar devices. 106 | 107 | ```php 108 | use Rmunate\AgentDetection\Agent; 109 | 110 | Agent::isIpod(); 111 | // True Or False 112 | ``` 113 | 114 | ## From iPad 115 | 116 | This method validates if the connection is from an iPad specifically, not validating similar devices. 117 | 118 | ```php 119 | use Rmunate\AgentDetection\Agent; 120 | 121 | Agent::isIpad(); 122 | // True Or False 123 | ``` 124 | 125 | ## From Linux 126 | 127 | This method validates if the connection is from a device with the Linux operating system (regardless of the distribution). 128 | 129 | ```php 130 | use Rmunate\AgentDetection\Agent; 131 | 132 | Agent::isLinux(); 133 | // True Or False 134 | ``` 135 | 136 | ## From Android 137 | 138 | This method validates if the connection is from a device with the Android operating system, commonly working on Linux but determining if an Android is in use. 139 | 140 | ```php 141 | use Rmunate\AgentDetection\Agent; 142 | 143 | Agent::isAndroid(); 144 | // True Or False 145 | ``` 146 | 147 | ## From Windows 148 | 149 | This method validates if the connection is from a device with the Windows operating system, specifically Windows Desktop. 150 | 151 | ```php 152 | use Rmunate\AgentDetection\Agent; 153 | 154 | Agent::isWindows(); 155 | // True Or False 156 | ``` 157 | 158 | ## From Windows Phone 159 | 160 | Although rare, if there are still users with this technology, we can identify them. 161 | 162 | ```php 163 | use Rmunate\AgentDetection\Agent; 164 | 165 | Agent::isWindowsPhone(); 166 | // True Or False 167 | ``` 168 | 169 | ## From a Tablet 170 | 171 | Sometimes we need to adjust content for tablets, so with this method, you can determine if the connection is from a tablet. 172 | 173 | ```php 174 | use Rmunate\AgentDetection\Agent; 175 | 176 | Agent::isTablet(); 177 | // True Or False 178 | ``` 179 | 180 | ## Is Crawler 181 | 182 | If we need to validate if the agent is a crawler, we can do so extremely simply: 183 | 184 | ```php 185 | use Rmunate\AgentDetection\Agent; 186 | 187 | Agent::isCrawler(); 188 | // True Or False 189 | 190 | Agent::set('Mozilla/5.0 (compatible; Sosospider/2.0; +http://help.soso.com/webspider.htm)')->isCrawler(); 191 | // True Or False 192 | ``` 193 | 194 | ## Get Crawler 195 | 196 | If, more than validating if it is a crawler, you need to extract it from the agent, you can use the following method: 197 | 198 | ```php 199 | use Rmunate\AgentDetection\Agent; 200 | 201 | Agent::set('Mozilla/5.0 (compatible; Sosospider/2.0; +http://help.soso.com/webspider.htm)')->getCrawler(); 202 | // "Sosospider" 203 | ``` 204 | 205 | ## Validate by Match 206 | 207 | If you want to have a dynamic method of validating the agent, you may find it convenient to use the following method supplied by the package: 208 | 209 | ```php 210 | use Rmunate\AgentDetection\Agent; 211 | 212 | Agent::match('Mac'); 213 | // True Or False 214 | ``` 215 | 216 | ## Get Operating System 217 | 218 | If you want to extract the value of the operating system in use from the agent, it will be more convenient to use the following method. 219 | 220 | ```php 221 | use Rmunate\AgentDetection\Agent; 222 | 223 | Agent::clientOS(); 224 | // 'Windows' // 'Mac' // 'Linux' // 'Android' // 'iOS' 225 | ``` 226 | 227 | ## Connection IP 228 | 229 | You can also find out through which IP the Agent connected to the application: 230 | 231 | ```php 232 | use Rmunate\AgentDetection\Agent; 233 | 234 | Agent::remoteAddress(); 235 | ``` 236 | 237 | ## Connection Port 238 | 239 | It may be useful to know through which port the connection occurred, so here it is: 240 | 241 | ```php 242 | use Rmunate\AgentDetection\Agent; 243 | 244 | Agent::remotePort(); 245 | ``` 246 | 247 | ## Browser 248 | 249 | Finally, we might be interested in the browser used to access our applications, so with the following method, you can extract all the connection browser data from the agent: 250 | 251 | ```php 252 | use Rmunate\AgentDetection\Agent; 253 | 254 | $browser = Agent::browser(); 255 | 256 | $browserName = $browser->getName(); 257 | $browserVersion = $browser->getVersion(); 258 | $browserPlatform = $browser->getPlatform(); 259 | ``` -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AgentDetection", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "vitepress": "^1.0.0-rc.13" 9 | } 10 | }, 11 | "node_modules/@algolia/autocomplete-core": { 12 | "version": "1.9.3", 13 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", 14 | "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", 15 | "dev": true, 16 | "dependencies": { 17 | "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", 18 | "@algolia/autocomplete-shared": "1.9.3" 19 | } 20 | }, 21 | "node_modules/@algolia/autocomplete-plugin-algolia-insights": { 22 | "version": "1.9.3", 23 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", 24 | "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", 25 | "dev": true, 26 | "dependencies": { 27 | "@algolia/autocomplete-shared": "1.9.3" 28 | }, 29 | "peerDependencies": { 30 | "search-insights": ">= 1 < 3" 31 | } 32 | }, 33 | "node_modules/@algolia/autocomplete-preset-algolia": { 34 | "version": "1.9.3", 35 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", 36 | "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", 37 | "dev": true, 38 | "dependencies": { 39 | "@algolia/autocomplete-shared": "1.9.3" 40 | }, 41 | "peerDependencies": { 42 | "@algolia/client-search": ">= 4.9.1 < 6", 43 | "algoliasearch": ">= 4.9.1 < 6" 44 | } 45 | }, 46 | "node_modules/@algolia/autocomplete-shared": { 47 | "version": "1.9.3", 48 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", 49 | "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", 50 | "dev": true, 51 | "peerDependencies": { 52 | "@algolia/client-search": ">= 4.9.1 < 6", 53 | "algoliasearch": ">= 4.9.1 < 6" 54 | } 55 | }, 56 | "node_modules/@algolia/cache-browser-local-storage": { 57 | "version": "4.20.0", 58 | "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.20.0.tgz", 59 | "integrity": "sha512-uujahcBt4DxduBTvYdwO3sBfHuJvJokiC3BP1+O70fglmE1ShkH8lpXqZBac1rrU3FnNYSUs4pL9lBdTKeRPOQ==", 60 | "dev": true, 61 | "dependencies": { 62 | "@algolia/cache-common": "4.20.0" 63 | } 64 | }, 65 | "node_modules/@algolia/cache-common": { 66 | "version": "4.20.0", 67 | "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.20.0.tgz", 68 | "integrity": "sha512-vCfxauaZutL3NImzB2G9LjLt36vKAckc6DhMp05An14kVo8F1Yofb6SIl6U3SaEz8pG2QOB9ptwM5c+zGevwIQ==", 69 | "dev": true 70 | }, 71 | "node_modules/@algolia/cache-in-memory": { 72 | "version": "4.20.0", 73 | "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.20.0.tgz", 74 | "integrity": "sha512-Wm9ak/IaacAZXS4mB3+qF/KCoVSBV6aLgIGFEtQtJwjv64g4ePMapORGmCyulCFwfePaRAtcaTbMcJF+voc/bg==", 75 | "dev": true, 76 | "dependencies": { 77 | "@algolia/cache-common": "4.20.0" 78 | } 79 | }, 80 | "node_modules/@algolia/client-account": { 81 | "version": "4.20.0", 82 | "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.20.0.tgz", 83 | "integrity": "sha512-GGToLQvrwo7am4zVkZTnKa72pheQeez/16sURDWm7Seyz+HUxKi3BM6fthVVPUEBhtJ0reyVtuK9ArmnaKl10Q==", 84 | "dev": true, 85 | "dependencies": { 86 | "@algolia/client-common": "4.20.0", 87 | "@algolia/client-search": "4.20.0", 88 | "@algolia/transporter": "4.20.0" 89 | } 90 | }, 91 | "node_modules/@algolia/client-analytics": { 92 | "version": "4.20.0", 93 | "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.20.0.tgz", 94 | "integrity": "sha512-EIr+PdFMOallRdBTHHdKI3CstslgLORQG7844Mq84ib5oVFRVASuuPmG4bXBgiDbcsMLUeOC6zRVJhv1KWI0ug==", 95 | "dev": true, 96 | "dependencies": { 97 | "@algolia/client-common": "4.20.0", 98 | "@algolia/client-search": "4.20.0", 99 | "@algolia/requester-common": "4.20.0", 100 | "@algolia/transporter": "4.20.0" 101 | } 102 | }, 103 | "node_modules/@algolia/client-common": { 104 | "version": "4.20.0", 105 | "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.20.0.tgz", 106 | "integrity": "sha512-P3WgMdEss915p+knMMSd/fwiHRHKvDu4DYRrCRaBrsfFw7EQHon+EbRSm4QisS9NYdxbS04kcvNoavVGthyfqQ==", 107 | "dev": true, 108 | "dependencies": { 109 | "@algolia/requester-common": "4.20.0", 110 | "@algolia/transporter": "4.20.0" 111 | } 112 | }, 113 | "node_modules/@algolia/client-personalization": { 114 | "version": "4.20.0", 115 | "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.20.0.tgz", 116 | "integrity": "sha512-N9+zx0tWOQsLc3K4PVRDV8GUeOLAY0i445En79Pr3zWB+m67V+n/8w4Kw1C5LlbHDDJcyhMMIlqezh6BEk7xAQ==", 117 | "dev": true, 118 | "dependencies": { 119 | "@algolia/client-common": "4.20.0", 120 | "@algolia/requester-common": "4.20.0", 121 | "@algolia/transporter": "4.20.0" 122 | } 123 | }, 124 | "node_modules/@algolia/client-search": { 125 | "version": "4.20.0", 126 | "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.20.0.tgz", 127 | "integrity": "sha512-zgwqnMvhWLdpzKTpd3sGmMlr4c+iS7eyyLGiaO51zDZWGMkpgoNVmltkzdBwxOVXz0RsFMznIxB9zuarUv4TZg==", 128 | "dev": true, 129 | "dependencies": { 130 | "@algolia/client-common": "4.20.0", 131 | "@algolia/requester-common": "4.20.0", 132 | "@algolia/transporter": "4.20.0" 133 | } 134 | }, 135 | "node_modules/@algolia/logger-common": { 136 | "version": "4.20.0", 137 | "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.20.0.tgz", 138 | "integrity": "sha512-xouigCMB5WJYEwvoWW5XDv7Z9f0A8VoXJc3VKwlHJw/je+3p2RcDXfksLI4G4lIVncFUYMZx30tP/rsdlvvzHQ==", 139 | "dev": true 140 | }, 141 | "node_modules/@algolia/logger-console": { 142 | "version": "4.20.0", 143 | "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.20.0.tgz", 144 | "integrity": "sha512-THlIGG1g/FS63z0StQqDhT6bprUczBI8wnLT3JWvfAQDZX5P6fCg7dG+pIrUBpDIHGszgkqYEqECaKKsdNKOUA==", 145 | "dev": true, 146 | "dependencies": { 147 | "@algolia/logger-common": "4.20.0" 148 | } 149 | }, 150 | "node_modules/@algolia/requester-browser-xhr": { 151 | "version": "4.20.0", 152 | "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.20.0.tgz", 153 | "integrity": "sha512-HbzoSjcjuUmYOkcHECkVTwAelmvTlgs48N6Owt4FnTOQdwn0b8pdht9eMgishvk8+F8bal354nhx/xOoTfwiAw==", 154 | "dev": true, 155 | "dependencies": { 156 | "@algolia/requester-common": "4.20.0" 157 | } 158 | }, 159 | "node_modules/@algolia/requester-common": { 160 | "version": "4.20.0", 161 | "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.20.0.tgz", 162 | "integrity": "sha512-9h6ye6RY/BkfmeJp7Z8gyyeMrmmWsMOCRBXQDs4mZKKsyVlfIVICpcSibbeYcuUdurLhIlrOUkH3rQEgZzonng==", 163 | "dev": true 164 | }, 165 | "node_modules/@algolia/requester-node-http": { 166 | "version": "4.20.0", 167 | "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.20.0.tgz", 168 | "integrity": "sha512-ocJ66L60ABSSTRFnCHIEZpNHv6qTxsBwJEPfYaSBsLQodm0F9ptvalFkHMpvj5DfE22oZrcrLbOYM2bdPJRHng==", 169 | "dev": true, 170 | "dependencies": { 171 | "@algolia/requester-common": "4.20.0" 172 | } 173 | }, 174 | "node_modules/@algolia/transporter": { 175 | "version": "4.20.0", 176 | "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.20.0.tgz", 177 | "integrity": "sha512-Lsii1pGWOAISbzeyuf+r/GPhvHMPHSPrTDWNcIzOE1SG1inlJHICaVe2ikuoRjcpgxZNU54Jl+if15SUCsaTUg==", 178 | "dev": true, 179 | "dependencies": { 180 | "@algolia/cache-common": "4.20.0", 181 | "@algolia/logger-common": "4.20.0", 182 | "@algolia/requester-common": "4.20.0" 183 | } 184 | }, 185 | "node_modules/@babel/parser": { 186 | "version": "7.23.0", 187 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", 188 | "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", 189 | "dev": true, 190 | "bin": { 191 | "parser": "bin/babel-parser.js" 192 | }, 193 | "engines": { 194 | "node": ">=6.0.0" 195 | } 196 | }, 197 | "node_modules/@docsearch/css": { 198 | "version": "3.5.2", 199 | "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.2.tgz", 200 | "integrity": "sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==", 201 | "dev": true 202 | }, 203 | "node_modules/@docsearch/js": { 204 | "version": "3.5.2", 205 | "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.5.2.tgz", 206 | "integrity": "sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==", 207 | "dev": true, 208 | "dependencies": { 209 | "@docsearch/react": "3.5.2", 210 | "preact": "^10.0.0" 211 | } 212 | }, 213 | "node_modules/@docsearch/react": { 214 | "version": "3.5.2", 215 | "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.2.tgz", 216 | "integrity": "sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==", 217 | "dev": true, 218 | "dependencies": { 219 | "@algolia/autocomplete-core": "1.9.3", 220 | "@algolia/autocomplete-preset-algolia": "1.9.3", 221 | "@docsearch/css": "3.5.2", 222 | "algoliasearch": "^4.19.1" 223 | }, 224 | "peerDependencies": { 225 | "@types/react": ">= 16.8.0 < 19.0.0", 226 | "react": ">= 16.8.0 < 19.0.0", 227 | "react-dom": ">= 16.8.0 < 19.0.0", 228 | "search-insights": ">= 1 < 3" 229 | }, 230 | "peerDependenciesMeta": { 231 | "@types/react": { 232 | "optional": true 233 | }, 234 | "react": { 235 | "optional": true 236 | }, 237 | "react-dom": { 238 | "optional": true 239 | }, 240 | "search-insights": { 241 | "optional": true 242 | } 243 | } 244 | }, 245 | "node_modules/@esbuild/android-arm": { 246 | "version": "0.18.20", 247 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", 248 | "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", 249 | "cpu": [ 250 | "arm" 251 | ], 252 | "dev": true, 253 | "optional": true, 254 | "os": [ 255 | "android" 256 | ], 257 | "engines": { 258 | "node": ">=12" 259 | } 260 | }, 261 | "node_modules/@esbuild/android-arm64": { 262 | "version": "0.18.20", 263 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", 264 | "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", 265 | "cpu": [ 266 | "arm64" 267 | ], 268 | "dev": true, 269 | "optional": true, 270 | "os": [ 271 | "android" 272 | ], 273 | "engines": { 274 | "node": ">=12" 275 | } 276 | }, 277 | "node_modules/@esbuild/android-x64": { 278 | "version": "0.18.20", 279 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", 280 | "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", 281 | "cpu": [ 282 | "x64" 283 | ], 284 | "dev": true, 285 | "optional": true, 286 | "os": [ 287 | "android" 288 | ], 289 | "engines": { 290 | "node": ">=12" 291 | } 292 | }, 293 | "node_modules/@esbuild/darwin-arm64": { 294 | "version": "0.18.20", 295 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", 296 | "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", 297 | "cpu": [ 298 | "arm64" 299 | ], 300 | "dev": true, 301 | "optional": true, 302 | "os": [ 303 | "darwin" 304 | ], 305 | "engines": { 306 | "node": ">=12" 307 | } 308 | }, 309 | "node_modules/@esbuild/darwin-x64": { 310 | "version": "0.18.20", 311 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", 312 | "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", 313 | "cpu": [ 314 | "x64" 315 | ], 316 | "dev": true, 317 | "optional": true, 318 | "os": [ 319 | "darwin" 320 | ], 321 | "engines": { 322 | "node": ">=12" 323 | } 324 | }, 325 | "node_modules/@esbuild/freebsd-arm64": { 326 | "version": "0.18.20", 327 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", 328 | "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", 329 | "cpu": [ 330 | "arm64" 331 | ], 332 | "dev": true, 333 | "optional": true, 334 | "os": [ 335 | "freebsd" 336 | ], 337 | "engines": { 338 | "node": ">=12" 339 | } 340 | }, 341 | "node_modules/@esbuild/freebsd-x64": { 342 | "version": "0.18.20", 343 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", 344 | "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", 345 | "cpu": [ 346 | "x64" 347 | ], 348 | "dev": true, 349 | "optional": true, 350 | "os": [ 351 | "freebsd" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/@esbuild/linux-arm": { 358 | "version": "0.18.20", 359 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", 360 | "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", 361 | "cpu": [ 362 | "arm" 363 | ], 364 | "dev": true, 365 | "optional": true, 366 | "os": [ 367 | "linux" 368 | ], 369 | "engines": { 370 | "node": ">=12" 371 | } 372 | }, 373 | "node_modules/@esbuild/linux-arm64": { 374 | "version": "0.18.20", 375 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", 376 | "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", 377 | "cpu": [ 378 | "arm64" 379 | ], 380 | "dev": true, 381 | "optional": true, 382 | "os": [ 383 | "linux" 384 | ], 385 | "engines": { 386 | "node": ">=12" 387 | } 388 | }, 389 | "node_modules/@esbuild/linux-ia32": { 390 | "version": "0.18.20", 391 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", 392 | "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", 393 | "cpu": [ 394 | "ia32" 395 | ], 396 | "dev": true, 397 | "optional": true, 398 | "os": [ 399 | "linux" 400 | ], 401 | "engines": { 402 | "node": ">=12" 403 | } 404 | }, 405 | "node_modules/@esbuild/linux-loong64": { 406 | "version": "0.18.20", 407 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", 408 | "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", 409 | "cpu": [ 410 | "loong64" 411 | ], 412 | "dev": true, 413 | "optional": true, 414 | "os": [ 415 | "linux" 416 | ], 417 | "engines": { 418 | "node": ">=12" 419 | } 420 | }, 421 | "node_modules/@esbuild/linux-mips64el": { 422 | "version": "0.18.20", 423 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", 424 | "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", 425 | "cpu": [ 426 | "mips64el" 427 | ], 428 | "dev": true, 429 | "optional": true, 430 | "os": [ 431 | "linux" 432 | ], 433 | "engines": { 434 | "node": ">=12" 435 | } 436 | }, 437 | "node_modules/@esbuild/linux-ppc64": { 438 | "version": "0.18.20", 439 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", 440 | "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", 441 | "cpu": [ 442 | "ppc64" 443 | ], 444 | "dev": true, 445 | "optional": true, 446 | "os": [ 447 | "linux" 448 | ], 449 | "engines": { 450 | "node": ">=12" 451 | } 452 | }, 453 | "node_modules/@esbuild/linux-riscv64": { 454 | "version": "0.18.20", 455 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", 456 | "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", 457 | "cpu": [ 458 | "riscv64" 459 | ], 460 | "dev": true, 461 | "optional": true, 462 | "os": [ 463 | "linux" 464 | ], 465 | "engines": { 466 | "node": ">=12" 467 | } 468 | }, 469 | "node_modules/@esbuild/linux-s390x": { 470 | "version": "0.18.20", 471 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", 472 | "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", 473 | "cpu": [ 474 | "s390x" 475 | ], 476 | "dev": true, 477 | "optional": true, 478 | "os": [ 479 | "linux" 480 | ], 481 | "engines": { 482 | "node": ">=12" 483 | } 484 | }, 485 | "node_modules/@esbuild/linux-x64": { 486 | "version": "0.18.20", 487 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", 488 | "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", 489 | "cpu": [ 490 | "x64" 491 | ], 492 | "dev": true, 493 | "optional": true, 494 | "os": [ 495 | "linux" 496 | ], 497 | "engines": { 498 | "node": ">=12" 499 | } 500 | }, 501 | "node_modules/@esbuild/netbsd-x64": { 502 | "version": "0.18.20", 503 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", 504 | "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", 505 | "cpu": [ 506 | "x64" 507 | ], 508 | "dev": true, 509 | "optional": true, 510 | "os": [ 511 | "netbsd" 512 | ], 513 | "engines": { 514 | "node": ">=12" 515 | } 516 | }, 517 | "node_modules/@esbuild/openbsd-x64": { 518 | "version": "0.18.20", 519 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", 520 | "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", 521 | "cpu": [ 522 | "x64" 523 | ], 524 | "dev": true, 525 | "optional": true, 526 | "os": [ 527 | "openbsd" 528 | ], 529 | "engines": { 530 | "node": ">=12" 531 | } 532 | }, 533 | "node_modules/@esbuild/sunos-x64": { 534 | "version": "0.18.20", 535 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", 536 | "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", 537 | "cpu": [ 538 | "x64" 539 | ], 540 | "dev": true, 541 | "optional": true, 542 | "os": [ 543 | "sunos" 544 | ], 545 | "engines": { 546 | "node": ">=12" 547 | } 548 | }, 549 | "node_modules/@esbuild/win32-arm64": { 550 | "version": "0.18.20", 551 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", 552 | "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", 553 | "cpu": [ 554 | "arm64" 555 | ], 556 | "dev": true, 557 | "optional": true, 558 | "os": [ 559 | "win32" 560 | ], 561 | "engines": { 562 | "node": ">=12" 563 | } 564 | }, 565 | "node_modules/@esbuild/win32-ia32": { 566 | "version": "0.18.20", 567 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", 568 | "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", 569 | "cpu": [ 570 | "ia32" 571 | ], 572 | "dev": true, 573 | "optional": true, 574 | "os": [ 575 | "win32" 576 | ], 577 | "engines": { 578 | "node": ">=12" 579 | } 580 | }, 581 | "node_modules/@esbuild/win32-x64": { 582 | "version": "0.18.20", 583 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", 584 | "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", 585 | "cpu": [ 586 | "x64" 587 | ], 588 | "dev": true, 589 | "optional": true, 590 | "os": [ 591 | "win32" 592 | ], 593 | "engines": { 594 | "node": ">=12" 595 | } 596 | }, 597 | "node_modules/@jridgewell/sourcemap-codec": { 598 | "version": "1.4.15", 599 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 600 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 601 | "dev": true 602 | }, 603 | "node_modules/@types/linkify-it": { 604 | "version": "3.0.4", 605 | "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.4.tgz", 606 | "integrity": "sha512-hPpIeeHb/2UuCw06kSNAOVWgehBLXEo0/fUs0mw3W2qhqX89PI2yvok83MnuctYGCPrabGIoi0fFso4DQ+sNUQ==", 607 | "dev": true 608 | }, 609 | "node_modules/@types/markdown-it": { 610 | "version": "13.0.5", 611 | "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.5.tgz", 612 | "integrity": "sha512-QhJP7hkq3FCrFNx0szMNCT/79CXfcEgUIA3jc5GBfeXqoKsk3R8JZm2wRXJ2DiyjbPE4VMFOSDemLFcUTZmHEQ==", 613 | "dev": true, 614 | "dependencies": { 615 | "@types/linkify-it": "*", 616 | "@types/mdurl": "*" 617 | } 618 | }, 619 | "node_modules/@types/mdurl": { 620 | "version": "1.0.4", 621 | "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.4.tgz", 622 | "integrity": "sha512-ARVxjAEX5TARFRzpDRVC6cEk0hUIXCCwaMhz8y7S1/PxU6zZS1UMjyobz7q4w/D/R552r4++EhwmXK1N2rAy0A==", 623 | "dev": true 624 | }, 625 | "node_modules/@types/web-bluetooth": { 626 | "version": "0.0.18", 627 | "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.18.tgz", 628 | "integrity": "sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==", 629 | "dev": true 630 | }, 631 | "node_modules/@vitejs/plugin-vue": { 632 | "version": "4.3.1", 633 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.1.tgz", 634 | "integrity": "sha512-tUBEtWcF7wFtII7ayNiLNDTCE1X1afySEo+XNVMNkFXaThENyCowIEX095QqbJZGTgoOcSVDJGlnde2NG4jtbQ==", 635 | "dev": true, 636 | "engines": { 637 | "node": "^14.18.0 || >=16.0.0" 638 | }, 639 | "peerDependencies": { 640 | "vite": "^4.0.0", 641 | "vue": "^3.2.25" 642 | } 643 | }, 644 | "node_modules/@vue/compiler-core": { 645 | "version": "3.3.6", 646 | "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.6.tgz", 647 | "integrity": "sha512-2JNjemwaNwf+MkkatATVZi7oAH1Hx0B04DdPH3ZoZ8vKC1xZVP7nl4HIsk8XYd3r+/52sqqoz9TWzYc3yE9dqA==", 648 | "dev": true, 649 | "dependencies": { 650 | "@babel/parser": "^7.23.0", 651 | "@vue/shared": "3.3.6", 652 | "estree-walker": "^2.0.2", 653 | "source-map-js": "^1.0.2" 654 | } 655 | }, 656 | "node_modules/@vue/compiler-dom": { 657 | "version": "3.3.6", 658 | "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.6.tgz", 659 | "integrity": "sha512-1MxXcJYMHiTPexjLAJUkNs/Tw2eDf2tY3a0rL+LfuWyiKN2s6jvSwywH3PWD8bKICjfebX3GWx2Os8jkRDq3Ng==", 660 | "dev": true, 661 | "dependencies": { 662 | "@vue/compiler-core": "3.3.6", 663 | "@vue/shared": "3.3.6" 664 | } 665 | }, 666 | "node_modules/@vue/compiler-sfc": { 667 | "version": "3.3.6", 668 | "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.6.tgz", 669 | "integrity": "sha512-/Kms6du2h1VrXFreuZmlvQej8B1zenBqIohP0690IUBkJjsFvJxY0crcvVRJ0UhMgSR9dewB+khdR1DfbpArJA==", 670 | "dev": true, 671 | "dependencies": { 672 | "@babel/parser": "^7.23.0", 673 | "@vue/compiler-core": "3.3.6", 674 | "@vue/compiler-dom": "3.3.6", 675 | "@vue/compiler-ssr": "3.3.6", 676 | "@vue/reactivity-transform": "3.3.6", 677 | "@vue/shared": "3.3.6", 678 | "estree-walker": "^2.0.2", 679 | "magic-string": "^0.30.5", 680 | "postcss": "^8.4.31", 681 | "source-map-js": "^1.0.2" 682 | } 683 | }, 684 | "node_modules/@vue/compiler-ssr": { 685 | "version": "3.3.6", 686 | "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.6.tgz", 687 | "integrity": "sha512-QTIHAfDCHhjXlYGkUg5KH7YwYtdUM1vcFl/FxFDlD6d0nXAmnjizka3HITp8DGudzHndv2PjKVS44vqqy0vP4w==", 688 | "dev": true, 689 | "dependencies": { 690 | "@vue/compiler-dom": "3.3.6", 691 | "@vue/shared": "3.3.6" 692 | } 693 | }, 694 | "node_modules/@vue/devtools-api": { 695 | "version": "6.5.1", 696 | "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", 697 | "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==", 698 | "dev": true 699 | }, 700 | "node_modules/@vue/reactivity": { 701 | "version": "3.3.6", 702 | "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.6.tgz", 703 | "integrity": "sha512-gtChAumfQz5lSy5jZXfyXbKrIYPf9XEOrIr6rxwVyeWVjFhJwmwPLtV6Yis+M9onzX++I5AVE9j+iPH60U+B8Q==", 704 | "dev": true, 705 | "dependencies": { 706 | "@vue/shared": "3.3.6" 707 | } 708 | }, 709 | "node_modules/@vue/reactivity-transform": { 710 | "version": "3.3.6", 711 | "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.6.tgz", 712 | "integrity": "sha512-RlJl4dHfeO7EuzU1iJOsrlqWyJfHTkJbvYz/IOJWqu8dlCNWtxWX377WI0VsbAgBizjwD+3ZjdnvSyyFW1YVng==", 713 | "dev": true, 714 | "dependencies": { 715 | "@babel/parser": "^7.23.0", 716 | "@vue/compiler-core": "3.3.6", 717 | "@vue/shared": "3.3.6", 718 | "estree-walker": "^2.0.2", 719 | "magic-string": "^0.30.5" 720 | } 721 | }, 722 | "node_modules/@vue/runtime-core": { 723 | "version": "3.3.6", 724 | "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.6.tgz", 725 | "integrity": "sha512-qp7HTP1iw1UW2ZGJ8L3zpqlngrBKvLsDAcq5lA6JvEXHmpoEmjKju7ahM9W2p/h51h0OT5F2fGlP/gMhHOmbUA==", 726 | "dev": true, 727 | "dependencies": { 728 | "@vue/reactivity": "3.3.6", 729 | "@vue/shared": "3.3.6" 730 | } 731 | }, 732 | "node_modules/@vue/runtime-dom": { 733 | "version": "3.3.6", 734 | "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.6.tgz", 735 | "integrity": "sha512-AoX3Cp8NqMXjLbIG9YR6n/pPLWE9TiDdk6wTJHFnl2GpHzDFH1HLBC9wlqqQ7RlnvN3bVLpzPGAAH00SAtOxHg==", 736 | "dev": true, 737 | "dependencies": { 738 | "@vue/runtime-core": "3.3.6", 739 | "@vue/shared": "3.3.6", 740 | "csstype": "^3.1.2" 741 | } 742 | }, 743 | "node_modules/@vue/server-renderer": { 744 | "version": "3.3.6", 745 | "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.6.tgz", 746 | "integrity": "sha512-kgLoN43W4ERdZ6dpyy+gnk2ZHtcOaIr5Uc/WUP5DRwutgvluzu2pudsZGoD2b7AEJHByUVMa9k6Sho5lLRCykw==", 747 | "dev": true, 748 | "dependencies": { 749 | "@vue/compiler-ssr": "3.3.6", 750 | "@vue/shared": "3.3.6" 751 | }, 752 | "peerDependencies": { 753 | "vue": "3.3.6" 754 | } 755 | }, 756 | "node_modules/@vue/shared": { 757 | "version": "3.3.6", 758 | "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.6.tgz", 759 | "integrity": "sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==", 760 | "dev": true 761 | }, 762 | "node_modules/@vueuse/core": { 763 | "version": "10.5.0", 764 | "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.5.0.tgz", 765 | "integrity": "sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==", 766 | "dev": true, 767 | "dependencies": { 768 | "@types/web-bluetooth": "^0.0.18", 769 | "@vueuse/metadata": "10.5.0", 770 | "@vueuse/shared": "10.5.0", 771 | "vue-demi": ">=0.14.6" 772 | }, 773 | "funding": { 774 | "url": "https://github.com/sponsors/antfu" 775 | } 776 | }, 777 | "node_modules/@vueuse/core/node_modules/vue-demi": { 778 | "version": "0.14.6", 779 | "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", 780 | "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", 781 | "dev": true, 782 | "hasInstallScript": true, 783 | "bin": { 784 | "vue-demi-fix": "bin/vue-demi-fix.js", 785 | "vue-demi-switch": "bin/vue-demi-switch.js" 786 | }, 787 | "engines": { 788 | "node": ">=12" 789 | }, 790 | "funding": { 791 | "url": "https://github.com/sponsors/antfu" 792 | }, 793 | "peerDependencies": { 794 | "@vue/composition-api": "^1.0.0-rc.1", 795 | "vue": "^3.0.0-0 || ^2.6.0" 796 | }, 797 | "peerDependenciesMeta": { 798 | "@vue/composition-api": { 799 | "optional": true 800 | } 801 | } 802 | }, 803 | "node_modules/@vueuse/integrations": { 804 | "version": "10.5.0", 805 | "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.5.0.tgz", 806 | "integrity": "sha512-fm5sXLCK0Ww3rRnzqnCQRmfjDURaI4xMsx+T+cec0ngQqHx/JgUtm8G0vRjwtonIeTBsH1Q8L3SucE+7K7upJQ==", 807 | "dev": true, 808 | "dependencies": { 809 | "@vueuse/core": "10.5.0", 810 | "@vueuse/shared": "10.5.0", 811 | "vue-demi": ">=0.14.6" 812 | }, 813 | "funding": { 814 | "url": "https://github.com/sponsors/antfu" 815 | }, 816 | "peerDependencies": { 817 | "async-validator": "*", 818 | "axios": "*", 819 | "change-case": "*", 820 | "drauu": "*", 821 | "focus-trap": "*", 822 | "fuse.js": "*", 823 | "idb-keyval": "*", 824 | "jwt-decode": "*", 825 | "nprogress": "*", 826 | "qrcode": "*", 827 | "sortablejs": "*", 828 | "universal-cookie": "*" 829 | }, 830 | "peerDependenciesMeta": { 831 | "async-validator": { 832 | "optional": true 833 | }, 834 | "axios": { 835 | "optional": true 836 | }, 837 | "change-case": { 838 | "optional": true 839 | }, 840 | "drauu": { 841 | "optional": true 842 | }, 843 | "focus-trap": { 844 | "optional": true 845 | }, 846 | "fuse.js": { 847 | "optional": true 848 | }, 849 | "idb-keyval": { 850 | "optional": true 851 | }, 852 | "jwt-decode": { 853 | "optional": true 854 | }, 855 | "nprogress": { 856 | "optional": true 857 | }, 858 | "qrcode": { 859 | "optional": true 860 | }, 861 | "sortablejs": { 862 | "optional": true 863 | }, 864 | "universal-cookie": { 865 | "optional": true 866 | } 867 | } 868 | }, 869 | "node_modules/@vueuse/integrations/node_modules/vue-demi": { 870 | "version": "0.14.6", 871 | "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", 872 | "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", 873 | "dev": true, 874 | "hasInstallScript": true, 875 | "bin": { 876 | "vue-demi-fix": "bin/vue-demi-fix.js", 877 | "vue-demi-switch": "bin/vue-demi-switch.js" 878 | }, 879 | "engines": { 880 | "node": ">=12" 881 | }, 882 | "funding": { 883 | "url": "https://github.com/sponsors/antfu" 884 | }, 885 | "peerDependencies": { 886 | "@vue/composition-api": "^1.0.0-rc.1", 887 | "vue": "^3.0.0-0 || ^2.6.0" 888 | }, 889 | "peerDependenciesMeta": { 890 | "@vue/composition-api": { 891 | "optional": true 892 | } 893 | } 894 | }, 895 | "node_modules/@vueuse/metadata": { 896 | "version": "10.5.0", 897 | "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.5.0.tgz", 898 | "integrity": "sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==", 899 | "dev": true, 900 | "funding": { 901 | "url": "https://github.com/sponsors/antfu" 902 | } 903 | }, 904 | "node_modules/@vueuse/shared": { 905 | "version": "10.5.0", 906 | "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.5.0.tgz", 907 | "integrity": "sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==", 908 | "dev": true, 909 | "dependencies": { 910 | "vue-demi": ">=0.14.6" 911 | }, 912 | "funding": { 913 | "url": "https://github.com/sponsors/antfu" 914 | } 915 | }, 916 | "node_modules/@vueuse/shared/node_modules/vue-demi": { 917 | "version": "0.14.6", 918 | "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", 919 | "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", 920 | "dev": true, 921 | "hasInstallScript": true, 922 | "bin": { 923 | "vue-demi-fix": "bin/vue-demi-fix.js", 924 | "vue-demi-switch": "bin/vue-demi-switch.js" 925 | }, 926 | "engines": { 927 | "node": ">=12" 928 | }, 929 | "funding": { 930 | "url": "https://github.com/sponsors/antfu" 931 | }, 932 | "peerDependencies": { 933 | "@vue/composition-api": "^1.0.0-rc.1", 934 | "vue": "^3.0.0-0 || ^2.6.0" 935 | }, 936 | "peerDependenciesMeta": { 937 | "@vue/composition-api": { 938 | "optional": true 939 | } 940 | } 941 | }, 942 | "node_modules/algoliasearch": { 943 | "version": "4.20.0", 944 | "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.20.0.tgz", 945 | "integrity": "sha512-y+UHEjnOItoNy0bYO+WWmLWBlPwDjKHW6mNHrPi0NkuhpQOOEbrkwQH/wgKFDLh7qlKjzoKeiRtlpewDPDG23g==", 946 | "dev": true, 947 | "dependencies": { 948 | "@algolia/cache-browser-local-storage": "4.20.0", 949 | "@algolia/cache-common": "4.20.0", 950 | "@algolia/cache-in-memory": "4.20.0", 951 | "@algolia/client-account": "4.20.0", 952 | "@algolia/client-analytics": "4.20.0", 953 | "@algolia/client-common": "4.20.0", 954 | "@algolia/client-personalization": "4.20.0", 955 | "@algolia/client-search": "4.20.0", 956 | "@algolia/logger-common": "4.20.0", 957 | "@algolia/logger-console": "4.20.0", 958 | "@algolia/requester-browser-xhr": "4.20.0", 959 | "@algolia/requester-common": "4.20.0", 960 | "@algolia/requester-node-http": "4.20.0", 961 | "@algolia/transporter": "4.20.0" 962 | } 963 | }, 964 | "node_modules/ansi-sequence-parser": { 965 | "version": "1.1.1", 966 | "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", 967 | "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", 968 | "dev": true 969 | }, 970 | "node_modules/csstype": { 971 | "version": "3.1.2", 972 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", 973 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", 974 | "dev": true 975 | }, 976 | "node_modules/esbuild": { 977 | "version": "0.18.20", 978 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", 979 | "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", 980 | "dev": true, 981 | "hasInstallScript": true, 982 | "bin": { 983 | "esbuild": "bin/esbuild" 984 | }, 985 | "engines": { 986 | "node": ">=12" 987 | }, 988 | "optionalDependencies": { 989 | "@esbuild/android-arm": "0.18.20", 990 | "@esbuild/android-arm64": "0.18.20", 991 | "@esbuild/android-x64": "0.18.20", 992 | "@esbuild/darwin-arm64": "0.18.20", 993 | "@esbuild/darwin-x64": "0.18.20", 994 | "@esbuild/freebsd-arm64": "0.18.20", 995 | "@esbuild/freebsd-x64": "0.18.20", 996 | "@esbuild/linux-arm": "0.18.20", 997 | "@esbuild/linux-arm64": "0.18.20", 998 | "@esbuild/linux-ia32": "0.18.20", 999 | "@esbuild/linux-loong64": "0.18.20", 1000 | "@esbuild/linux-mips64el": "0.18.20", 1001 | "@esbuild/linux-ppc64": "0.18.20", 1002 | "@esbuild/linux-riscv64": "0.18.20", 1003 | "@esbuild/linux-s390x": "0.18.20", 1004 | "@esbuild/linux-x64": "0.18.20", 1005 | "@esbuild/netbsd-x64": "0.18.20", 1006 | "@esbuild/openbsd-x64": "0.18.20", 1007 | "@esbuild/sunos-x64": "0.18.20", 1008 | "@esbuild/win32-arm64": "0.18.20", 1009 | "@esbuild/win32-ia32": "0.18.20", 1010 | "@esbuild/win32-x64": "0.18.20" 1011 | } 1012 | }, 1013 | "node_modules/estree-walker": { 1014 | "version": "2.0.2", 1015 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 1016 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 1017 | "dev": true 1018 | }, 1019 | "node_modules/focus-trap": { 1020 | "version": "7.5.4", 1021 | "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", 1022 | "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", 1023 | "dev": true, 1024 | "dependencies": { 1025 | "tabbable": "^6.2.0" 1026 | } 1027 | }, 1028 | "node_modules/fsevents": { 1029 | "version": "2.3.3", 1030 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1031 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1032 | "dev": true, 1033 | "hasInstallScript": true, 1034 | "optional": true, 1035 | "os": [ 1036 | "darwin" 1037 | ], 1038 | "engines": { 1039 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1040 | } 1041 | }, 1042 | "node_modules/jsonc-parser": { 1043 | "version": "3.2.0", 1044 | "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", 1045 | "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", 1046 | "dev": true 1047 | }, 1048 | "node_modules/magic-string": { 1049 | "version": "0.30.5", 1050 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", 1051 | "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", 1052 | "dev": true, 1053 | "dependencies": { 1054 | "@jridgewell/sourcemap-codec": "^1.4.15" 1055 | }, 1056 | "engines": { 1057 | "node": ">=12" 1058 | } 1059 | }, 1060 | "node_modules/mark.js": { 1061 | "version": "8.11.1", 1062 | "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", 1063 | "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", 1064 | "dev": true 1065 | }, 1066 | "node_modules/minisearch": { 1067 | "version": "6.1.0", 1068 | "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.1.0.tgz", 1069 | "integrity": "sha512-PNxA/X8pWk+TiqPbsoIYH0GQ5Di7m6326/lwU/S4mlo4wGQddIcf/V//1f9TB0V4j59b57b+HZxt8h3iMROGvg==", 1070 | "dev": true 1071 | }, 1072 | "node_modules/nanoid": { 1073 | "version": "3.3.6", 1074 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 1075 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 1076 | "dev": true, 1077 | "funding": [ 1078 | { 1079 | "type": "github", 1080 | "url": "https://github.com/sponsors/ai" 1081 | } 1082 | ], 1083 | "bin": { 1084 | "nanoid": "bin/nanoid.cjs" 1085 | }, 1086 | "engines": { 1087 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1088 | } 1089 | }, 1090 | "node_modules/picocolors": { 1091 | "version": "1.0.0", 1092 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1093 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1094 | "dev": true 1095 | }, 1096 | "node_modules/postcss": { 1097 | "version": "8.4.31", 1098 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 1099 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 1100 | "dev": true, 1101 | "funding": [ 1102 | { 1103 | "type": "opencollective", 1104 | "url": "https://opencollective.com/postcss/" 1105 | }, 1106 | { 1107 | "type": "tidelift", 1108 | "url": "https://tidelift.com/funding/github/npm/postcss" 1109 | }, 1110 | { 1111 | "type": "github", 1112 | "url": "https://github.com/sponsors/ai" 1113 | } 1114 | ], 1115 | "dependencies": { 1116 | "nanoid": "^3.3.6", 1117 | "picocolors": "^1.0.0", 1118 | "source-map-js": "^1.0.2" 1119 | }, 1120 | "engines": { 1121 | "node": "^10 || ^12 || >=14" 1122 | } 1123 | }, 1124 | "node_modules/preact": { 1125 | "version": "10.18.1", 1126 | "resolved": "https://registry.npmjs.org/preact/-/preact-10.18.1.tgz", 1127 | "integrity": "sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==", 1128 | "dev": true, 1129 | "funding": { 1130 | "type": "opencollective", 1131 | "url": "https://opencollective.com/preact" 1132 | } 1133 | }, 1134 | "node_modules/rollup": { 1135 | "version": "3.29.4", 1136 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", 1137 | "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", 1138 | "dev": true, 1139 | "bin": { 1140 | "rollup": "dist/bin/rollup" 1141 | }, 1142 | "engines": { 1143 | "node": ">=14.18.0", 1144 | "npm": ">=8.0.0" 1145 | }, 1146 | "optionalDependencies": { 1147 | "fsevents": "~2.3.2" 1148 | } 1149 | }, 1150 | "node_modules/search-insights": { 1151 | "version": "2.9.0", 1152 | "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.9.0.tgz", 1153 | "integrity": "sha512-bkWW9nIHOFkLwjQ1xqVaMbjjO5vhP26ERsH9Y3pKr8imthofEFIxlnOabkmGcw6ksRj9jWidcI65vvjJH/nTGg==", 1154 | "dev": true, 1155 | "peer": true 1156 | }, 1157 | "node_modules/shiki": { 1158 | "version": "0.14.5", 1159 | "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", 1160 | "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", 1161 | "dev": true, 1162 | "dependencies": { 1163 | "ansi-sequence-parser": "^1.1.0", 1164 | "jsonc-parser": "^3.2.0", 1165 | "vscode-oniguruma": "^1.7.0", 1166 | "vscode-textmate": "^8.0.0" 1167 | } 1168 | }, 1169 | "node_modules/source-map-js": { 1170 | "version": "1.0.2", 1171 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1172 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1173 | "dev": true, 1174 | "engines": { 1175 | "node": ">=0.10.0" 1176 | } 1177 | }, 1178 | "node_modules/tabbable": { 1179 | "version": "6.2.0", 1180 | "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", 1181 | "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", 1182 | "dev": true 1183 | }, 1184 | "node_modules/vite": { 1185 | "version": "4.5.0", 1186 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", 1187 | "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", 1188 | "dev": true, 1189 | "dependencies": { 1190 | "esbuild": "^0.18.10", 1191 | "postcss": "^8.4.27", 1192 | "rollup": "^3.27.1" 1193 | }, 1194 | "bin": { 1195 | "vite": "bin/vite.js" 1196 | }, 1197 | "engines": { 1198 | "node": "^14.18.0 || >=16.0.0" 1199 | }, 1200 | "funding": { 1201 | "url": "https://github.com/vitejs/vite?sponsor=1" 1202 | }, 1203 | "optionalDependencies": { 1204 | "fsevents": "~2.3.2" 1205 | }, 1206 | "peerDependencies": { 1207 | "@types/node": ">= 14", 1208 | "less": "*", 1209 | "lightningcss": "^1.21.0", 1210 | "sass": "*", 1211 | "stylus": "*", 1212 | "sugarss": "*", 1213 | "terser": "^5.4.0" 1214 | }, 1215 | "peerDependenciesMeta": { 1216 | "@types/node": { 1217 | "optional": true 1218 | }, 1219 | "less": { 1220 | "optional": true 1221 | }, 1222 | "lightningcss": { 1223 | "optional": true 1224 | }, 1225 | "sass": { 1226 | "optional": true 1227 | }, 1228 | "stylus": { 1229 | "optional": true 1230 | }, 1231 | "sugarss": { 1232 | "optional": true 1233 | }, 1234 | "terser": { 1235 | "optional": true 1236 | } 1237 | } 1238 | }, 1239 | "node_modules/vitepress": { 1240 | "version": "1.0.0-rc.24", 1241 | "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.24.tgz", 1242 | "integrity": "sha512-RpnL8cnOGwiRlBbrYQUm9sYkJbtyOt/wYXk2diTcokY4yvks/5lq9LuSt+MURWB6ZqwpSNHvTmxgaSfLoG0/OA==", 1243 | "dev": true, 1244 | "dependencies": { 1245 | "@docsearch/css": "^3.5.2", 1246 | "@docsearch/js": "^3.5.2", 1247 | "@types/markdown-it": "^13.0.4", 1248 | "@vitejs/plugin-vue": "4.3.1", 1249 | "@vue/devtools-api": "^6.5.1", 1250 | "@vueuse/core": "^10.5.0", 1251 | "@vueuse/integrations": "^10.5.0", 1252 | "focus-trap": "^7.5.4", 1253 | "mark.js": "8.11.1", 1254 | "minisearch": "^6.1.0", 1255 | "shiki": "^0.14.5", 1256 | "vite": "^4.5.0", 1257 | "vue": "^3.3.6" 1258 | }, 1259 | "bin": { 1260 | "vitepress": "bin/vitepress.js" 1261 | }, 1262 | "peerDependencies": { 1263 | "markdown-it-mathjax3": "^4.3.2", 1264 | "postcss": "^8.4.31" 1265 | }, 1266 | "peerDependenciesMeta": { 1267 | "markdown-it-mathjax3": { 1268 | "optional": true 1269 | }, 1270 | "postcss": { 1271 | "optional": true 1272 | } 1273 | } 1274 | }, 1275 | "node_modules/vscode-oniguruma": { 1276 | "version": "1.7.0", 1277 | "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", 1278 | "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", 1279 | "dev": true 1280 | }, 1281 | "node_modules/vscode-textmate": { 1282 | "version": "8.0.0", 1283 | "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", 1284 | "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", 1285 | "dev": true 1286 | }, 1287 | "node_modules/vue": { 1288 | "version": "3.3.6", 1289 | "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.6.tgz", 1290 | "integrity": "sha512-jJIDETeWJnoY+gfn4ZtMPMS5KtbP4ax+CT4dcQFhTnWEk8xMupFyQ0JxL28nvT/M4+p4a0ptxaV2WY0LiIxvRg==", 1291 | "dev": true, 1292 | "dependencies": { 1293 | "@vue/compiler-dom": "3.3.6", 1294 | "@vue/compiler-sfc": "3.3.6", 1295 | "@vue/runtime-dom": "3.3.6", 1296 | "@vue/server-renderer": "3.3.6", 1297 | "@vue/shared": "3.3.6" 1298 | }, 1299 | "peerDependencies": { 1300 | "typescript": "*" 1301 | }, 1302 | "peerDependenciesMeta": { 1303 | "typescript": { 1304 | "optional": true 1305 | } 1306 | } 1307 | } 1308 | } 1309 | } 1310 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "vitepress": "^1.0.0-rc.13" 4 | }, 5 | "scripts": { 6 | "docs:dev": "vitepress dev docs", 7 | "docs:build": "vitepress build docs", 8 | "docs:preview": "vitepress preview docs" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Agent.php: -------------------------------------------------------------------------------- 1 | {$method}(...$parameters); 47 | } catch (\Throwable $th) { 48 | throw AgentException::create("The method '{$method}()' does not exist."); 49 | } 50 | } 51 | 52 | /** 53 | * Get a UserAgent instance for the current request. 54 | * 55 | * @return UserAgent The UserAgent instance. 56 | */ 57 | public static function detect() 58 | { 59 | return new UserAgent(); 60 | } 61 | 62 | /** 63 | * Get the raw user agent string from the current request. 64 | * 65 | * @return string|null The user agent string. 66 | */ 67 | public static function get() 68 | { 69 | return $_SERVER['HTTP_USER_AGENT'] ?? null; 70 | } 71 | 72 | /** 73 | * Set a custom user agent string for testing or manipulation. 74 | * 75 | * @param string $string The custom user agent string. 76 | * 77 | * @return UserAgent The UserAgent instance with the custom string. 78 | */ 79 | public static function set(string $string) 80 | { 81 | return new UserAgent($string); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Designators/Identifiers.php: -------------------------------------------------------------------------------- 1 | 'Windows', 72 | '/\bMacintosh\b|Mac(?!.+OS X)/i' => 'Mac', 73 | '/\bLinux\b/i' => 'Linux', 74 | '/\bAndroid\b/i' => 'Android', 75 | '/\biPhone\b|\biPad\b|\biPod\b/i' => 'iOS', 76 | ]; 77 | 78 | // Browser mapping 79 | const BROWSERS = [ 80 | 'Internet Explorer' => [ 81 | 'MSIE', 82 | 'IEMobile', 83 | 'MSIEMobile', 84 | ], 85 | 'Netscape' => [ 86 | 'Netscape', 87 | ], 88 | 'Opera' => [ 89 | 'Opera', 90 | 'Opera Mini', 91 | ], 92 | 'Microsoft Edge' => [ 93 | 'Edg', 94 | 'Edge', 95 | ], 96 | 'Coc Coc' => [ 97 | 'coc_coc_browser', 98 | ], 99 | 'Vivaldi' => [ 100 | 'Vivaldi', 101 | ], 102 | 'UCBrowser' => [ 103 | 'UCBrowser', 104 | ], 105 | 'Google Chrome' => [ 106 | 'Chrome', 107 | ], 108 | 'Mozilla Firefox' => [ 109 | 'Firefox', 110 | ], 111 | 'WeChat' => [ 112 | 'MicroMessenger', 113 | ], 114 | 'Safari' => [ 115 | 'Safari', 116 | 'Apple Safari', 117 | ], 118 | ]; 119 | 120 | // Platform mapping based on regular expressions 121 | const PLATFORMS = [ 122 | 'Linux' => '/linux/i', 123 | 'Macintosh' => '/macintosh|mac os x/i', 124 | 'Windows' => '/windows|win32/i', 125 | ]; 126 | } 127 | -------------------------------------------------------------------------------- /src/Exceptions/AgentException.php: -------------------------------------------------------------------------------- 1 | detect()->isMobile()): ?>"; 31 | }); 32 | 33 | Blade::directive('endAgentMobile', function () { 34 | return ''; 35 | }); 36 | 37 | /** 38 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is a desktop device. 39 | */ 40 | Blade::directive('agentDesktop', function () { 41 | return "detect()->isDesktop()): ?>"; 42 | }); 43 | 44 | Blade::directive('endAgentDesktop', function () { 45 | return ''; 46 | }); 47 | 48 | /** 49 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an iPhone. 50 | */ 51 | Blade::directive('agentIPhone', function () { 52 | return "detect()->isIPhone()): ?>"; 53 | }); 54 | 55 | Blade::directive('endAgentIPhone', function () { 56 | return ''; 57 | }); 58 | 59 | /** 60 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is a Macintosh device. 61 | */ 62 | Blade::directive('agentMacintosh', function () { 63 | return "detect()->isMacintosh()): ?>"; 64 | }); 65 | 66 | Blade::directive('endAgentMacintosh', function () { 67 | return ''; 68 | }); 69 | 70 | /** 71 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an iMac device. 72 | */ 73 | Blade::directive('agentIMac', function () { 74 | return "detect()->isIMac()): ?>"; 75 | }); 76 | 77 | Blade::directive('endAgentIMac', function () { 78 | return ''; 79 | }); 80 | 81 | /** 82 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an iPod device. 83 | */ 84 | Blade::directive('agentIpod', function () { 85 | return "detect()->isIpod()): ?>"; 86 | }); 87 | 88 | Blade::directive('endAgentIpod', function () { 89 | return ''; 90 | }); 91 | 92 | /** 93 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an iPad device. 94 | */ 95 | Blade::directive('agentIpad', function () { 96 | return "detect()->isIpad()): ?>"; 97 | }); 98 | 99 | Blade::directive('endAgentIpad', function () { 100 | return ''; 101 | }); 102 | 103 | /** 104 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is a Linux-based device. 105 | */ 106 | Blade::directive('agentLinux', function () { 107 | return "detect()->isLinux()): ?>"; 108 | }); 109 | 110 | Blade::directive('endAgentLinux', function () { 111 | return ''; 112 | }); 113 | 114 | /** 115 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an Android device. 116 | */ 117 | Blade::directive('agentAndroid', function () { 118 | return "detect()->isAndroid()): ?>"; 119 | }); 120 | 121 | Blade::directive('endAgentAndroid', function () { 122 | return ''; 123 | }); 124 | 125 | /** 126 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is a Windows-based device. 127 | */ 128 | Blade::directive('agentWindows', function () { 129 | return "detect()->isWindows()): ?>"; 130 | }); 131 | 132 | Blade::directive('endAgentWindows', function () { 133 | return ''; 134 | }); 135 | 136 | /** 137 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is a Windows Phone device. 138 | */ 139 | Blade::directive('agentWindowsPhone', function () { 140 | return "detect()->isWindowsPhone()): ?>"; 141 | }); 142 | 143 | Blade::directive('endAgentWindowsPhone', function () { 144 | return ''; 145 | }); 146 | 147 | /** 148 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an Tabñet. 149 | */ 150 | Blade::directive('agentTablet', function () { 151 | return "detect()->isTablet()): ?>"; 152 | }); 153 | 154 | Blade::directive('endAgentTablet', function () { 155 | return ''; 156 | }); 157 | 158 | /** 159 | * This Blade directive starts a block that will be conditionally rendered if the detected user agent is an Robot1. 160 | */ 161 | Blade::directive('agentCrawler', function () { 162 | return "detect()->isCrawler()): ?>"; 163 | }); 164 | 165 | Blade::directive('endAgentCrawler', function () { 166 | return ''; 167 | }); 168 | 169 | /** 170 | * This Blade directive starts a block that will be conditionally rendered if the supplied IP matches 171 | * the remote address obtained from Agent::detect()->remoteAddress(). 172 | */ 173 | Blade::directive('agentMatch', function ($expression = null) { 174 | return "detect()->match($expression)): ?>"; 175 | }); 176 | 177 | Blade::directive('endAgentMatch', function () { 178 | return ''; 179 | }); 180 | 181 | /** 182 | * This Blade directive starts a block that will be conditionally rendered if the supplied IP matches the remote address obtained from Agent::detect()->remoteAddress(). 183 | */ 184 | Blade::directive('agentRemoteIp', function ($expression) { 185 | return "detect()->remoteAddress()): ?>"; 186 | }); 187 | 188 | Blade::directive('endAgentRemoteIp', function () { 189 | return ''; 190 | }); 191 | 192 | /** 193 | * This Blade directive starts a block that will be conditionally rendered if the supplied port matches the remote port obtained from Agent::detect()->remotePort(). 194 | */ 195 | Blade::directive('agentRemotePort', function ($expression) { 196 | return "detect()->remotePort()): ?>"; 197 | }); 198 | 199 | Blade::directive('endAgentRemotePort', function () { 200 | return ''; 201 | }); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/Response/Browser.php: -------------------------------------------------------------------------------- 1 | name = $data['name'] ?? null; 30 | $this->version = $data['version'] ?? null; 31 | $this->platform = $data['platform'] ?? null; 32 | } 33 | 34 | /** 35 | * Get the name of the browser. 36 | * 37 | * @return string The browser name. 38 | */ 39 | public function getName() 40 | { 41 | return $this->name; 42 | } 43 | 44 | /** 45 | * Get the version of the browser. 46 | * 47 | * @return string The browser version. 48 | */ 49 | public function getVersion() 50 | { 51 | return $this->version; 52 | } 53 | 54 | /** 55 | * Get the platform on which the browser is running. 56 | * 57 | * @return string The browser platform. 58 | */ 59 | public function getPlatform() 60 | { 61 | return $this->platform; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Server/UserAgent.php: -------------------------------------------------------------------------------- 1 | agent = !empty($agent) ? $agent : $_SERVER['HTTP_USER_AGENT']; 26 | } 27 | 28 | /** 29 | * Check if the user agent represents a mobile device. 30 | * 31 | * @return bool True if the user agent is from a mobile device, false otherwise. 32 | */ 33 | public function isMobile() 34 | { 35 | return Utilities::inString($this->agent, Identifiers::MOBILES); 36 | } 37 | 38 | /** 39 | * Check if the user agent represents a desktop device. 40 | * 41 | * @return bool True if the user agent is from a desktop device, false otherwise. 42 | */ 43 | public function isDesktop() 44 | { 45 | return !$this->isMobile(); 46 | } 47 | 48 | /** 49 | * Check if the user agent represents an iPhone. 50 | * 51 | * @return bool True if the user agent is from an iPhone, false otherwise. 52 | */ 53 | public function isIPhone() 54 | { 55 | return Utilities::inString($this->agent, Identifiers::IPHONE); 56 | } 57 | 58 | /** 59 | * Check if the user agent represents a Macintosh. 60 | * 61 | * @return bool True if the user agent is from a Macintosh, false otherwise. 62 | */ 63 | public function isMacintosh() 64 | { 65 | return Utilities::inString($this->agent, Identifiers::MAC); 66 | } 67 | 68 | /** 69 | * Check if the user agent represents an iMac. 70 | * 71 | * @return bool True if the user agent is from an iMac, false otherwise. 72 | */ 73 | public function isIMac() 74 | { 75 | return Utilities::inString($this->agent, Identifiers::IMAC); 76 | } 77 | 78 | /** 79 | * Check if the user agent represents an iPod. 80 | * 81 | * @return bool True if the user agent is from an iPod, false otherwise. 82 | */ 83 | public function isIpod() 84 | { 85 | return Utilities::inString($this->agent, Identifiers::IPOD); 86 | } 87 | 88 | /** 89 | * Check if the user agent represents an iPad. 90 | * 91 | * @return bool True if the user agent is from an iPad, false otherwise. 92 | */ 93 | public function isIpad() 94 | { 95 | return Utilities::inString($this->agent, Identifiers::IPAD); 96 | } 97 | 98 | /** 99 | * Check if the user agent represents a Linux system. 100 | * 101 | * @return bool True if the user agent is from a Linux system, false otherwise. 102 | */ 103 | public function isLinux() 104 | { 105 | return Utilities::inString($this->agent, Identifiers::LINUX); 106 | } 107 | 108 | /** 109 | * Check if the user agent represents an Android device. 110 | * 111 | * @return bool True if the user agent is from an Android device, false otherwise. 112 | */ 113 | public function isAndroid() 114 | { 115 | return Utilities::inString($this->agent, Identifiers::ANDROID); 116 | } 117 | 118 | /** 119 | * Check if the user agent represents a Windows system. 120 | * 121 | * @return bool True if the user agent is from a Windows system, false otherwise. 122 | */ 123 | public function isWindows() 124 | { 125 | return Utilities::inString($this->agent, Identifiers::WINDOWS); 126 | } 127 | 128 | /** 129 | * Check if the user agent represents a Windows Phone device. 130 | * 131 | * @return bool True if the user agent is from a Windows Phone device, false otherwise. 132 | */ 133 | public function isWindowsPhone() 134 | { 135 | return Utilities::inString($this->agent, Identifiers::WINDOWS_PHONE); 136 | } 137 | 138 | /** 139 | * Check if the user agent represents a tablet device. 140 | * 141 | * @return bool True if the user agent is from a tablet device, false otherwise. 142 | */ 143 | public function isTablet() 144 | { 145 | $mobileDetect = new MobileDetect(null, $this->agent); 146 | 147 | return $mobileDetect->isTablet(); 148 | } 149 | 150 | /** 151 | * Check if the user agent is identified as a crawler. 152 | * 153 | * @return bool True if the user agent is identified as a crawler, false otherwise. 154 | */ 155 | public function isCrawler() 156 | { 157 | return (new CrawlerDetect())->isCrawler($this->agent); 158 | } 159 | 160 | /** 161 | * Get the matches from the crawler detection. 162 | * 163 | * @return array An array containing matches from the crawler detection. 164 | */ 165 | public function getCrawler() 166 | { 167 | $CrawlerDetect = new CrawlerDetect(); 168 | $CrawlerDetect->isCrawler($this->agent); 169 | 170 | return $CrawlerDetect->getMatches(); 171 | } 172 | 173 | /** 174 | * Check if the user agent matches a given regex pattern. 175 | * 176 | * @param string $regex The regex pattern to match. 177 | * 178 | * @return bool True if the user agent matches the regex pattern, false otherwise. 179 | */ 180 | public function match(string $regex) 181 | { 182 | return stripos($this->agent, $regex) !== false; 183 | } 184 | 185 | /** 186 | * Get the client's operating system. 187 | * 188 | * @return string The client's operating system. 189 | */ 190 | public function clientOS() 191 | { 192 | $operatingSystems = Identifiers::OPERATING_SYSTEM; 193 | 194 | foreach ($operatingSystems as $pattern => $os) { 195 | if (preg_match($pattern, $this->agent)) { 196 | return $os; 197 | } 198 | } 199 | 200 | return 'Unknown'; 201 | } 202 | 203 | /** 204 | * Get the remote IP address. 205 | * 206 | * @return string|null The remote IP address, or null if not available. 207 | */ 208 | public function remoteAddress() 209 | { 210 | return $_SERVER['REMOTE_ADDR'] ?? null; 211 | } 212 | 213 | /** 214 | * Get the remote port. 215 | * 216 | * @return string|null The remote port, or null if not available. 217 | */ 218 | public function remotePort() 219 | { 220 | return $_SERVER['REMOTE_PORT'] ?? null; 221 | } 222 | 223 | /** 224 | * Get information about the browser. 225 | * 226 | * @return array An array containing browser name, version, and platform. 227 | */ 228 | public function browser() 229 | { 230 | $userAgent = $this->agent; 231 | $browsers = Identifiers::BROWSERS; 232 | $platforms = Identifiers::PLATFORMS; 233 | 234 | $browserName = 'Unknown'; 235 | $platform = 'Unknown'; 236 | $version = 'Unknown'; 237 | 238 | // Detect platform 239 | foreach ($platforms as $platformName => $platformRegex) { 240 | if (preg_match($platformRegex, $userAgent)) { 241 | $platform = $platformName; 242 | break; 243 | } 244 | } 245 | 246 | // Detect browser and version 247 | foreach ($browsers as $browserNameI => $browserCode) { 248 | if ($browserName == 'Unknown') { 249 | foreach ($browserCode as $browse) { 250 | $pattern = '#(?'.preg_quote($browse).')[/ ]+(?[0-9.|a-zA-Z.]*)#'; 251 | if (preg_match_all($pattern, $userAgent, $matches)) { 252 | $i = count($matches['browser']); 253 | if ($i > 0) { 254 | $browserName = $browserNameI; 255 | $version = $matches['version'][$i - 1]; 256 | } 257 | break; 258 | } 259 | } 260 | } 261 | } 262 | 263 | return new Browser([ 264 | 'name' => $browserName, 265 | 'version' => $version, 266 | 'platform' => $platform, 267 | ]); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/Tools/Utilities.php: -------------------------------------------------------------------------------- 1 |