├── .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 | [](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 | [](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 |
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 = '#(?