├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist ├── bundle.dev.js ├── production.min.js ├── webrtc-ip.d.ts └── webrtc-ip.js ├── imgs ├── webrtc-ips-banner.png └── webrtc-ips-banner.svg ├── package-lock.json ├── package.json ├── src └── webrtc-ip.ts ├── tsconfig.json └── website ├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── favicon.ico ├── globals.css ├── layout.tsx ├── minimal │ └── page.tsx └── page.tsx ├── bun.lockb ├── components.json ├── components ├── charts │ └── graph.tsx └── ui │ ├── Button.tsx │ ├── Code.tsx │ ├── CopyToClipboard.tsx │ ├── card.tsx │ └── chart.tsx ├── lib └── utils.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public ├── .nojekyll └── hack │ ├── Hack-Bold.ttf │ ├── Hack-BoldItalic.ttf │ ├── Hack-Italic.ttf │ └── Hack-Regular.ttf ├── style.css ├── tailwind.config.ts └── tsconfig.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy WebRTC-IP Sample to Github Pages 2 | 3 | on: 4 | # Runs on pushes targeting the default branch 5 | push: 6 | branches: ["main"] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 12 | permissions: 13 | contents: read 14 | pages: write 15 | id-token: write 16 | 17 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 18 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: false 22 | 23 | jobs: 24 | # Build job 25 | build: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: Detect package manager 31 | id: detect-package-manager 32 | run: | 33 | if [ -f "${{ github.workspace }}/website/yarn.lock" ]; then 34 | echo "manager=yarn" >> $GITHUB_OUTPUT 35 | echo "command=install" >> $GITHUB_OUTPUT 36 | echo "runner=yarn" >> $GITHUB_OUTPUT 37 | exit 0 38 | elif [ -f "${{ github.workspace }}/website/package.json" ]; then 39 | echo "manager=npm" >> $GITHUB_OUTPUT 40 | echo "command=ci" >> $GITHUB_OUTPUT 41 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT 42 | exit 0 43 | else 44 | echo "Unable to determine package manager" 45 | exit 1 46 | fi 47 | - name: Setup Node 48 | uses: actions/setup-node@v4 49 | with: 50 | node-version: "20" 51 | cache: ${{ steps.detect-package-manager.outputs.manager }} 52 | - name: Setup Pages 53 | uses: actions/configure-pages@v5 54 | with: 55 | # Automatically inject basePath in your Next.js configuration file and disable 56 | # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). 57 | # 58 | # You may remove this line if you want to manage the configuration yourself. 59 | static_site_generator: next 60 | - name: Restore cache 61 | uses: actions/cache@v4 62 | with: 63 | path: | 64 | website/.next/cache 65 | # Generate a new cache whenever packages or source files change. 66 | key: ${{ runner.os }}-nextjs-${{ hashFiles('website/**/package-lock.json', 'website/**/yarn.lock') }}-${{ hashFiles('website/**/*.[jt]s', 'website/**/*.[jt]sx') }} 67 | # If source files changed but packages didn't, rebuild from a prior cache. 68 | restore-keys: | 69 | ${{ runner.os }}-nextjs-${{ hashFiles('website/**/package-lock.json', 'website/**/yarn.lock') }}- 70 | - name: Install dependencies 71 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 72 | working-directory: website 73 | - name: Build with Next.js 74 | run: ${{ steps.detect-package-manager.outputs.runner }} next build 75 | working-directory: website 76 | - name: Upload artifact 77 | uses: actions/upload-pages-artifact@v3 78 | with: 79 | path: website/out 80 | 81 | # Deployment job 82 | deploy: 83 | environment: 84 | name: github-pages 85 | url: ${{ steps.deployment.outputs.page_url }} 86 | runs-on: ubuntu-latest 87 | needs: build 88 | steps: 89 | - name: Deploy to GitHub Pages 90 | id: deployment 91 | uses: actions/deploy-pages@v4 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | 3 | !dist/webrtc-* 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright © 2021 Joey Malvinni 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 |

5 |

6 | 7 | Build Status 8 | 9 | 10 | Apache 2.0 License 11 | 12 | 13 | Github contributors 14 | 15 | 16 | Snyk vulnerabilities 17 | 18 | 19 | PRs welcome 20 | 21 |

22 | 23 |
24 | 25 | 26 | ## Installation 27 | 28 | The `webrtc-ip` package is available through [npm](https://www.npmjs.com/package/webrtc-ip): 29 | 30 | ```bash 31 | npm install webrtc-ip 32 | ``` 33 | 34 | Alternatively, install using `bun`: 35 | ``` bash 36 | bun install webrtc-ip 37 | ``` 38 | 39 | 40 | ## Usage 41 | 42 | WebRTC-IP is intended to be used with Next.js. A minimal example is present in [website/example]: 43 | 44 | ```ts 45 | import { useState, useEffect } from "react"; 46 | import { getIP } from 'webrtc-ip'; 47 | 48 | export default function Home() { 49 | const [ip, setIp] = useState("0.0.0.0"); 50 | 51 | useEffect(() => { 52 | (async () => { 53 | try { 54 | const ipAddress: string = await getIP(); 55 | setIp(ipAddress); 56 | } catch (error) { 57 | console.error("Failed to fetch IP address:", error); 58 | } 59 | })(); 60 | }, []); 61 | 62 | return ( 63 |
64 |

{ip}

