├── .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 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 |
--------------------------------------------------------------------------------
/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 |
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 |