65 |
66 | ); 67 | } 68 | ``` 69 | 70 | ## Authors 71 | 72 | The author of webrtc-ips is [Joey Malvinni](https://github.com/joeymalvinni) 73 | 74 | [List of all contributors](https://github.com/joeymalvinni/webrtc-ip/graphs/contributors) 75 | 76 | ## License 77 | 78 | [Apache 2.0](LICENSE) 79 | -------------------------------------------------------------------------------- /dist/bundle.dev.js: -------------------------------------------------------------------------------- 1 | // ! WARNING: Outdated code. Please update to the newest 'webrtc-ip' npm module for use with Next.js 2 | 3 | /* 4 | * This file is the entire script combined in working order. 5 | * Copyright 2021 © Joey Malvinni 6 | * License: MIT 7 | */ 8 | 9 | // [---------------------------------------------------------------------------] 10 | // File: ip_validator.js 11 | 12 | /* 13 | * This module validates the two types of IP addresses. 14 | * Copyright 2021 © Joey Malvinni 15 | * License: MIT 16 | */ 17 | 18 | // The function that checks if the given IPv4 address is valid. 19 | function is_ipv4(ip){ 20 | return regex_v4.test(ip); 21 | }; 22 | 23 | // Checks if the IPv6 address is valid. 24 | function is_ipv6(ip){ 25 | return regex_v6.test(ip); 26 | }; 27 | 28 | // Simple IP regex that works on both IPv6 and IPv4 29 | var simpleIPRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g; 30 | 31 | // IPv4 regex used to determine whether an IP is IPv4 or not. 32 | let regex_v4 = /((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/; 33 | 34 | // The IPv6 regex used when determining an IPv6 address. 35 | let regex_v6 = /((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/; 36 | 37 | // Exporting the two regexes in an array to be used in the main detector. 38 | let ip_regex_array = [regex_v6, regex_v4] 39 | 40 | 41 | // [---------------------------------------------------------------------------] 42 | // File: peer_conn.js 43 | 44 | /* 45 | * This module provides the main WebRTC functions that return IP addresses from the STUN request. 46 | * Copyright 2021 © Joey Malvinni 47 | * License: MIT 48 | */ 49 | 50 | 51 | function peer(callback){ 52 | // Creating the peer connection. 53 | var WebRTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; 54 | // Initializing the connection. 55 | var createdConnection; 56 | 57 | // Main start function. 58 | function start(){ 59 | // Creating the actual connection. 60 | createConnection() 61 | // Log the STUN request. 62 | createStunRequest() 63 | }; 64 | 65 | // Stop function to reset the connection. 66 | function stop(){ 67 | // Checking if the connection has been created or not. 68 | if (createdConnection) { 69 | // Attempt to close it, if RTCPeerConnection.close() is not supported, remove the event listeners. 70 | try { 71 | createdConnection.close(); 72 | } finally { 73 | createdConnection.onicecandidate = () => {}; 74 | createdConnection = null; 75 | }; 76 | }; 77 | }; 78 | 79 | // Function that makes the connection request to Google's STUN server 80 | function createConnection(){ 81 | let iceServers = [{ 82 | urls: 'stun:stun.l.google.com:19302' 83 | }]; 84 | // Creating the connection with the STUN server. 85 | createdConnection = new WebRTCPeerConnection({ iceServers }); 86 | // Handling the ICE candidate event. 87 | createdConnection.onicecandidate = (data) => handleCandidates(data); 88 | // Creation of the fake data channel. 89 | createdConnection.createDataChannel('fake_data_channel'); 90 | }; 91 | 92 | // Function that creates the STUN request offer needed to get the IPs. 93 | function createStunRequest(){ 94 | // Create the offer that exposes the IP addresses. 95 | return createdConnection.createOffer().then(sdp => createdConnection.setLocalDescription(sdp)); 96 | }; 97 | 98 | // Handling the onIceCandidate event. 99 | function handleCandidates(ice){ 100 | // Checking if the ICE candidate lines given are valid. 101 | if (ice && ice.candidate && ice.candidate.candidate) { 102 | // Returning the IPs to the main function. 103 | callback(ice && ice.candidate && ice.candidate.candidate); 104 | }; 105 | }; 106 | 107 | // Returning the main functions needed. 108 | return { 109 | start, 110 | stop, 111 | createConnection, 112 | createStunRequest, 113 | handleCandidates 114 | }; 115 | }; 116 | 117 | // [---------------------------------------------------------------------------] 118 | // File: public_ip.js 119 | 120 | /* 121 | * This module provides the worker functions that return the public IP addresses. 122 | * Copyright 2021 © Joey Malvinni 123 | * License: MIT 124 | */ 125 | 126 | 127 | function publicIPs(timer){ 128 | // Timing validation. 129 | if(timer) if(timer < 100) throw new Error('Custom timeout cannot be under 100 milliseconds.'); 130 | 131 | // IPs is the final array of all valid IPs found. 132 | var IPs = []; 133 | // Creating the peer connection request while handling the callback event. 134 | var peerConn = peer(handleIceCandidates); 135 | 136 | function getIps(){ 137 | // Returning a promise. 138 | return new Promise(function(resolve, reject) { 139 | // Starting the peer connection. 140 | peerConn.start(); 141 | // Setting the timer. 142 | setTimeout(() => { 143 | // Checking if the IP array exists. 144 | if(!IPs){ 145 | // Rejecting the error 146 | reject('No IP addresses were found.') 147 | } else { 148 | // Return the unique IP addresses in an array. 149 | resolve(unique(IPs.flat(Infinity))) 150 | }; 151 | // reset the peer connection. 152 | reset(); 153 | // Set the Timeout to the custom timer, default to 500 milliseconds. 154 | }, timer || 500); 155 | }); 156 | }; 157 | 158 | function handleIceCandidates(ip){ 159 | var array = []; 160 | // Looping over the two regexs for IPv6 and IPv4 161 | for(let regex of ip_regex_array){ 162 | let arr = []; 163 | // Lutting all of the strings that match either IP format in an array 164 | let possible_ips_array = regex.exec(ip) 165 | if(possible_ips_array){ 166 | // Looping over that array 167 | for(let i = 0; i < possible_ips_array.length; i++){ 168 | // Checking if the "IP" is valid 169 | if(is_ipv4(possible_ips_array[i]) || is_ipv6(possible_ips_array[i])){ 170 | arr.push(possible_ips_array[i]) 171 | }; 172 | }; 173 | array.push(arr); 174 | }; 175 | }; 176 | // Final function that does more checks to determine the array's validity, 177 | // Also flattens the array to remove extraneous sub-arrays. 178 | push(array.flat(Infinity)) 179 | }; 180 | 181 | function push(ip){ 182 | // Checks if the IP addresses givin are already in the array. 183 | if(!IPs.includes(ip)){ 184 | IPs.push(unique(ip.flat(Infinity))); 185 | }; 186 | }; 187 | 188 | function reset(){ 189 | // Stops the peer connection to the STUN server. 190 | peerConn.stop() 191 | }; 192 | // Use this to only return unique IP addresses. 193 | function unique(a) { 194 | return Array.from(new Set(a)); 195 | }; 196 | 197 | return getIps(); 198 | }; 199 | 200 | // [---------------------------------------------------------------------------] 201 | // File: index.js 202 | 203 | /* 204 | * This module combines all of the worker modules into the main functions that get exported. 205 | * Copyright 2021 © Joey Malvinni 206 | * License: MIT 207 | */ 208 | 209 | // Categorizes the IPs by IP, type, and IPv4. 210 | function getIPTypes(timer){ 211 | // Returning the result as a promise. 212 | return new Promise(function(resolve, reject) { 213 | // Final array 214 | let finalIpArray = [] 215 | // Getting the raw IPs in an array. 216 | publicIPs(timer).then((ips)=>{ 217 | // Looping over each IP. 218 | ips.forEach(ip => { 219 | if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) { 220 | // The IP is private. 221 | finalIpArray.push({ ip: ip, type: 'private', IPv4: true }) 222 | } else if (ip.match(/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/)) { 223 | // The IP is an IPv6 address. 224 | finalIpArray.push({ ip: ip, type: 'IPv6', IPv4: false }) 225 | } else { 226 | // Assume the IP is public. 227 | finalIpArray.push({ ip: ip, type: 'public', IPv4: true }) 228 | } 229 | }) 230 | // Resolving the promise. 231 | resolve(finalIpArray) 232 | }).catch(reject) 233 | }) 234 | } 235 | 236 | // Filters out IPv4 addresses. 237 | function getIPv4(timer) { 238 | return getIPTypes(timer).then(ips => { 239 | // Filters the IP by IPv4. 240 | const ip = ips.filter(ip => ip.IPv4); 241 | // Loops over each object and extracts the IP. 242 | for(let i = 0; i < ip.length; i++){ 243 | ip[i] = ip[i].ip 244 | } 245 | // Returns undefined if the array is empty. 246 | return ip ? ip : ''; 247 | }); 248 | } 249 | 250 | // Filters out IPv6 addresses. 251 | function getIPv6(timer) { 252 | // Getting the IPs by type. 253 | return getIPTypes(timer).then(ips => { 254 | // Filtering the IPs by IPv6. 255 | const ip = ips.filter(ip => ip.type === 'IPv6'); 256 | // Extracting the IPs 257 | for(let i = 0; i < ip.length; i++){ 258 | // Removing all other data from the object. 259 | ip[i] = ip[i].ip 260 | } 261 | // Returning the IP or undefined. 262 | return ip ? ip.ip : ''; 263 | }); 264 | } 265 | 266 | // Returns all of the functions in an object, default to getting all of the IPs without any filtering applied. 267 | function getIPs(timer){ 268 | return Object.assign( 269 | publicIPs(timer), { 270 | types: getIPTypes, 271 | public: publicIPs, 272 | IPv4: getIPv4, 273 | IPv6: getIPv6, 274 | } 275 | ) 276 | }; -------------------------------------------------------------------------------- /dist/production.min.js: -------------------------------------------------------------------------------- 1 | function is_ipv4(d){return regex_v4.test(d)}function is_ipv6(d){return regex_v6.test(d)}var simpleIPRegex=/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g;let regex_v4=/((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/,regex_v6=/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/,ip_regex_array=[regex_v6,regex_v4];function peer(d){var e,t=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection;function n(){(e=new t({iceServers:[{urls:"stun:stun.l.google.com:19302"}]})).onicecandidate=(d=>f(d)),e.createDataChannel("fake_data_channel")}function a(){return e.createOffer().then(d=>e.setLocalDescription(d))}function f(e){e&&e.candidate&&e.candidate.candidate&&d(e&&e.candidate&&e.candidate.candidate)}return{start:function(){n(),a()},stop:function(){if(e)try{e.close()}finally{e.onicecandidate=(()=>{}),e=null}},createConnection:n,createStunRequest:a,handleCandidates:f}}function publicIPs(d){if(d&&d<100)throw new Error("Custom timeout cannot be under 100 milliseconds.");var e=[],t=peer(function(d){var t=[];for(let e of ip_regex_array){let n=[],a=e.exec(d);if(a){for(let d=0;d{e&&e!==[]?a(n(e.flat(1/0))):f("No IP addresses were found."),t.stop()},d||500)})}function getIPTypes(d){return new Promise(function(e,t){let n=[];publicIPs(d).then(d=>{d.forEach(d=>{d.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)?n.push({ip:d,type:"private",IPv4:!0}):d.match(/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/)?n.push({ip:d,type:"IPv6",IPv4:!1}):n.push({ip:d,type:"public",IPv4:!0})}),e(n)}).catch(t)})}function getIPv4(d){return getIPTypes(d).then(d=>{const e=d.filter(d=>d.IPv4);for(let d=0;d{const e=d.filter(d=>"IPv6"===d.type);for(let d=0;d; 2 | export { getIP }; 3 | -------------------------------------------------------------------------------- /dist/webrtc-ip.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.getIP = getIP; 13 | function getIP() { 14 | return __awaiter(this, arguments, void 0, function* (stun = "stun:stun.l.google.com:19302") { 15 | if (typeof window === 'undefined') { 16 | return ""; 17 | } 18 | const config = { 19 | iceServers: [{ urls: stun }] 20 | }; 21 | const p = new RTCPeerConnection(config); 22 | return new Promise((resolve, reject) => { 23 | p.onicecandidate = (event) => { 24 | if (event.candidate && event.candidate.candidate) { 25 | let split = event.candidate.candidate.split(" "); 26 | if (split[7] !== "host") { 27 | resolve(split[4]); 28 | } 29 | } 30 | }; 31 | p.createDataChannel('ip channel'); 32 | p.createOffer() 33 | .then((offer) => p.setLocalDescription(offer)) 34 | .catch(reject); 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /imgs/webrtc-ips-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joeymalvinni/webrtc-ip/f6ca9c42b3487545dc906615eb087acd3c666b2d/imgs/webrtc-ips-banner.png -------------------------------------------------------------------------------- /imgs/webrtc-ips-banner.svg: -------------------------------------------------------------------------------- 1 | webrtc-ips💻 Simplified IP client using WebRTC. -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtc-ip", 3 | "version": "4.0.5", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "webrtc-ip", 9 | "version": "4.0.5", 10 | "license": "Apache-2.0", 11 | "devDependencies": { 12 | "next": "*", 13 | "react-syntax-highlighter": "^15.5.0", 14 | "typescript": "^5.5.3", 15 | "webrtc-ip": "^4.0.4" 16 | } 17 | }, 18 | "node_modules/@babel/runtime": { 19 | "version": "7.24.8", 20 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", 21 | "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", 22 | "dev": true, 23 | "license": "MIT", 24 | "dependencies": { 25 | "regenerator-runtime": "^0.14.0" 26 | }, 27 | "engines": { 28 | "node": ">=6.9.0" 29 | } 30 | }, 31 | "node_modules/@next/env": { 32 | "version": "14.2.5", 33 | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz", 34 | "integrity": "sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==", 35 | "dev": true, 36 | "license": "MIT" 37 | }, 38 | "node_modules/@next/swc-darwin-arm64": { 39 | "version": "14.2.5", 40 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz", 41 | "integrity": "sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==", 42 | "cpu": [ 43 | "arm64" 44 | ], 45 | "dev": true, 46 | "license": "MIT", 47 | "optional": true, 48 | "os": [ 49 | "darwin" 50 | ], 51 | "engines": { 52 | "node": ">= 10" 53 | } 54 | }, 55 | "node_modules/@next/swc-darwin-x64": { 56 | "version": "14.2.5", 57 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", 58 | "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", 59 | "cpu": [ 60 | "x64" 61 | ], 62 | "dev": true, 63 | "license": "MIT", 64 | "optional": true, 65 | "os": [ 66 | "darwin" 67 | ], 68 | "engines": { 69 | "node": ">= 10" 70 | } 71 | }, 72 | "node_modules/@next/swc-linux-arm64-gnu": { 73 | "version": "14.2.5", 74 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", 75 | "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", 76 | "cpu": [ 77 | "arm64" 78 | ], 79 | "dev": true, 80 | "license": "MIT", 81 | "optional": true, 82 | "os": [ 83 | "linux" 84 | ], 85 | "engines": { 86 | "node": ">= 10" 87 | } 88 | }, 89 | "node_modules/@next/swc-linux-arm64-musl": { 90 | "version": "14.2.5", 91 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", 92 | "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", 93 | "cpu": [ 94 | "arm64" 95 | ], 96 | "dev": true, 97 | "license": "MIT", 98 | "optional": true, 99 | "os": [ 100 | "linux" 101 | ], 102 | "engines": { 103 | "node": ">= 10" 104 | } 105 | }, 106 | "node_modules/@next/swc-linux-x64-gnu": { 107 | "version": "14.2.5", 108 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", 109 | "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", 110 | "cpu": [ 111 | "x64" 112 | ], 113 | "dev": true, 114 | "license": "MIT", 115 | "optional": true, 116 | "os": [ 117 | "linux" 118 | ], 119 | "engines": { 120 | "node": ">= 10" 121 | } 122 | }, 123 | "node_modules/@next/swc-linux-x64-musl": { 124 | "version": "14.2.5", 125 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", 126 | "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", 127 | "cpu": [ 128 | "x64" 129 | ], 130 | "dev": true, 131 | "license": "MIT", 132 | "optional": true, 133 | "os": [ 134 | "linux" 135 | ], 136 | "engines": { 137 | "node": ">= 10" 138 | } 139 | }, 140 | "node_modules/@next/swc-win32-arm64-msvc": { 141 | "version": "14.2.5", 142 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", 143 | "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", 144 | "cpu": [ 145 | "arm64" 146 | ], 147 | "dev": true, 148 | "license": "MIT", 149 | "optional": true, 150 | "os": [ 151 | "win32" 152 | ], 153 | "engines": { 154 | "node": ">= 10" 155 | } 156 | }, 157 | "node_modules/@next/swc-win32-ia32-msvc": { 158 | "version": "14.2.5", 159 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", 160 | "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", 161 | "cpu": [ 162 | "ia32" 163 | ], 164 | "dev": true, 165 | "license": "MIT", 166 | "optional": true, 167 | "os": [ 168 | "win32" 169 | ], 170 | "engines": { 171 | "node": ">= 10" 172 | } 173 | }, 174 | "node_modules/@next/swc-win32-x64-msvc": { 175 | "version": "14.2.5", 176 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", 177 | "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", 178 | "cpu": [ 179 | "x64" 180 | ], 181 | "dev": true, 182 | "license": "MIT", 183 | "optional": true, 184 | "os": [ 185 | "win32" 186 | ], 187 | "engines": { 188 | "node": ">= 10" 189 | } 190 | }, 191 | "node_modules/@swc/counter": { 192 | "version": "0.1.3", 193 | "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", 194 | "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", 195 | "dev": true, 196 | "license": "Apache-2.0" 197 | }, 198 | "node_modules/@swc/helpers": { 199 | "version": "0.5.5", 200 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", 201 | "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", 202 | "dev": true, 203 | "license": "Apache-2.0", 204 | "dependencies": { 205 | "@swc/counter": "^0.1.3", 206 | "tslib": "^2.4.0" 207 | } 208 | }, 209 | "node_modules/@types/hast": { 210 | "version": "2.3.10", 211 | "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", 212 | "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", 213 | "dev": true, 214 | "license": "MIT", 215 | "dependencies": { 216 | "@types/unist": "^2" 217 | } 218 | }, 219 | "node_modules/@types/unist": { 220 | "version": "2.0.10", 221 | "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", 222 | "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", 223 | "dev": true, 224 | "license": "MIT" 225 | }, 226 | "node_modules/busboy": { 227 | "version": "1.6.0", 228 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 229 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 230 | "dev": true, 231 | "dependencies": { 232 | "streamsearch": "^1.1.0" 233 | }, 234 | "engines": { 235 | "node": ">=10.16.0" 236 | } 237 | }, 238 | "node_modules/caniuse-lite": { 239 | "version": "1.0.30001642", 240 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", 241 | "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", 242 | "dev": true, 243 | "funding": [ 244 | { 245 | "type": "opencollective", 246 | "url": "https://opencollective.com/browserslist" 247 | }, 248 | { 249 | "type": "tidelift", 250 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 251 | }, 252 | { 253 | "type": "github", 254 | "url": "https://github.com/sponsors/ai" 255 | } 256 | ], 257 | "license": "CC-BY-4.0" 258 | }, 259 | "node_modules/character-entities": { 260 | "version": "1.2.4", 261 | "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", 262 | "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", 263 | "dev": true, 264 | "license": "MIT", 265 | "funding": { 266 | "type": "github", 267 | "url": "https://github.com/sponsors/wooorm" 268 | } 269 | }, 270 | "node_modules/character-entities-legacy": { 271 | "version": "1.1.4", 272 | "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", 273 | "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", 274 | "dev": true, 275 | "license": "MIT", 276 | "funding": { 277 | "type": "github", 278 | "url": "https://github.com/sponsors/wooorm" 279 | } 280 | }, 281 | "node_modules/character-reference-invalid": { 282 | "version": "1.1.4", 283 | "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", 284 | "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", 285 | "dev": true, 286 | "license": "MIT", 287 | "funding": { 288 | "type": "github", 289 | "url": "https://github.com/sponsors/wooorm" 290 | } 291 | }, 292 | "node_modules/client-only": { 293 | "version": "0.0.1", 294 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", 295 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", 296 | "dev": true, 297 | "license": "MIT" 298 | }, 299 | "node_modules/comma-separated-tokens": { 300 | "version": "1.0.8", 301 | "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", 302 | "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", 303 | "dev": true, 304 | "license": "MIT", 305 | "funding": { 306 | "type": "github", 307 | "url": "https://github.com/sponsors/wooorm" 308 | } 309 | }, 310 | "node_modules/fault": { 311 | "version": "1.0.4", 312 | "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", 313 | "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", 314 | "dev": true, 315 | "license": "MIT", 316 | "dependencies": { 317 | "format": "^0.2.0" 318 | }, 319 | "funding": { 320 | "type": "github", 321 | "url": "https://github.com/sponsors/wooorm" 322 | } 323 | }, 324 | "node_modules/format": { 325 | "version": "0.2.2", 326 | "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", 327 | "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", 328 | "dev": true, 329 | "engines": { 330 | "node": ">=0.4.x" 331 | } 332 | }, 333 | "node_modules/graceful-fs": { 334 | "version": "4.2.11", 335 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 336 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 337 | "dev": true, 338 | "license": "ISC" 339 | }, 340 | "node_modules/hast-util-parse-selector": { 341 | "version": "2.2.5", 342 | "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", 343 | "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", 344 | "dev": true, 345 | "license": "MIT", 346 | "funding": { 347 | "type": "opencollective", 348 | "url": "https://opencollective.com/unified" 349 | } 350 | }, 351 | "node_modules/hastscript": { 352 | "version": "6.0.0", 353 | "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", 354 | "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", 355 | "dev": true, 356 | "license": "MIT", 357 | "dependencies": { 358 | "@types/hast": "^2.0.0", 359 | "comma-separated-tokens": "^1.0.0", 360 | "hast-util-parse-selector": "^2.0.0", 361 | "property-information": "^5.0.0", 362 | "space-separated-tokens": "^1.0.0" 363 | }, 364 | "funding": { 365 | "type": "opencollective", 366 | "url": "https://opencollective.com/unified" 367 | } 368 | }, 369 | "node_modules/highlight.js": { 370 | "version": "10.7.3", 371 | "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", 372 | "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", 373 | "dev": true, 374 | "license": "BSD-3-Clause", 375 | "engines": { 376 | "node": "*" 377 | } 378 | }, 379 | "node_modules/is-alphabetical": { 380 | "version": "1.0.4", 381 | "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", 382 | "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", 383 | "dev": true, 384 | "license": "MIT", 385 | "funding": { 386 | "type": "github", 387 | "url": "https://github.com/sponsors/wooorm" 388 | } 389 | }, 390 | "node_modules/is-alphanumerical": { 391 | "version": "1.0.4", 392 | "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", 393 | "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", 394 | "dev": true, 395 | "license": "MIT", 396 | "dependencies": { 397 | "is-alphabetical": "^1.0.0", 398 | "is-decimal": "^1.0.0" 399 | }, 400 | "funding": { 401 | "type": "github", 402 | "url": "https://github.com/sponsors/wooorm" 403 | } 404 | }, 405 | "node_modules/is-decimal": { 406 | "version": "1.0.4", 407 | "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", 408 | "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", 409 | "dev": true, 410 | "license": "MIT", 411 | "funding": { 412 | "type": "github", 413 | "url": "https://github.com/sponsors/wooorm" 414 | } 415 | }, 416 | "node_modules/is-hexadecimal": { 417 | "version": "1.0.4", 418 | "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", 419 | "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", 420 | "dev": true, 421 | "license": "MIT", 422 | "funding": { 423 | "type": "github", 424 | "url": "https://github.com/sponsors/wooorm" 425 | } 426 | }, 427 | "node_modules/js-tokens": { 428 | "version": "4.0.0", 429 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 430 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 431 | "dev": true, 432 | "license": "MIT", 433 | "peer": true 434 | }, 435 | "node_modules/loose-envify": { 436 | "version": "1.4.0", 437 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 438 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 439 | "dev": true, 440 | "license": "MIT", 441 | "peer": true, 442 | "dependencies": { 443 | "js-tokens": "^3.0.0 || ^4.0.0" 444 | }, 445 | "bin": { 446 | "loose-envify": "cli.js" 447 | } 448 | }, 449 | "node_modules/lowlight": { 450 | "version": "1.20.0", 451 | "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", 452 | "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", 453 | "dev": true, 454 | "license": "MIT", 455 | "dependencies": { 456 | "fault": "^1.0.0", 457 | "highlight.js": "~10.7.0" 458 | }, 459 | "funding": { 460 | "type": "github", 461 | "url": "https://github.com/sponsors/wooorm" 462 | } 463 | }, 464 | "node_modules/nanoid": { 465 | "version": "3.3.7", 466 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 467 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 468 | "dev": true, 469 | "funding": [ 470 | { 471 | "type": "github", 472 | "url": "https://github.com/sponsors/ai" 473 | } 474 | ], 475 | "license": "MIT", 476 | "bin": { 477 | "nanoid": "bin/nanoid.cjs" 478 | }, 479 | "engines": { 480 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 481 | } 482 | }, 483 | "node_modules/next": { 484 | "version": "14.2.5", 485 | "resolved": "https://registry.npmjs.org/next/-/next-14.2.5.tgz", 486 | "integrity": "sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==", 487 | "dev": true, 488 | "license": "MIT", 489 | "dependencies": { 490 | "@next/env": "14.2.5", 491 | "@swc/helpers": "0.5.5", 492 | "busboy": "1.6.0", 493 | "caniuse-lite": "^1.0.30001579", 494 | "graceful-fs": "^4.2.11", 495 | "postcss": "8.4.31", 496 | "styled-jsx": "5.1.1" 497 | }, 498 | "bin": { 499 | "next": "dist/bin/next" 500 | }, 501 | "engines": { 502 | "node": ">=18.17.0" 503 | }, 504 | "optionalDependencies": { 505 | "@next/swc-darwin-arm64": "14.2.5", 506 | "@next/swc-darwin-x64": "14.2.5", 507 | "@next/swc-linux-arm64-gnu": "14.2.5", 508 | "@next/swc-linux-arm64-musl": "14.2.5", 509 | "@next/swc-linux-x64-gnu": "14.2.5", 510 | "@next/swc-linux-x64-musl": "14.2.5", 511 | "@next/swc-win32-arm64-msvc": "14.2.5", 512 | "@next/swc-win32-ia32-msvc": "14.2.5", 513 | "@next/swc-win32-x64-msvc": "14.2.5" 514 | }, 515 | "peerDependencies": { 516 | "@opentelemetry/api": "^1.1.0", 517 | "@playwright/test": "^1.41.2", 518 | "react": "^18.2.0", 519 | "react-dom": "^18.2.0", 520 | "sass": "^1.3.0" 521 | }, 522 | "peerDependenciesMeta": { 523 | "@opentelemetry/api": { 524 | "optional": true 525 | }, 526 | "@playwright/test": { 527 | "optional": true 528 | }, 529 | "sass": { 530 | "optional": true 531 | } 532 | } 533 | }, 534 | "node_modules/parse-entities": { 535 | "version": "2.0.0", 536 | "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", 537 | "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", 538 | "dev": true, 539 | "license": "MIT", 540 | "dependencies": { 541 | "character-entities": "^1.0.0", 542 | "character-entities-legacy": "^1.0.0", 543 | "character-reference-invalid": "^1.0.0", 544 | "is-alphanumerical": "^1.0.0", 545 | "is-decimal": "^1.0.0", 546 | "is-hexadecimal": "^1.0.0" 547 | }, 548 | "funding": { 549 | "type": "github", 550 | "url": "https://github.com/sponsors/wooorm" 551 | } 552 | }, 553 | "node_modules/picocolors": { 554 | "version": "1.0.1", 555 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", 556 | "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", 557 | "dev": true, 558 | "license": "ISC" 559 | }, 560 | "node_modules/postcss": { 561 | "version": "8.4.31", 562 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 563 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 564 | "dev": true, 565 | "funding": [ 566 | { 567 | "type": "opencollective", 568 | "url": "https://opencollective.com/postcss/" 569 | }, 570 | { 571 | "type": "tidelift", 572 | "url": "https://tidelift.com/funding/github/npm/postcss" 573 | }, 574 | { 575 | "type": "github", 576 | "url": "https://github.com/sponsors/ai" 577 | } 578 | ], 579 | "license": "MIT", 580 | "dependencies": { 581 | "nanoid": "^3.3.6", 582 | "picocolors": "^1.0.0", 583 | "source-map-js": "^1.0.2" 584 | }, 585 | "engines": { 586 | "node": "^10 || ^12 || >=14" 587 | } 588 | }, 589 | "node_modules/prismjs": { 590 | "version": "1.29.0", 591 | "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", 592 | "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", 593 | "dev": true, 594 | "license": "MIT", 595 | "engines": { 596 | "node": ">=6" 597 | } 598 | }, 599 | "node_modules/property-information": { 600 | "version": "5.6.0", 601 | "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", 602 | "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", 603 | "dev": true, 604 | "license": "MIT", 605 | "dependencies": { 606 | "xtend": "^4.0.0" 607 | }, 608 | "funding": { 609 | "type": "github", 610 | "url": "https://github.com/sponsors/wooorm" 611 | } 612 | }, 613 | "node_modules/react": { 614 | "version": "18.3.1", 615 | "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", 616 | "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", 617 | "dev": true, 618 | "license": "MIT", 619 | "peer": true, 620 | "dependencies": { 621 | "loose-envify": "^1.1.0" 622 | }, 623 | "engines": { 624 | "node": ">=0.10.0" 625 | } 626 | }, 627 | "node_modules/react-dom": { 628 | "version": "18.3.1", 629 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", 630 | "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", 631 | "dev": true, 632 | "license": "MIT", 633 | "peer": true, 634 | "dependencies": { 635 | "loose-envify": "^1.1.0", 636 | "scheduler": "^0.23.2" 637 | }, 638 | "peerDependencies": { 639 | "react": "^18.3.1" 640 | } 641 | }, 642 | "node_modules/react-syntax-highlighter": { 643 | "version": "15.5.0", 644 | "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", 645 | "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", 646 | "dev": true, 647 | "license": "MIT", 648 | "dependencies": { 649 | "@babel/runtime": "^7.3.1", 650 | "highlight.js": "^10.4.1", 651 | "lowlight": "^1.17.0", 652 | "prismjs": "^1.27.0", 653 | "refractor": "^3.6.0" 654 | }, 655 | "peerDependencies": { 656 | "react": ">= 0.14.0" 657 | } 658 | }, 659 | "node_modules/refractor": { 660 | "version": "3.6.0", 661 | "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", 662 | "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", 663 | "dev": true, 664 | "license": "MIT", 665 | "dependencies": { 666 | "hastscript": "^6.0.0", 667 | "parse-entities": "^2.0.0", 668 | "prismjs": "~1.27.0" 669 | }, 670 | "funding": { 671 | "type": "github", 672 | "url": "https://github.com/sponsors/wooorm" 673 | } 674 | }, 675 | "node_modules/refractor/node_modules/prismjs": { 676 | "version": "1.27.0", 677 | "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", 678 | "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", 679 | "dev": true, 680 | "license": "MIT", 681 | "engines": { 682 | "node": ">=6" 683 | } 684 | }, 685 | "node_modules/regenerator-runtime": { 686 | "version": "0.14.1", 687 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", 688 | "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", 689 | "dev": true, 690 | "license": "MIT" 691 | }, 692 | "node_modules/scheduler": { 693 | "version": "0.23.2", 694 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", 695 | "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", 696 | "dev": true, 697 | "license": "MIT", 698 | "peer": true, 699 | "dependencies": { 700 | "loose-envify": "^1.1.0" 701 | } 702 | }, 703 | "node_modules/source-map-js": { 704 | "version": "1.2.0", 705 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", 706 | "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", 707 | "dev": true, 708 | "license": "BSD-3-Clause", 709 | "engines": { 710 | "node": ">=0.10.0" 711 | } 712 | }, 713 | "node_modules/space-separated-tokens": { 714 | "version": "1.1.5", 715 | "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", 716 | "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", 717 | "dev": true, 718 | "license": "MIT", 719 | "funding": { 720 | "type": "github", 721 | "url": "https://github.com/sponsors/wooorm" 722 | } 723 | }, 724 | "node_modules/streamsearch": { 725 | "version": "1.1.0", 726 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 727 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 728 | "dev": true, 729 | "engines": { 730 | "node": ">=10.0.0" 731 | } 732 | }, 733 | "node_modules/styled-jsx": { 734 | "version": "5.1.1", 735 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", 736 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", 737 | "dev": true, 738 | "license": "MIT", 739 | "dependencies": { 740 | "client-only": "0.0.1" 741 | }, 742 | "engines": { 743 | "node": ">= 12.0.0" 744 | }, 745 | "peerDependencies": { 746 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" 747 | }, 748 | "peerDependenciesMeta": { 749 | "@babel/core": { 750 | "optional": true 751 | }, 752 | "babel-plugin-macros": { 753 | "optional": true 754 | } 755 | } 756 | }, 757 | "node_modules/tslib": { 758 | "version": "2.6.3", 759 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", 760 | "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", 761 | "dev": true, 762 | "license": "0BSD" 763 | }, 764 | "node_modules/typescript": { 765 | "version": "5.5.3", 766 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", 767 | "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", 768 | "dev": true, 769 | "license": "Apache-2.0", 770 | "bin": { 771 | "tsc": "bin/tsc", 772 | "tsserver": "bin/tsserver" 773 | }, 774 | "engines": { 775 | "node": ">=14.17" 776 | } 777 | }, 778 | "node_modules/webrtc-ip": { 779 | "version": "4.0.5", 780 | "resolved": "https://registry.npmjs.org/webrtc-ip/-/webrtc-ip-4.0.5.tgz", 781 | "integrity": "sha512-8wV3N610r53xohTrUiNJQn4g9fDWuERPUuiUlgCb70iRsdFr7UBiASRte3TnfNgVUxUa8SxokM24tkto3x7z+A==", 782 | "dev": true, 783 | "license": "Apache-2.0" 784 | }, 785 | "node_modules/xtend": { 786 | "version": "4.0.2", 787 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 788 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 789 | "dev": true, 790 | "license": "MIT", 791 | "engines": { 792 | "node": ">=0.4" 793 | } 794 | } 795 | }, 796 | "dependencies": { 797 | "@babel/runtime": { 798 | "version": "7.24.8", 799 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", 800 | "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", 801 | "dev": true, 802 | "requires": { 803 | "regenerator-runtime": "^0.14.0" 804 | } 805 | }, 806 | "@next/env": { 807 | "version": "14.2.5", 808 | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz", 809 | "integrity": "sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==", 810 | "dev": true 811 | }, 812 | "@next/swc-darwin-arm64": { 813 | "version": "14.2.5", 814 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz", 815 | "integrity": "sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==", 816 | "dev": true, 817 | "optional": true 818 | }, 819 | "@next/swc-darwin-x64": { 820 | "version": "14.2.5", 821 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", 822 | "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", 823 | "dev": true, 824 | "optional": true 825 | }, 826 | "@next/swc-linux-arm64-gnu": { 827 | "version": "14.2.5", 828 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", 829 | "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", 830 | "dev": true, 831 | "optional": true 832 | }, 833 | "@next/swc-linux-arm64-musl": { 834 | "version": "14.2.5", 835 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", 836 | "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", 837 | "dev": true, 838 | "optional": true 839 | }, 840 | "@next/swc-linux-x64-gnu": { 841 | "version": "14.2.5", 842 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", 843 | "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", 844 | "dev": true, 845 | "optional": true 846 | }, 847 | "@next/swc-linux-x64-musl": { 848 | "version": "14.2.5", 849 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", 850 | "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", 851 | "dev": true, 852 | "optional": true 853 | }, 854 | "@next/swc-win32-arm64-msvc": { 855 | "version": "14.2.5", 856 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", 857 | "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", 858 | "dev": true, 859 | "optional": true 860 | }, 861 | "@next/swc-win32-ia32-msvc": { 862 | "version": "14.2.5", 863 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", 864 | "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", 865 | "dev": true, 866 | "optional": true 867 | }, 868 | "@next/swc-win32-x64-msvc": { 869 | "version": "14.2.5", 870 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", 871 | "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", 872 | "dev": true, 873 | "optional": true 874 | }, 875 | "@swc/counter": { 876 | "version": "0.1.3", 877 | "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", 878 | "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", 879 | "dev": true 880 | }, 881 | "@swc/helpers": { 882 | "version": "0.5.5", 883 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", 884 | "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", 885 | "dev": true, 886 | "requires": { 887 | "@swc/counter": "^0.1.3", 888 | "tslib": "^2.4.0" 889 | } 890 | }, 891 | "@types/hast": { 892 | "version": "2.3.10", 893 | "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", 894 | "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", 895 | "dev": true, 896 | "requires": { 897 | "@types/unist": "^2" 898 | } 899 | }, 900 | "@types/unist": { 901 | "version": "2.0.10", 902 | "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", 903 | "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", 904 | "dev": true 905 | }, 906 | "busboy": { 907 | "version": "1.6.0", 908 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 909 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 910 | "dev": true, 911 | "requires": { 912 | "streamsearch": "^1.1.0" 913 | } 914 | }, 915 | "caniuse-lite": { 916 | "version": "1.0.30001642", 917 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", 918 | "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", 919 | "dev": true 920 | }, 921 | "character-entities": { 922 | "version": "1.2.4", 923 | "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", 924 | "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", 925 | "dev": true 926 | }, 927 | "character-entities-legacy": { 928 | "version": "1.1.4", 929 | "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", 930 | "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", 931 | "dev": true 932 | }, 933 | "character-reference-invalid": { 934 | "version": "1.1.4", 935 | "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", 936 | "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", 937 | "dev": true 938 | }, 939 | "client-only": { 940 | "version": "0.0.1", 941 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", 942 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", 943 | "dev": true 944 | }, 945 | "comma-separated-tokens": { 946 | "version": "1.0.8", 947 | "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", 948 | "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", 949 | "dev": true 950 | }, 951 | "fault": { 952 | "version": "1.0.4", 953 | "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", 954 | "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", 955 | "dev": true, 956 | "requires": { 957 | "format": "^0.2.0" 958 | } 959 | }, 960 | "format": { 961 | "version": "0.2.2", 962 | "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", 963 | "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", 964 | "dev": true 965 | }, 966 | "graceful-fs": { 967 | "version": "4.2.11", 968 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 969 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 970 | "dev": true 971 | }, 972 | "hast-util-parse-selector": { 973 | "version": "2.2.5", 974 | "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", 975 | "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", 976 | "dev": true 977 | }, 978 | "hastscript": { 979 | "version": "6.0.0", 980 | "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", 981 | "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", 982 | "dev": true, 983 | "requires": { 984 | "@types/hast": "^2.0.0", 985 | "comma-separated-tokens": "^1.0.0", 986 | "hast-util-parse-selector": "^2.0.0", 987 | "property-information": "^5.0.0", 988 | "space-separated-tokens": "^1.0.0" 989 | } 990 | }, 991 | "highlight.js": { 992 | "version": "10.7.3", 993 | "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", 994 | "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", 995 | "dev": true 996 | }, 997 | "is-alphabetical": { 998 | "version": "1.0.4", 999 | "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", 1000 | "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", 1001 | "dev": true 1002 | }, 1003 | "is-alphanumerical": { 1004 | "version": "1.0.4", 1005 | "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", 1006 | "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", 1007 | "dev": true, 1008 | "requires": { 1009 | "is-alphabetical": "^1.0.0", 1010 | "is-decimal": "^1.0.0" 1011 | } 1012 | }, 1013 | "is-decimal": { 1014 | "version": "1.0.4", 1015 | "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", 1016 | "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", 1017 | "dev": true 1018 | }, 1019 | "is-hexadecimal": { 1020 | "version": "1.0.4", 1021 | "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", 1022 | "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", 1023 | "dev": true 1024 | }, 1025 | "js-tokens": { 1026 | "version": "4.0.0", 1027 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1028 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1029 | "dev": true, 1030 | "peer": true 1031 | }, 1032 | "loose-envify": { 1033 | "version": "1.4.0", 1034 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 1035 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 1036 | "dev": true, 1037 | "peer": true, 1038 | "requires": { 1039 | "js-tokens": "^3.0.0 || ^4.0.0" 1040 | } 1041 | }, 1042 | "lowlight": { 1043 | "version": "1.20.0", 1044 | "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", 1045 | "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", 1046 | "dev": true, 1047 | "requires": { 1048 | "fault": "^1.0.0", 1049 | "highlight.js": "~10.7.0" 1050 | } 1051 | }, 1052 | "nanoid": { 1053 | "version": "3.3.7", 1054 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 1055 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 1056 | "dev": true 1057 | }, 1058 | "next": { 1059 | "version": "14.2.5", 1060 | "resolved": "https://registry.npmjs.org/next/-/next-14.2.5.tgz", 1061 | "integrity": "sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==", 1062 | "dev": true, 1063 | "requires": { 1064 | "@next/env": "14.2.5", 1065 | "@next/swc-darwin-arm64": "14.2.5", 1066 | "@next/swc-darwin-x64": "14.2.5", 1067 | "@next/swc-linux-arm64-gnu": "14.2.5", 1068 | "@next/swc-linux-arm64-musl": "14.2.5", 1069 | "@next/swc-linux-x64-gnu": "14.2.5", 1070 | "@next/swc-linux-x64-musl": "14.2.5", 1071 | "@next/swc-win32-arm64-msvc": "14.2.5", 1072 | "@next/swc-win32-ia32-msvc": "14.2.5", 1073 | "@next/swc-win32-x64-msvc": "14.2.5", 1074 | "@swc/helpers": "0.5.5", 1075 | "busboy": "1.6.0", 1076 | "caniuse-lite": "^1.0.30001579", 1077 | "graceful-fs": "^4.2.11", 1078 | "postcss": "8.4.31", 1079 | "styled-jsx": "5.1.1" 1080 | } 1081 | }, 1082 | "parse-entities": { 1083 | "version": "2.0.0", 1084 | "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", 1085 | "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", 1086 | "dev": true, 1087 | "requires": { 1088 | "character-entities": "^1.0.0", 1089 | "character-entities-legacy": "^1.0.0", 1090 | "character-reference-invalid": "^1.0.0", 1091 | "is-alphanumerical": "^1.0.0", 1092 | "is-decimal": "^1.0.0", 1093 | "is-hexadecimal": "^1.0.0" 1094 | } 1095 | }, 1096 | "picocolors": { 1097 | "version": "1.0.1", 1098 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", 1099 | "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", 1100 | "dev": true 1101 | }, 1102 | "postcss": { 1103 | "version": "8.4.31", 1104 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 1105 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 1106 | "dev": true, 1107 | "requires": { 1108 | "nanoid": "^3.3.6", 1109 | "picocolors": "^1.0.0", 1110 | "source-map-js": "^1.0.2" 1111 | } 1112 | }, 1113 | "prismjs": { 1114 | "version": "1.29.0", 1115 | "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", 1116 | "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", 1117 | "dev": true 1118 | }, 1119 | "property-information": { 1120 | "version": "5.6.0", 1121 | "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", 1122 | "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", 1123 | "dev": true, 1124 | "requires": { 1125 | "xtend": "^4.0.0" 1126 | } 1127 | }, 1128 | "react": { 1129 | "version": "18.3.1", 1130 | "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", 1131 | "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", 1132 | "dev": true, 1133 | "peer": true, 1134 | "requires": { 1135 | "loose-envify": "^1.1.0" 1136 | } 1137 | }, 1138 | "react-dom": { 1139 | "version": "18.3.1", 1140 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", 1141 | "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", 1142 | "dev": true, 1143 | "peer": true, 1144 | "requires": { 1145 | "loose-envify": "^1.1.0", 1146 | "scheduler": "^0.23.2" 1147 | } 1148 | }, 1149 | "react-syntax-highlighter": { 1150 | "version": "15.5.0", 1151 | "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", 1152 | "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", 1153 | "dev": true, 1154 | "requires": { 1155 | "@babel/runtime": "^7.3.1", 1156 | "highlight.js": "^10.4.1", 1157 | "lowlight": "^1.17.0", 1158 | "prismjs": "^1.27.0", 1159 | "refractor": "^3.6.0" 1160 | } 1161 | }, 1162 | "refractor": { 1163 | "version": "3.6.0", 1164 | "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", 1165 | "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", 1166 | "dev": true, 1167 | "requires": { 1168 | "hastscript": "^6.0.0", 1169 | "parse-entities": "^2.0.0", 1170 | "prismjs": "~1.27.0" 1171 | }, 1172 | "dependencies": { 1173 | "prismjs": { 1174 | "version": "1.27.0", 1175 | "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", 1176 | "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", 1177 | "dev": true 1178 | } 1179 | } 1180 | }, 1181 | "regenerator-runtime": { 1182 | "version": "0.14.1", 1183 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", 1184 | "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", 1185 | "dev": true 1186 | }, 1187 | "scheduler": { 1188 | "version": "0.23.2", 1189 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", 1190 | "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", 1191 | "dev": true, 1192 | "peer": true, 1193 | "requires": { 1194 | "loose-envify": "^1.1.0" 1195 | } 1196 | }, 1197 | "source-map-js": { 1198 | "version": "1.2.0", 1199 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", 1200 | "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", 1201 | "dev": true 1202 | }, 1203 | "space-separated-tokens": { 1204 | "version": "1.1.5", 1205 | "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", 1206 | "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", 1207 | "dev": true 1208 | }, 1209 | "streamsearch": { 1210 | "version": "1.1.0", 1211 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1212 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1213 | "dev": true 1214 | }, 1215 | "styled-jsx": { 1216 | "version": "5.1.1", 1217 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", 1218 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", 1219 | "dev": true, 1220 | "requires": { 1221 | "client-only": "0.0.1" 1222 | } 1223 | }, 1224 | "tslib": { 1225 | "version": "2.6.3", 1226 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", 1227 | "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", 1228 | "dev": true 1229 | }, 1230 | "typescript": { 1231 | "version": "5.5.3", 1232 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", 1233 | "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", 1234 | "dev": true 1235 | }, 1236 | "webrtc-ip": { 1237 | "version": "4.0.5", 1238 | "resolved": "https://registry.npmjs.org/webrtc-ip/-/webrtc-ip-4.0.5.tgz", 1239 | "integrity": "sha512-8wV3N610r53xohTrUiNJQn4g9fDWuERPUuiUlgCb70iRsdFr7UBiASRte3TnfNgVUxUa8SxokM24tkto3x7z+A==", 1240 | "dev": true 1241 | }, 1242 | "xtend": { 1243 | "version": "4.0.2", 1244 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1245 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1246 | "dev": true 1247 | } 1248 | } 1249 | } 1250 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtc-ip", 3 | "type": "commonjs", 4 | "main": "dist/webrtc-ip.js", 5 | "types": "dist/webrtc-ip.d.ts", 6 | "author": "Joey Malvinni", 7 | "license": "Apache-2.0", 8 | "version": "4.0.5", 9 | "homepage": "https://github.com/joeymalvinni/webrtc-ip#readme", 10 | "description": "💻 Fast IP client using WebRTC.", 11 | "bugs": { 12 | "url": "https://github.com/joeymalvinni/webrtc-ip/issues" 13 | }, 14 | "scripts": { 15 | "start": "cd website && npm run dev" 16 | }, 17 | "keywords": [ 18 | "webrtc", 19 | "ipv4", 20 | "ip-addresses", 21 | "ip-request", 22 | "webrtc-ip", 23 | "webrtc-ip-addresses" 24 | ], 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/joeymalvinni/webrtc-ip.git" 28 | }, 29 | "devDependencies": { 30 | "next": "*", 31 | "react-syntax-highlighter": "^15.5.0", 32 | "typescript": "^5.5.3", 33 | "webrtc-ip": "^4.0.4" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/webrtc-ip.ts: -------------------------------------------------------------------------------- 1 | async function getIP(stun: string = "stun:stun.l.google.com:19302"): Promise { 2 | if (typeof window === 'undefined') { 3 | return ""; 4 | } 5 | 6 | const config = { 7 | iceServers: [{ urls: stun }] 8 | }; 9 | 10 | const p = new RTCPeerConnection(config); 11 | 12 | return new Promise((resolve, reject) => { 13 | p.onicecandidate = (event) => { 14 | if (event.candidate && event.candidate.candidate) { 15 | let split = event.candidate.candidate.split(" "); 16 | 17 | if (split[7] !== "host") { 18 | resolve(split[4]); 19 | } 20 | } 21 | }; 22 | 23 | p.createDataChannel('ip channel'); 24 | p.createOffer() 25 | .then((offer) => p.setLocalDescription(offer)) 26 | .catch(reject); 27 | }); 28 | } 29 | 30 | 31 | export { getIP }; 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true 11 | }, 12 | "exclude": [ 13 | "website", 14 | "example", 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /website/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "react/display-name": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # WebRTC-IP Website 2 | The official website which speed tests WebRTC-IP and other related IP methods. 3 | 4 | ## Running 5 | ``` 6 | npm run dev 7 | ``` 8 | -------------------------------------------------------------------------------- /website/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joeymalvinni/webrtc-ip/f6ca9c42b3487545dc906615eb087acd3c666b2d/website/app/favicon.ico -------------------------------------------------------------------------------- /website/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --chart-1: 12 76% 61%; 8 | --chart-2: 173 58% 39%; 9 | --chart-3: 197 37% 24%; 10 | --chart-4: 43 74% 66%; 11 | --chart-5: 27 87% 67%; 12 | } 13 | 14 | .dark { 15 | --chart-1: 220 70% 50%; 16 | --chart-2: 160 60% 45%; 17 | --chart-3: 30 80% 55%; 18 | --chart-4: 280 65% 60%; 19 | --chart-5: 340 75% 55%; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /website/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Poppins, IBM_Plex_Mono } from "next/font/google"; 3 | import localFont from 'next/font/local'; 4 | import "./globals.css"; 5 | 6 | const poppins = Poppins({ subsets: ["latin"], weight: ['400', '700'], variable: "--font-poppins" }); 7 | const ibm = IBM_Plex_Mono({ subsets: ["latin"], weight: ['400', '700'], variable: "--font-ibm" }); 8 | const hack = localFont({ 9 | src: [ 10 | { 11 | path: '../public/hack/Hack-Regular.ttf', 12 | weight: '400', 13 | style: 'normal', 14 | }, 15 | { 16 | path: '../public/hack/Hack-Bold.ttf', 17 | weight: '700', 18 | style: 'bold', 19 | }, 20 | ], 21 | variable: '--font-hack' 22 | }) 23 | 24 | export const metadata: Metadata = { 25 | title: "WebRTC IP Comparison Website", 26 | description: "The official website which speed tests WebRTC-IP and other related IP methods.", 27 | }; 28 | 29 | export default function RootLayout({ 30 | children, 31 | }: Readonly<{ 32 | children: React.ReactNode; 33 | }>) { 34 | return ( 35 | 36 | {children} 37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /website/app/minimal/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useState, useEffect } from "react"; 4 | import { getIP } from 'webrtc-ip'; 5 | 6 | export default function Home() { 7 | const [ip, setIp] = useState("0.0.0.0"); 8 | const [time, setTime] = useState(0); 9 | 10 | useEffect(() => { 11 | (async () => { 12 | try { 13 | const t0 = performance.now(); 14 | const ipAddress = await getIP(); 15 | const t1 = performance.now(); 16 | setTime(t1 - t0); 17 | setIp(ipAddress); 18 | } catch (error) { 19 | console.error("Failed to fetch IP address:", error); 20 | } 21 | })(); 22 | }, []); 23 | 24 | return ( 25 |
26 |

{ip} - {time} ms

27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /website/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState, useEffect } from "react"; 4 | import Button from "@/components/ui/Button"; 5 | import Code from "@/components/ui/Code"; 6 | import Graph from "@/components/charts/graph"; 7 | import { getIP } from "webrtc-ip"; 8 | 9 | const webrtc_code = `import { get_ip } from "webrtc-ip"; 10 | 11 | get_ip().then((my_ip) => { 12 | console.log(my_ip); 13 | });`; 14 | 15 | const fetch_code = `fetch('https://api.ipify.org') 16 | .then(response => { 17 | if (!response.ok) { 18 | throw new Error('Request failed'); 19 | } 20 | return response.text(); 21 | }) 22 | .then(data => { 23 | console.log(data); 24 | }) 25 | .catch(error => { 26 | console.error(error); 27 | });`; 28 | 29 | // hard coded syntax highlighting generated by shiki 30 | const webrtc_html: string = `
import { get_ip } from "webrtc-ip";
 31 | 
 32 | get_ip().then((my_ip) => {
 33 |     console.log(my_ip);
 34 | });
` satisfies TrustedHTML; 35 | 36 | const fetch_html: string = `
fetch('https://api.ipify.org')
 37 |   .then(response => {
 38 |     if (!response.ok) {
 39 |       throw new Error('Request failed');
 40 |     }
 41 |     return response.text();
 42 |   })
 43 |   .then(data => {
 44 |     console.log(data);
 45 |   })
 46 |   .catch(error => {
 47 |     console.error(error);
 48 |   });
` satisfies TrustedHTML; 49 | 50 | 51 | const Home: React.FC = () => { 52 | const [isWebRTC, setIsWebRTC] = useState(true); 53 | const [ip, setIp] = useState(""); 54 | const [data, setData] = useState<{ name: string; time: number }[]>([]); 55 | const [speedFactor, setSpeedFactor] = useState(null); 56 | 57 | const handleFetchClick = () => { 58 | setIsWebRTC(false); 59 | }; 60 | 61 | const handleWebRTCClick = () => { 62 | setIsWebRTC(true); 63 | }; 64 | 65 | useEffect(() => { 66 | let startWebrtcTime = performance.now(); 67 | getIP().then((ip) => { 68 | let endWebrtcTime = performance.now(); 69 | setIp(ip); 70 | let webrtcTime = endWebrtcTime - startWebrtcTime; 71 | 72 | const fetchLinks = async () => { 73 | const times = await Promise.all([ 74 | fetchAndTime('ipify', 'https://api.ipify.org/'), 75 | fetchAndTime('ipregistry', 'https://api.ipregistry.co/?key=c4j3vfp0lre55jsw'), 76 | fetchAndTime('jsonip', 'https://jsonip.com/') 77 | ]); 78 | 79 | const averageFetchTime = times.reduce((a, b) => a + b, 0) / times.length; 80 | const speedFactor = averageFetchTime / webrtcTime; 81 | 82 | const combinedData = [ 83 | { name: "WebRTC-IP", time: webrtcTime }, 84 | { name: "IPIFY", time: times[0] }, 85 | { name: "IPRegistry", time: times[1] }, 86 | { name: "JSONIP", time: times[2] } 87 | ].sort((a, b) => a.time - b.time); 88 | 89 | setData(combinedData); 90 | setSpeedFactor(speedFactor); 91 | }; 92 | 93 | fetchLinks(); 94 | }); 95 | 96 | }, []); 97 | 98 | async function fetchAndTime(apiName: string, apiUrl: string): Promise { 99 | let startTime = performance.now(); 100 | return fetch(apiUrl, { cache: "no-store" }) 101 | .then(() => { 102 | let endTime = performance.now(); 103 | let time = endTime - startTime; 104 | return time; 105 | }) 106 | .catch(error => { 107 | console.error(`Error fetching IP from ${apiName}:`, error); 108 | return -1; 109 | }); 110 | } 111 | 112 | return ( 113 |
114 |

WebRTC-IP Comparison

115 |

🌐 Enhanced IP address querying with WebRTC

116 | 117 |
118 |
119 | 120 |
121 |

WEBRTC-IP

122 |

123 | Up to {speedFactor ? speedFactor.toFixed(2) : "0"}x faster
124 | 125 | than the average fetch request 126 | 127 |

128 | 129 | 130 |
131 | 132 |
133 |
134 | 141 | 147 |
148 | 149 |
150 | {isWebRTC ? 151 | <> 152 | 156 | : <> 157 | 161 | 162 | } 163 |
164 |
165 |
166 | 167 |

Your IP address: {ip}

168 |
169 |
170 | ); 171 | } 172 | 173 | export default Home; 174 | -------------------------------------------------------------------------------- /website/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joeymalvinni/webrtc-ip/f6ca9c42b3487545dc906615eb087acd3c666b2d/website/bun.lockb -------------------------------------------------------------------------------- /website/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": false, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /website/components/charts/graph.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts" 4 | 5 | import { 6 | ChartConfig, 7 | ChartContainer, 8 | ChartTooltip, 9 | ChartTooltipContent, 10 | } from "@/components/ui/chart" 11 | 12 | const chartConfig = { 13 | time: { 14 | label: "Time (ms)", 15 | color: "#504EC2", 16 | }, 17 | } satisfies ChartConfig 18 | 19 | interface ComponentProps { 20 | data: { name: string; time: number }[]; 21 | } 22 | 23 | export default function Component({ data }: ComponentProps) { 24 | return ( 25 | 26 | 33 | value.slice(0, 6)} 39 | /> 40 | } 43 | /> 44 | 45 | 46 | 47 | 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /website/components/ui/Button.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const cls = (input: string): string => 5 | input 6 | .replace(/\s+/gm, ' ') 7 | .split(' ') 8 | .filter((cond: string) => typeof cond === 'string') 9 | .join(' ') 10 | .trim(); 11 | 12 | const classes = { 13 | base: 'focus:outline-none transition ease-in-out duration-200', 14 | disabled: 'opacity-50 cursor-not-allowed', 15 | pill: 'rounded-full', 16 | size: { 17 | small: 'px-2 py-1 text-sm', 18 | normal: 'px-3 py-2', 19 | large: 'px-8 py-3 text-lg', 20 | }, 21 | variant: { 22 | primary: 23 | 'bg-primary hover:bg-primary_hovered text-white font-ibm tracking-tigher rounded-md', 24 | secondary: 25 | 'text-white bg-secondary hover:bg-secondary_hovered hover:text-[#C3C3D7] font-ibm rounded-md', 26 | danger: 27 | 'bg-red-500 hover:bg-red-800 focus:ring-2 focus:ring-red-500 focus:ring-opacity-50 text-white font-ibm', 28 | }, 29 | }; 30 | 31 | type ButtonProps = { 32 | children: React.ReactNode; 33 | type?: 'button' | 'submit' | 'reset'; 34 | className?: string; 35 | variant?: 'primary' | 'secondary' | 'danger'; 36 | size?: 'small' | 'normal' | 'large'; 37 | pill?: boolean; 38 | disabled?: boolean; 39 | } & React.ButtonHTMLAttributes; 40 | 41 | const Button = forwardRef( 42 | ( 43 | { 44 | children, 45 | type = 'button', 46 | className, 47 | variant = 'primary', 48 | size = 'normal', 49 | pill, 50 | disabled = false, 51 | ...props 52 | }, 53 | ref 54 | ) => ( 55 | 71 | ) 72 | ); 73 | 74 | Button.propTypes = { 75 | children: PropTypes.node.isRequired, 76 | type: PropTypes.oneOf(['button', 'submit', 'reset']), 77 | className: PropTypes.string, 78 | pill: PropTypes.bool, 79 | disabled: PropTypes.bool, 80 | variant: PropTypes.oneOf(['primary', 'secondary', 'danger']), 81 | size: PropTypes.oneOf(['small', 'normal', 'large']), 82 | }; 83 | 84 | export default Button; 85 | -------------------------------------------------------------------------------- /website/components/ui/Code.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import CopyToClipboard from './CopyToClipboard'; 3 | 4 | type Props = { 5 | html: string; 6 | code: string; 7 | }; 8 | 9 | const Code: React.FC = ({ html, code }) => { 10 | const [isHovering, setIsHovered] = useState(false); 11 | const onMouseEnter = () => setIsHovered(true); 12 | const onMouseLeave = () => setIsHovered(false); 13 | return ( 14 |
19 |
23 |
24 | 25 |
26 |
27 | ); 28 | }; 29 | 30 | export default Code; 31 | -------------------------------------------------------------------------------- /website/components/ui/CopyToClipboard.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | 5 | export default function CopyToClipboard({ code }: { code: string }) { 6 | const [isCopied, setIsCopied] = useState(false); 7 | 8 | const copyToClipboard = async () => { 9 | try { 10 | await navigator.clipboard.writeText(code); 11 | console.log("Copied to clipboard"); 12 | setIsCopied(true); 13 | setTimeout(() => { 14 | setIsCopied(false); 15 | }, 2000); 16 | } catch (error) { 17 | console.error("Error copying to clipboard", error); 18 | } 19 | }; 20 | 21 | return ( 22 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /website/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )) 45 | CardTitle.displayName = "CardTitle" 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )) 57 | CardDescription.displayName = "CardDescription" 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )) 65 | CardContent.displayName = "CardContent" 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = "CardFooter" 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /website/components/ui/chart.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as RechartsPrimitive from "recharts" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | // Format: { THEME_NAME: CSS_SELECTOR } 9 | const THEMES = { light: "", dark: ".dark" } as const 10 | 11 | export type ChartConfig = { 12 | [k in string]: { 13 | label?: React.ReactNode 14 | icon?: React.ComponentType 15 | } & ( 16 | | { color?: string; theme?: never } 17 | | { color?: never; theme: Record } 18 | ) 19 | } 20 | 21 | type ChartContextProps = { 22 | config: ChartConfig 23 | } 24 | 25 | const ChartContext = React.createContext(null) 26 | 27 | function useChart() { 28 | const context = React.useContext(ChartContext) 29 | 30 | if (!context) { 31 | throw new Error("useChart must be used within a ") 32 | } 33 | 34 | return context 35 | } 36 | 37 | const ChartContainer = React.forwardRef< 38 | HTMLDivElement, 39 | React.ComponentProps<"div"> & { 40 | config: ChartConfig 41 | children: React.ComponentProps< 42 | typeof RechartsPrimitive.ResponsiveContainer 43 | >["children"] 44 | } 45 | >(({ id, className, children, config, ...props }, ref) => { 46 | const uniqueId = React.useId() 47 | const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` 48 | 49 | return ( 50 | 51 |
60 | 61 | 62 | {children} 63 | 64 |
65 |
66 | ) 67 | }) 68 | ChartContainer.displayName = "Chart" 69 | 70 | const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { 71 | const colorConfig = Object.entries(config).filter( 72 | ([_, config]) => config.theme || config.color 73 | ) 74 | 75 | if (!colorConfig.length) { 76 | return null 77 | } 78 | 79 | return ( 80 |