├── .github
├── FUNDING.yml
└── workflows
│ └── diagram.yml
├── .gitignore
├── LICENSE
├── README.md
├── bun.lockb
├── diagram.svg
├── jsconfig.json
├── middleware.ts
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.js
├── _document.js
├── api
│ ├── v0
│ │ └── shorten.js
│ ├── v1
│ │ └── shorten.js
│ ├── v2
│ │ ├── domain.js
│ │ └── shorten.js
│ ├── v3
│ │ ├── count.js
│ │ ├── domain.js
│ │ └── shorten.js
│ ├── v4
│ │ ├── count.js
│ │ ├── dns.js
│ │ ├── domain.js
│ │ └── shorten.js
│ ├── v5
│ │ ├── count.js
│ │ ├── dns.js
│ │ ├── domain.js
│ │ └── shorten.js
│ └── v6
│ │ ├── count.js
│ │ ├── dns.js
│ │ ├── domain.js
│ │ └── shorten.js
├── index.js
└── unlock.js
├── postcss.config.js
├── public
└── img
│ ├── city.jpg
│ ├── cityNight.jpeg
│ └── mountain.jpeg
├── styles
└── globals.css
└── tailwind.config.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: 1998code
4 |
--------------------------------------------------------------------------------
/.github/workflows/diagram.yml:
--------------------------------------------------------------------------------
1 | name: Create diagram
2 | on:
3 | workflow_dispatch: {}
4 | push:
5 | branches:
6 | - "*"
7 | jobs:
8 | get_data:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout code
12 | uses: actions/checkout@master
13 | - name: Update diagram
14 | uses: githubocto/repo-visualizer@main
15 | with:
16 | excluded_paths: "ignore,.github"
17 |
--------------------------------------------------------------------------------
/.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 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 MING
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Magic Teleport - Shorten URL
2 | ### A New URL Shortener Solution - Powered by Vercel Storage
3 |
4 | [](https://github.com/1998code/shorten-url/actions/workflows/diagram.yml)
5 |
6 |
7 |
8 | ## Tutorial
9 | https://post.1998.media/how-to-create-a-url-shorten-app-with-vercel-kv > https://shareby.vercel.app/j7ipk2
10 |
11 | ## API Doc
12 | https://1998code.gitbook.io/shortenurl-api/quick-start > https://shareby.vercel.app/q1onhr
13 |
14 |
15 |
16 | ## Structure
17 | 
18 |
19 | ## License
20 | MIT
21 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1998code/shorten-url/dd19c13182e851384491d4756ff8d78124fff823/bun.lockb
--------------------------------------------------------------------------------
/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/middleware.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest, NextResponse } from 'next/server'
2 | import { kv } from "@vercel/kv"
3 |
4 | /**
5 | * Configuration for URL path matching
6 | */
7 | export const config = {
8 | matcher: '/:path*',
9 | }
10 |
11 | /**
12 | * URL redirection middleware
13 | *
14 | * Handles short URL redirection with support for:
15 | * - Direct URL redirection
16 | * - Password-protected URLs (using $ delimiter)
17 | * - Unlock page redirection for protected URLs
18 | */
19 | export async function middleware(req: NextRequest) {
20 | const path = req.nextUrl.pathname.replace('/', '')
21 |
22 | // Skip middleware for system paths
23 | if (path.startsWith('api/') || path.startsWith('img/') || path === '' || path.startsWith('_next/static/')) {
24 | return NextResponse.next()
25 | }
26 |
27 | // Handle password-protected URLs (with $ delimiter)
28 | if (path.includes('$')) {
29 | const longUrl = await kv.get(path)
30 |
31 | if (longUrl) {
32 | // Ensure URL has protocol
33 | let finalUrl = longUrl
34 | if (!finalUrl.match(/^[a-zA-Z]+:\/\//)) {
35 | finalUrl = 'http://' + finalUrl
36 | }
37 | return NextResponse.redirect(new URL(finalUrl))
38 | }
39 | }
40 |
41 | // Handle direct URL redirection
42 | const longUrl = await kv.get(path)
43 | if (longUrl) {
44 | // Ensure URL has protocol
45 | if (!longUrl.match(/^[a-zA-Z]+:\/\//)) {
46 | return NextResponse.redirect(new URL('http://' + longUrl))
47 | }
48 | return NextResponse.redirect(new URL(longUrl))
49 | }
50 |
51 | // Check for password-protected URLs
52 | const hasSecuredUrl = await kv.exists(`${path}$*`)
53 | if (hasSecuredUrl) {
54 | return NextResponse.redirect(new URL(`${req.nextUrl.origin}/unlock?key=${path}`))
55 | }
56 |
57 | // Fallback pattern matching for KV implementations
58 | let securedUrlFound = false
59 | try {
60 | // Fast pattern-based scan
61 | for await (const key of kv.scanIterator({ match: `${path}$*`, count: 1 })) {
62 | securedUrlFound = true
63 | break;
64 | }
65 |
66 | if (securedUrlFound) {
67 | return NextResponse.redirect(new URL(`${req.nextUrl.origin}/unlock?key=${path}`))
68 | }
69 | } catch (error) {
70 | // Progressive fallback for KV implementations without pattern matching
71 | for await (const key of kv.scanIterator()) {
72 | if (key.startsWith(`${path}$`)) {
73 | return NextResponse.redirect(new URL(`${req.nextUrl.origin}/unlock?key=${path}`))
74 | }
75 | }
76 | }
77 |
78 | return NextResponse.next()
79 | }
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | }
5 |
6 | module.exports = nextConfig
7 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shorten-url",
3 | "version": "4.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "shorten-url",
9 | "version": "4.0.0",
10 | "dependencies": {
11 | "@vercel/kv": "^0.2.1",
12 | "autoprefixer": "10.4.14",
13 | "next": "13.4.3",
14 | "postcss": "8.4.23",
15 | "react": "18.2.0",
16 | "react-dom": "18.2.0",
17 | "tailwindcss": "3.3.2"
18 | }
19 | },
20 | "node_modules/@alloc/quick-lru": {
21 | "version": "5.2.0",
22 | "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
23 | "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
24 | "engines": {
25 | "node": ">=10"
26 | },
27 | "funding": {
28 | "url": "https://github.com/sponsors/sindresorhus"
29 | }
30 | },
31 | "node_modules/@jridgewell/gen-mapping": {
32 | "version": "0.3.3",
33 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
34 | "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
35 | "dependencies": {
36 | "@jridgewell/set-array": "^1.0.1",
37 | "@jridgewell/sourcemap-codec": "^1.4.10",
38 | "@jridgewell/trace-mapping": "^0.3.9"
39 | },
40 | "engines": {
41 | "node": ">=6.0.0"
42 | }
43 | },
44 | "node_modules/@jridgewell/resolve-uri": {
45 | "version": "3.1.0",
46 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
47 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
48 | "engines": {
49 | "node": ">=6.0.0"
50 | }
51 | },
52 | "node_modules/@jridgewell/set-array": {
53 | "version": "1.1.2",
54 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
55 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
56 | "engines": {
57 | "node": ">=6.0.0"
58 | }
59 | },
60 | "node_modules/@jridgewell/sourcemap-codec": {
61 | "version": "1.4.15",
62 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
63 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
64 | },
65 | "node_modules/@jridgewell/trace-mapping": {
66 | "version": "0.3.18",
67 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
68 | "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
69 | "dependencies": {
70 | "@jridgewell/resolve-uri": "3.1.0",
71 | "@jridgewell/sourcemap-codec": "1.4.14"
72 | }
73 | },
74 | "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
75 | "version": "1.4.14",
76 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
77 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
78 | },
79 | "node_modules/@next/env": {
80 | "version": "13.4.3",
81 | "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.3.tgz",
82 | "integrity": "sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ=="
83 | },
84 | "node_modules/@next/swc-darwin-arm64": {
85 | "version": "13.4.3",
86 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.3.tgz",
87 | "integrity": "sha512-yx18udH/ZmR4Bw4M6lIIPE3JxsAZwo04iaucEfA2GMt1unXr2iodHUX/LAKNyi6xoLP2ghi0E+Xi1f4Qb8f1LQ==",
88 | "cpu": [
89 | "arm64"
90 | ],
91 | "optional": true,
92 | "os": [
93 | "darwin"
94 | ],
95 | "engines": {
96 | "node": ">= 10"
97 | }
98 | },
99 | "node_modules/@next/swc-darwin-x64": {
100 | "version": "13.4.3",
101 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.3.tgz",
102 | "integrity": "sha512-Mi8xJWh2IOjryAM1mx18vwmal9eokJ2njY4nDh04scy37F0LEGJ/diL6JL6kTXi0UfUCGbMsOItf7vpReNiD2A==",
103 | "cpu": [
104 | "x64"
105 | ],
106 | "optional": true,
107 | "os": [
108 | "darwin"
109 | ],
110 | "engines": {
111 | "node": ">= 10"
112 | }
113 | },
114 | "node_modules/@next/swc-linux-arm64-gnu": {
115 | "version": "13.4.3",
116 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.3.tgz",
117 | "integrity": "sha512-aBvtry4bxJ1xwKZ/LVPeBGBwWVwxa4bTnNkRRw6YffJnn/f4Tv4EGDPaVeYHZGQVA56wsGbtA6nZMuWs/EIk4Q==",
118 | "cpu": [
119 | "arm64"
120 | ],
121 | "optional": true,
122 | "os": [
123 | "linux"
124 | ],
125 | "engines": {
126 | "node": ">= 10"
127 | }
128 | },
129 | "node_modules/@next/swc-linux-arm64-musl": {
130 | "version": "13.4.3",
131 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.3.tgz",
132 | "integrity": "sha512-krT+2G3kEsEUvZoYte3/2IscscDraYPc2B+fDJFipPktJmrv088Pei/RjrhWm5TMIy5URYjZUoDZdh5k940Dyw==",
133 | "cpu": [
134 | "arm64"
135 | ],
136 | "optional": true,
137 | "os": [
138 | "linux"
139 | ],
140 | "engines": {
141 | "node": ">= 10"
142 | }
143 | },
144 | "node_modules/@next/swc-linux-x64-gnu": {
145 | "version": "13.4.3",
146 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.3.tgz",
147 | "integrity": "sha512-AMdFX6EKJjC0G/CM6hJvkY8wUjCcbdj3Qg7uAQJ7PVejRWaVt0sDTMavbRfgMchx8h8KsAudUCtdFkG9hlEClw==",
148 | "cpu": [
149 | "x64"
150 | ],
151 | "optional": true,
152 | "os": [
153 | "linux"
154 | ],
155 | "engines": {
156 | "node": ">= 10"
157 | }
158 | },
159 | "node_modules/@next/swc-linux-x64-musl": {
160 | "version": "13.4.3",
161 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.3.tgz",
162 | "integrity": "sha512-jySgSXE48shaLtcQbiFO9ajE9mqz7pcAVLnVLvRIlUHyQYR/WyZdK8ehLs65Mz6j9cLrJM+YdmdJPyV4WDaz2g==",
163 | "cpu": [
164 | "x64"
165 | ],
166 | "optional": true,
167 | "os": [
168 | "linux"
169 | ],
170 | "engines": {
171 | "node": ">= 10"
172 | }
173 | },
174 | "node_modules/@next/swc-win32-arm64-msvc": {
175 | "version": "13.4.3",
176 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.3.tgz",
177 | "integrity": "sha512-5DxHo8uYcaADiE9pHrg8o28VMt/1kR8voDehmfs9AqS0qSClxAAl+CchjdboUvbCjdNWL1MISCvEfKY2InJ3JA==",
178 | "cpu": [
179 | "arm64"
180 | ],
181 | "optional": true,
182 | "os": [
183 | "win32"
184 | ],
185 | "engines": {
186 | "node": ">= 10"
187 | }
188 | },
189 | "node_modules/@next/swc-win32-ia32-msvc": {
190 | "version": "13.4.3",
191 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.3.tgz",
192 | "integrity": "sha512-LaqkF3d+GXRA5X6zrUjQUrXm2MN/3E2arXBtn5C7avBCNYfm9G3Xc646AmmmpN3DJZVaMYliMyCIQCMDEzk80w==",
193 | "cpu": [
194 | "ia32"
195 | ],
196 | "optional": true,
197 | "os": [
198 | "win32"
199 | ],
200 | "engines": {
201 | "node": ">= 10"
202 | }
203 | },
204 | "node_modules/@next/swc-win32-x64-msvc": {
205 | "version": "13.4.3",
206 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.3.tgz",
207 | "integrity": "sha512-jglUk/x7ZWeOJWlVoKyIAkHLTI+qEkOriOOV+3hr1GyiywzcqfI7TpFSiwC7kk1scOiH7NTFKp8mA3XPNO9bDw==",
208 | "cpu": [
209 | "x64"
210 | ],
211 | "optional": true,
212 | "os": [
213 | "win32"
214 | ],
215 | "engines": {
216 | "node": ">= 10"
217 | }
218 | },
219 | "node_modules/@nodelib/fs.scandir": {
220 | "version": "2.1.5",
221 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
222 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
223 | "dependencies": {
224 | "@nodelib/fs.stat": "2.0.5",
225 | "run-parallel": "^1.1.9"
226 | },
227 | "engines": {
228 | "node": ">= 8"
229 | }
230 | },
231 | "node_modules/@nodelib/fs.stat": {
232 | "version": "2.0.5",
233 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
234 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
235 | "engines": {
236 | "node": ">= 8"
237 | }
238 | },
239 | "node_modules/@nodelib/fs.walk": {
240 | "version": "1.2.8",
241 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
242 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
243 | "dependencies": {
244 | "@nodelib/fs.scandir": "2.1.5",
245 | "fastq": "^1.6.0"
246 | },
247 | "engines": {
248 | "node": ">= 8"
249 | }
250 | },
251 | "node_modules/@swc/helpers": {
252 | "version": "0.5.1",
253 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
254 | "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==",
255 | "dependencies": {
256 | "tslib": "^2.4.0"
257 | }
258 | },
259 | "node_modules/@upstash/redis": {
260 | "version": "1.20.6",
261 | "resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.20.6.tgz",
262 | "integrity": "sha512-q1izaYEUsq/WiXNOjf4oOjFZe8fIeBSZN8d5cEyOD4nem+zxc4jccieorQQrNlEahKPE1ZYLzVEkMODRUfch2g==",
263 | "dependencies": {
264 | "isomorphic-fetch": "^3.0.0"
265 | }
266 | },
267 | "node_modules/@vercel/kv": {
268 | "version": "0.2.1",
269 | "resolved": "https://registry.npmjs.org/@vercel/kv/-/kv-0.2.1.tgz",
270 | "integrity": "sha512-0O1CVh0maG/bduAE6DPKUTfGSnORgrcS5xBYZCb62sOU7PrVZrXhaPbUSBE4q5PXS5DC+cpN6FY2RWNlslUaWQ==",
271 | "dependencies": {
272 | "@upstash/redis": "1.20.6"
273 | },
274 | "engines": {
275 | "node": ">=14.6"
276 | }
277 | },
278 | "node_modules/any-promise": {
279 | "version": "1.3.0",
280 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
281 | "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
282 | },
283 | "node_modules/anymatch": {
284 | "version": "3.1.3",
285 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
286 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
287 | "dependencies": {
288 | "normalize-path": "^3.0.0",
289 | "picomatch": "^2.0.4"
290 | },
291 | "engines": {
292 | "node": ">= 8"
293 | }
294 | },
295 | "node_modules/arg": {
296 | "version": "5.0.2",
297 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
298 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
299 | },
300 | "node_modules/autoprefixer": {
301 | "version": "10.4.14",
302 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
303 | "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
304 | "funding": [
305 | {
306 | "type": "opencollective",
307 | "url": "https://opencollective.com/postcss/"
308 | },
309 | {
310 | "type": "tidelift",
311 | "url": "https://tidelift.com/funding/github/npm/autoprefixer"
312 | }
313 | ],
314 | "dependencies": {
315 | "browserslist": "^4.21.5",
316 | "caniuse-lite": "^1.0.30001464",
317 | "fraction.js": "^4.2.0",
318 | "normalize-range": "^0.1.2",
319 | "picocolors": "^1.0.0",
320 | "postcss-value-parser": "^4.2.0"
321 | },
322 | "bin": {
323 | "autoprefixer": "bin/autoprefixer"
324 | },
325 | "engines": {
326 | "node": "^10 || ^12 || >=14"
327 | },
328 | "peerDependencies": {
329 | "postcss": "^8.1.0"
330 | }
331 | },
332 | "node_modules/balanced-match": {
333 | "version": "1.0.2",
334 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
335 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
336 | },
337 | "node_modules/binary-extensions": {
338 | "version": "2.2.0",
339 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
340 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
341 | "engines": {
342 | "node": ">=8"
343 | }
344 | },
345 | "node_modules/brace-expansion": {
346 | "version": "1.1.11",
347 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
348 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
349 | "dependencies": {
350 | "balanced-match": "^1.0.0",
351 | "concat-map": "0.0.1"
352 | }
353 | },
354 | "node_modules/braces": {
355 | "version": "3.0.2",
356 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
357 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
358 | "dependencies": {
359 | "fill-range": "^7.0.1"
360 | },
361 | "engines": {
362 | "node": ">=8"
363 | }
364 | },
365 | "node_modules/browserslist": {
366 | "version": "4.21.5",
367 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
368 | "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
369 | "funding": [
370 | {
371 | "type": "opencollective",
372 | "url": "https://opencollective.com/browserslist"
373 | },
374 | {
375 | "type": "tidelift",
376 | "url": "https://tidelift.com/funding/github/npm/browserslist"
377 | }
378 | ],
379 | "dependencies": {
380 | "caniuse-lite": "^1.0.30001449",
381 | "electron-to-chromium": "^1.4.284",
382 | "node-releases": "^2.0.8",
383 | "update-browserslist-db": "^1.0.10"
384 | },
385 | "bin": {
386 | "browserslist": "cli.js"
387 | },
388 | "engines": {
389 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
390 | }
391 | },
392 | "node_modules/busboy": {
393 | "version": "1.6.0",
394 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
395 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
396 | "dependencies": {
397 | "streamsearch": "^1.1.0"
398 | },
399 | "engines": {
400 | "node": ">=10.16.0"
401 | }
402 | },
403 | "node_modules/camelcase-css": {
404 | "version": "2.0.1",
405 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
406 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
407 | "engines": {
408 | "node": ">= 6"
409 | }
410 | },
411 | "node_modules/caniuse-lite": {
412 | "version": "1.0.30001488",
413 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001488.tgz",
414 | "integrity": "sha512-NORIQuuL4xGpIy6iCCQGN4iFjlBXtfKWIenlUuyZJumLRIindLb7wXM+GO8erEhb7vXfcnf4BAg2PrSDN5TNLQ==",
415 | "funding": [
416 | {
417 | "type": "opencollective",
418 | "url": "https://opencollective.com/browserslist"
419 | },
420 | {
421 | "type": "tidelift",
422 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
423 | },
424 | {
425 | "type": "github",
426 | "url": "https://github.com/sponsors/ai"
427 | }
428 | ]
429 | },
430 | "node_modules/chokidar": {
431 | "version": "3.5.3",
432 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
433 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
434 | "funding": [
435 | {
436 | "type": "individual",
437 | "url": "https://paulmillr.com/funding/"
438 | }
439 | ],
440 | "dependencies": {
441 | "anymatch": "~3.1.2",
442 | "braces": "~3.0.2",
443 | "glob-parent": "~5.1.2",
444 | "is-binary-path": "~2.1.0",
445 | "is-glob": "~4.0.1",
446 | "normalize-path": "~3.0.0",
447 | "readdirp": "~3.6.0"
448 | },
449 | "engines": {
450 | "node": ">= 8.10.0"
451 | },
452 | "optionalDependencies": {
453 | "fsevents": "~2.3.2"
454 | }
455 | },
456 | "node_modules/chokidar/node_modules/glob-parent": {
457 | "version": "5.1.2",
458 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
459 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
460 | "dependencies": {
461 | "is-glob": "^4.0.1"
462 | },
463 | "engines": {
464 | "node": ">= 6"
465 | }
466 | },
467 | "node_modules/client-only": {
468 | "version": "0.0.1",
469 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
470 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
471 | },
472 | "node_modules/commander": {
473 | "version": "4.1.1",
474 | "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
475 | "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
476 | "engines": {
477 | "node": ">= 6"
478 | }
479 | },
480 | "node_modules/concat-map": {
481 | "version": "0.0.1",
482 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
483 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
484 | },
485 | "node_modules/cssesc": {
486 | "version": "3.0.0",
487 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
488 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
489 | "bin": {
490 | "cssesc": "bin/cssesc"
491 | },
492 | "engines": {
493 | "node": ">=4"
494 | }
495 | },
496 | "node_modules/didyoumean": {
497 | "version": "1.2.2",
498 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
499 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
500 | },
501 | "node_modules/dlv": {
502 | "version": "1.1.3",
503 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
504 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
505 | },
506 | "node_modules/electron-to-chromium": {
507 | "version": "1.4.402",
508 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.402.tgz",
509 | "integrity": "sha512-gWYvJSkohOiBE6ecVYXkrDgNaUjo47QEKK0kQzmWyhkH+yoYiG44bwuicTGNSIQRG3WDMsWVZJLRnJnLNkbWvA=="
510 | },
511 | "node_modules/escalade": {
512 | "version": "3.1.1",
513 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
514 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
515 | "engines": {
516 | "node": ">=6"
517 | }
518 | },
519 | "node_modules/fast-glob": {
520 | "version": "3.2.12",
521 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
522 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
523 | "dependencies": {
524 | "@nodelib/fs.stat": "^2.0.2",
525 | "@nodelib/fs.walk": "^1.2.3",
526 | "glob-parent": "^5.1.2",
527 | "merge2": "^1.3.0",
528 | "micromatch": "^4.0.4"
529 | },
530 | "engines": {
531 | "node": ">=8.6.0"
532 | }
533 | },
534 | "node_modules/fast-glob/node_modules/glob-parent": {
535 | "version": "5.1.2",
536 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
537 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
538 | "dependencies": {
539 | "is-glob": "^4.0.1"
540 | },
541 | "engines": {
542 | "node": ">= 6"
543 | }
544 | },
545 | "node_modules/fastq": {
546 | "version": "1.15.0",
547 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
548 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
549 | "dependencies": {
550 | "reusify": "^1.0.4"
551 | }
552 | },
553 | "node_modules/fill-range": {
554 | "version": "7.0.1",
555 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
556 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
557 | "dependencies": {
558 | "to-regex-range": "^5.0.1"
559 | },
560 | "engines": {
561 | "node": ">=8"
562 | }
563 | },
564 | "node_modules/fraction.js": {
565 | "version": "4.2.0",
566 | "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
567 | "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
568 | "engines": {
569 | "node": "*"
570 | },
571 | "funding": {
572 | "type": "patreon",
573 | "url": "https://www.patreon.com/infusion"
574 | }
575 | },
576 | "node_modules/fs.realpath": {
577 | "version": "1.0.0",
578 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
579 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
580 | },
581 | "node_modules/fsevents": {
582 | "version": "2.3.2",
583 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
584 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
585 | "hasInstallScript": true,
586 | "optional": true,
587 | "os": [
588 | "darwin"
589 | ],
590 | "engines": {
591 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
592 | }
593 | },
594 | "node_modules/function-bind": {
595 | "version": "1.1.1",
596 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
597 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
598 | },
599 | "node_modules/glob": {
600 | "version": "7.1.6",
601 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
602 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
603 | "dependencies": {
604 | "fs.realpath": "^1.0.0",
605 | "inflight": "^1.0.4",
606 | "inherits": "2",
607 | "minimatch": "^3.0.4",
608 | "once": "^1.3.0",
609 | "path-is-absolute": "^1.0.0"
610 | },
611 | "engines": {
612 | "node": "*"
613 | },
614 | "funding": {
615 | "url": "https://github.com/sponsors/isaacs"
616 | }
617 | },
618 | "node_modules/glob-parent": {
619 | "version": "6.0.2",
620 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
621 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
622 | "dependencies": {
623 | "is-glob": "^4.0.3"
624 | },
625 | "engines": {
626 | "node": ">=10.13.0"
627 | }
628 | },
629 | "node_modules/has": {
630 | "version": "1.0.3",
631 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
632 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
633 | "dependencies": {
634 | "function-bind": "^1.1.1"
635 | },
636 | "engines": {
637 | "node": ">= 0.4.0"
638 | }
639 | },
640 | "node_modules/inflight": {
641 | "version": "1.0.6",
642 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
643 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
644 | "dependencies": {
645 | "once": "^1.3.0",
646 | "wrappy": "1"
647 | }
648 | },
649 | "node_modules/inherits": {
650 | "version": "2.0.4",
651 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
652 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
653 | },
654 | "node_modules/is-binary-path": {
655 | "version": "2.1.0",
656 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
657 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
658 | "dependencies": {
659 | "binary-extensions": "^2.0.0"
660 | },
661 | "engines": {
662 | "node": ">=8"
663 | }
664 | },
665 | "node_modules/is-core-module": {
666 | "version": "2.12.1",
667 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
668 | "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
669 | "dependencies": {
670 | "has": "^1.0.3"
671 | },
672 | "funding": {
673 | "url": "https://github.com/sponsors/ljharb"
674 | }
675 | },
676 | "node_modules/is-extglob": {
677 | "version": "2.1.1",
678 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
679 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
680 | "engines": {
681 | "node": ">=0.10.0"
682 | }
683 | },
684 | "node_modules/is-glob": {
685 | "version": "4.0.3",
686 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
687 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
688 | "dependencies": {
689 | "is-extglob": "^2.1.1"
690 | },
691 | "engines": {
692 | "node": ">=0.10.0"
693 | }
694 | },
695 | "node_modules/is-number": {
696 | "version": "7.0.0",
697 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
698 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
699 | "engines": {
700 | "node": ">=0.12.0"
701 | }
702 | },
703 | "node_modules/isomorphic-fetch": {
704 | "version": "3.0.0",
705 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
706 | "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
707 | "dependencies": {
708 | "node-fetch": "^2.6.1",
709 | "whatwg-fetch": "^3.4.1"
710 | }
711 | },
712 | "node_modules/jiti": {
713 | "version": "1.18.2",
714 | "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
715 | "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
716 | "bin": {
717 | "jiti": "bin/jiti.js"
718 | }
719 | },
720 | "node_modules/js-tokens": {
721 | "version": "4.0.0",
722 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
723 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
724 | },
725 | "node_modules/lilconfig": {
726 | "version": "2.1.0",
727 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
728 | "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
729 | "engines": {
730 | "node": ">=10"
731 | }
732 | },
733 | "node_modules/lines-and-columns": {
734 | "version": "1.2.4",
735 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
736 | "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
737 | },
738 | "node_modules/loose-envify": {
739 | "version": "1.4.0",
740 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
741 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
742 | "dependencies": {
743 | "js-tokens": "^3.0.0 || ^4.0.0"
744 | },
745 | "bin": {
746 | "loose-envify": "cli.js"
747 | }
748 | },
749 | "node_modules/merge2": {
750 | "version": "1.4.1",
751 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
752 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
753 | "engines": {
754 | "node": ">= 8"
755 | }
756 | },
757 | "node_modules/micromatch": {
758 | "version": "4.0.5",
759 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
760 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
761 | "dependencies": {
762 | "braces": "^3.0.2",
763 | "picomatch": "^2.3.1"
764 | },
765 | "engines": {
766 | "node": ">=8.6"
767 | }
768 | },
769 | "node_modules/minimatch": {
770 | "version": "3.1.2",
771 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
772 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
773 | "dependencies": {
774 | "brace-expansion": "^1.1.7"
775 | },
776 | "engines": {
777 | "node": "*"
778 | }
779 | },
780 | "node_modules/mz": {
781 | "version": "2.7.0",
782 | "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
783 | "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
784 | "dependencies": {
785 | "any-promise": "^1.0.0",
786 | "object-assign": "^4.0.1",
787 | "thenify-all": "^1.0.0"
788 | }
789 | },
790 | "node_modules/nanoid": {
791 | "version": "3.3.6",
792 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
793 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
794 | "funding": [
795 | {
796 | "type": "github",
797 | "url": "https://github.com/sponsors/ai"
798 | }
799 | ],
800 | "bin": {
801 | "nanoid": "bin/nanoid.cjs"
802 | },
803 | "engines": {
804 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
805 | }
806 | },
807 | "node_modules/next": {
808 | "version": "13.4.3",
809 | "resolved": "https://registry.npmjs.org/next/-/next-13.4.3.tgz",
810 | "integrity": "sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA==",
811 | "dependencies": {
812 | "@next/env": "13.4.3",
813 | "@swc/helpers": "0.5.1",
814 | "busboy": "1.6.0",
815 | "caniuse-lite": "^1.0.30001406",
816 | "postcss": "8.4.14",
817 | "styled-jsx": "5.1.1",
818 | "zod": "3.21.4"
819 | },
820 | "bin": {
821 | "next": "dist/bin/next"
822 | },
823 | "engines": {
824 | "node": ">=16.8.0"
825 | },
826 | "optionalDependencies": {
827 | "@next/swc-darwin-arm64": "13.4.3",
828 | "@next/swc-darwin-x64": "13.4.3",
829 | "@next/swc-linux-arm64-gnu": "13.4.3",
830 | "@next/swc-linux-arm64-musl": "13.4.3",
831 | "@next/swc-linux-x64-gnu": "13.4.3",
832 | "@next/swc-linux-x64-musl": "13.4.3",
833 | "@next/swc-win32-arm64-msvc": "13.4.3",
834 | "@next/swc-win32-ia32-msvc": "13.4.3",
835 | "@next/swc-win32-x64-msvc": "13.4.3"
836 | },
837 | "peerDependencies": {
838 | "@opentelemetry/api": "^1.1.0",
839 | "fibers": ">= 3.1.0",
840 | "node-sass": "^6.0.0 || ^7.0.0",
841 | "react": "^18.2.0",
842 | "react-dom": "^18.2.0",
843 | "sass": "^1.3.0"
844 | },
845 | "peerDependenciesMeta": {
846 | "@opentelemetry/api": {
847 | "optional": true
848 | },
849 | "fibers": {
850 | "optional": true
851 | },
852 | "node-sass": {
853 | "optional": true
854 | },
855 | "sass": {
856 | "optional": true
857 | }
858 | }
859 | },
860 | "node_modules/next/node_modules/postcss": {
861 | "version": "8.4.14",
862 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
863 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
864 | "funding": [
865 | {
866 | "type": "opencollective",
867 | "url": "https://opencollective.com/postcss/"
868 | },
869 | {
870 | "type": "tidelift",
871 | "url": "https://tidelift.com/funding/github/npm/postcss"
872 | }
873 | ],
874 | "dependencies": {
875 | "nanoid": "^3.3.4",
876 | "picocolors": "^1.0.0",
877 | "source-map-js": "^1.0.2"
878 | },
879 | "engines": {
880 | "node": "^10 || ^12 || >=14"
881 | }
882 | },
883 | "node_modules/node-fetch": {
884 | "version": "2.6.11",
885 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
886 | "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
887 | "dependencies": {
888 | "whatwg-url": "^5.0.0"
889 | },
890 | "engines": {
891 | "node": "4.x || >=6.0.0"
892 | },
893 | "peerDependencies": {
894 | "encoding": "^0.1.0"
895 | },
896 | "peerDependenciesMeta": {
897 | "encoding": {
898 | "optional": true
899 | }
900 | }
901 | },
902 | "node_modules/node-releases": {
903 | "version": "2.0.10",
904 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
905 | "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w=="
906 | },
907 | "node_modules/normalize-path": {
908 | "version": "3.0.0",
909 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
910 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
911 | "engines": {
912 | "node": ">=0.10.0"
913 | }
914 | },
915 | "node_modules/normalize-range": {
916 | "version": "0.1.2",
917 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
918 | "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
919 | "engines": {
920 | "node": ">=0.10.0"
921 | }
922 | },
923 | "node_modules/object-assign": {
924 | "version": "4.1.1",
925 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
926 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
927 | "engines": {
928 | "node": ">=0.10.0"
929 | }
930 | },
931 | "node_modules/object-hash": {
932 | "version": "3.0.0",
933 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
934 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
935 | "engines": {
936 | "node": ">= 6"
937 | }
938 | },
939 | "node_modules/once": {
940 | "version": "1.4.0",
941 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
942 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
943 | "dependencies": {
944 | "wrappy": "1"
945 | }
946 | },
947 | "node_modules/path-is-absolute": {
948 | "version": "1.0.1",
949 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
950 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
951 | "engines": {
952 | "node": ">=0.10.0"
953 | }
954 | },
955 | "node_modules/path-parse": {
956 | "version": "1.0.7",
957 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
958 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
959 | },
960 | "node_modules/picocolors": {
961 | "version": "1.0.0",
962 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
963 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
964 | },
965 | "node_modules/picomatch": {
966 | "version": "2.3.1",
967 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
968 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
969 | "engines": {
970 | "node": ">=8.6"
971 | },
972 | "funding": {
973 | "url": "https://github.com/sponsors/jonschlinkert"
974 | }
975 | },
976 | "node_modules/pify": {
977 | "version": "2.3.0",
978 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
979 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
980 | "engines": {
981 | "node": ">=0.10.0"
982 | }
983 | },
984 | "node_modules/pirates": {
985 | "version": "4.0.5",
986 | "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
987 | "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
988 | "engines": {
989 | "node": ">= 6"
990 | }
991 | },
992 | "node_modules/postcss": {
993 | "version": "8.4.23",
994 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
995 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
996 | "funding": [
997 | {
998 | "type": "opencollective",
999 | "url": "https://opencollective.com/postcss/"
1000 | },
1001 | {
1002 | "type": "tidelift",
1003 | "url": "https://tidelift.com/funding/github/npm/postcss"
1004 | },
1005 | {
1006 | "type": "github",
1007 | "url": "https://github.com/sponsors/ai"
1008 | }
1009 | ],
1010 | "dependencies": {
1011 | "nanoid": "^3.3.6",
1012 | "picocolors": "^1.0.0",
1013 | "source-map-js": "^1.0.2"
1014 | },
1015 | "engines": {
1016 | "node": "^10 || ^12 || >=14"
1017 | }
1018 | },
1019 | "node_modules/postcss-import": {
1020 | "version": "15.1.0",
1021 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
1022 | "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
1023 | "dependencies": {
1024 | "postcss-value-parser": "^4.0.0",
1025 | "read-cache": "^1.0.0",
1026 | "resolve": "^1.1.7"
1027 | },
1028 | "engines": {
1029 | "node": ">=14.0.0"
1030 | },
1031 | "peerDependencies": {
1032 | "postcss": "^8.0.0"
1033 | }
1034 | },
1035 | "node_modules/postcss-js": {
1036 | "version": "4.0.1",
1037 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
1038 | "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
1039 | "dependencies": {
1040 | "camelcase-css": "^2.0.1"
1041 | },
1042 | "engines": {
1043 | "node": "^12 || ^14 || >= 16"
1044 | },
1045 | "funding": {
1046 | "type": "opencollective",
1047 | "url": "https://opencollective.com/postcss/"
1048 | },
1049 | "peerDependencies": {
1050 | "postcss": "^8.4.21"
1051 | }
1052 | },
1053 | "node_modules/postcss-load-config": {
1054 | "version": "4.0.1",
1055 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
1056 | "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
1057 | "dependencies": {
1058 | "lilconfig": "^2.0.5",
1059 | "yaml": "^2.1.1"
1060 | },
1061 | "engines": {
1062 | "node": ">= 14"
1063 | },
1064 | "funding": {
1065 | "type": "opencollective",
1066 | "url": "https://opencollective.com/postcss/"
1067 | },
1068 | "peerDependencies": {
1069 | "postcss": ">=8.0.9",
1070 | "ts-node": ">=9.0.0"
1071 | },
1072 | "peerDependenciesMeta": {
1073 | "postcss": {
1074 | "optional": true
1075 | },
1076 | "ts-node": {
1077 | "optional": true
1078 | }
1079 | }
1080 | },
1081 | "node_modules/postcss-nested": {
1082 | "version": "6.0.1",
1083 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
1084 | "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
1085 | "dependencies": {
1086 | "postcss-selector-parser": "^6.0.11"
1087 | },
1088 | "engines": {
1089 | "node": ">=12.0"
1090 | },
1091 | "funding": {
1092 | "type": "opencollective",
1093 | "url": "https://opencollective.com/postcss/"
1094 | },
1095 | "peerDependencies": {
1096 | "postcss": "^8.2.14"
1097 | }
1098 | },
1099 | "node_modules/postcss-selector-parser": {
1100 | "version": "6.0.13",
1101 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
1102 | "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
1103 | "dependencies": {
1104 | "cssesc": "^3.0.0",
1105 | "util-deprecate": "^1.0.2"
1106 | },
1107 | "engines": {
1108 | "node": ">=4"
1109 | }
1110 | },
1111 | "node_modules/postcss-value-parser": {
1112 | "version": "4.2.0",
1113 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
1114 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
1115 | },
1116 | "node_modules/queue-microtask": {
1117 | "version": "1.2.3",
1118 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
1119 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
1120 | "funding": [
1121 | {
1122 | "type": "github",
1123 | "url": "https://github.com/sponsors/feross"
1124 | },
1125 | {
1126 | "type": "patreon",
1127 | "url": "https://www.patreon.com/feross"
1128 | },
1129 | {
1130 | "type": "consulting",
1131 | "url": "https://feross.org/support"
1132 | }
1133 | ]
1134 | },
1135 | "node_modules/react": {
1136 | "version": "18.2.0",
1137 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
1138 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
1139 | "dependencies": {
1140 | "loose-envify": "^1.1.0"
1141 | },
1142 | "engines": {
1143 | "node": ">=0.10.0"
1144 | }
1145 | },
1146 | "node_modules/react-dom": {
1147 | "version": "18.2.0",
1148 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
1149 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
1150 | "dependencies": {
1151 | "loose-envify": "^1.1.0",
1152 | "scheduler": "^0.23.0"
1153 | },
1154 | "peerDependencies": {
1155 | "react": "^18.2.0"
1156 | }
1157 | },
1158 | "node_modules/read-cache": {
1159 | "version": "1.0.0",
1160 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
1161 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
1162 | "dependencies": {
1163 | "pify": "^2.3.0"
1164 | }
1165 | },
1166 | "node_modules/readdirp": {
1167 | "version": "3.6.0",
1168 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1169 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1170 | "dependencies": {
1171 | "picomatch": "^2.2.1"
1172 | },
1173 | "engines": {
1174 | "node": ">=8.10.0"
1175 | }
1176 | },
1177 | "node_modules/resolve": {
1178 | "version": "1.22.2",
1179 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
1180 | "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
1181 | "dependencies": {
1182 | "is-core-module": "^2.11.0",
1183 | "path-parse": "^1.0.7",
1184 | "supports-preserve-symlinks-flag": "^1.0.0"
1185 | },
1186 | "bin": {
1187 | "resolve": "bin/resolve"
1188 | },
1189 | "funding": {
1190 | "url": "https://github.com/sponsors/ljharb"
1191 | }
1192 | },
1193 | "node_modules/reusify": {
1194 | "version": "1.0.4",
1195 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
1196 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
1197 | "engines": {
1198 | "iojs": ">=1.0.0",
1199 | "node": ">=0.10.0"
1200 | }
1201 | },
1202 | "node_modules/run-parallel": {
1203 | "version": "1.2.0",
1204 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
1205 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
1206 | "funding": [
1207 | {
1208 | "type": "github",
1209 | "url": "https://github.com/sponsors/feross"
1210 | },
1211 | {
1212 | "type": "patreon",
1213 | "url": "https://www.patreon.com/feross"
1214 | },
1215 | {
1216 | "type": "consulting",
1217 | "url": "https://feross.org/support"
1218 | }
1219 | ],
1220 | "dependencies": {
1221 | "queue-microtask": "^1.2.2"
1222 | }
1223 | },
1224 | "node_modules/scheduler": {
1225 | "version": "0.23.0",
1226 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
1227 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
1228 | "dependencies": {
1229 | "loose-envify": "^1.1.0"
1230 | }
1231 | },
1232 | "node_modules/source-map-js": {
1233 | "version": "1.0.2",
1234 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1235 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
1236 | "engines": {
1237 | "node": ">=0.10.0"
1238 | }
1239 | },
1240 | "node_modules/streamsearch": {
1241 | "version": "1.1.0",
1242 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
1243 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
1244 | "engines": {
1245 | "node": ">=10.0.0"
1246 | }
1247 | },
1248 | "node_modules/styled-jsx": {
1249 | "version": "5.1.1",
1250 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
1251 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
1252 | "dependencies": {
1253 | "client-only": "0.0.1"
1254 | },
1255 | "engines": {
1256 | "node": ">= 12.0.0"
1257 | },
1258 | "peerDependencies": {
1259 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
1260 | },
1261 | "peerDependenciesMeta": {
1262 | "@babel/core": {
1263 | "optional": true
1264 | },
1265 | "babel-plugin-macros": {
1266 | "optional": true
1267 | }
1268 | }
1269 | },
1270 | "node_modules/sucrase": {
1271 | "version": "3.32.0",
1272 | "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
1273 | "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
1274 | "dependencies": {
1275 | "@jridgewell/gen-mapping": "^0.3.2",
1276 | "commander": "^4.0.0",
1277 | "glob": "7.1.6",
1278 | "lines-and-columns": "^1.1.6",
1279 | "mz": "^2.7.0",
1280 | "pirates": "^4.0.1",
1281 | "ts-interface-checker": "^0.1.9"
1282 | },
1283 | "bin": {
1284 | "sucrase": "bin/sucrase",
1285 | "sucrase-node": "bin/sucrase-node"
1286 | },
1287 | "engines": {
1288 | "node": ">=8"
1289 | }
1290 | },
1291 | "node_modules/supports-preserve-symlinks-flag": {
1292 | "version": "1.0.0",
1293 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
1294 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
1295 | "engines": {
1296 | "node": ">= 0.4"
1297 | },
1298 | "funding": {
1299 | "url": "https://github.com/sponsors/ljharb"
1300 | }
1301 | },
1302 | "node_modules/tailwindcss": {
1303 | "version": "3.3.2",
1304 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
1305 | "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
1306 | "dependencies": {
1307 | "@alloc/quick-lru": "^5.2.0",
1308 | "arg": "^5.0.2",
1309 | "chokidar": "^3.5.3",
1310 | "didyoumean": "^1.2.2",
1311 | "dlv": "^1.1.3",
1312 | "fast-glob": "^3.2.12",
1313 | "glob-parent": "^6.0.2",
1314 | "is-glob": "^4.0.3",
1315 | "jiti": "^1.18.2",
1316 | "lilconfig": "^2.1.0",
1317 | "micromatch": "^4.0.5",
1318 | "normalize-path": "^3.0.0",
1319 | "object-hash": "^3.0.0",
1320 | "picocolors": "^1.0.0",
1321 | "postcss": "^8.4.23",
1322 | "postcss-import": "^15.1.0",
1323 | "postcss-js": "^4.0.1",
1324 | "postcss-load-config": "^4.0.1",
1325 | "postcss-nested": "^6.0.1",
1326 | "postcss-selector-parser": "^6.0.11",
1327 | "postcss-value-parser": "^4.2.0",
1328 | "resolve": "^1.22.2",
1329 | "sucrase": "^3.32.0"
1330 | },
1331 | "bin": {
1332 | "tailwind": "lib/cli.js",
1333 | "tailwindcss": "lib/cli.js"
1334 | },
1335 | "engines": {
1336 | "node": ">=14.0.0"
1337 | }
1338 | },
1339 | "node_modules/thenify": {
1340 | "version": "3.3.1",
1341 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
1342 | "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
1343 | "dependencies": {
1344 | "any-promise": "^1.0.0"
1345 | }
1346 | },
1347 | "node_modules/thenify-all": {
1348 | "version": "1.6.0",
1349 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
1350 | "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
1351 | "dependencies": {
1352 | "thenify": ">= 3.1.0 < 4"
1353 | },
1354 | "engines": {
1355 | "node": ">=0.8"
1356 | }
1357 | },
1358 | "node_modules/to-regex-range": {
1359 | "version": "5.0.1",
1360 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1361 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1362 | "dependencies": {
1363 | "is-number": "^7.0.0"
1364 | },
1365 | "engines": {
1366 | "node": ">=8.0"
1367 | }
1368 | },
1369 | "node_modules/tr46": {
1370 | "version": "0.0.3",
1371 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
1372 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
1373 | },
1374 | "node_modules/ts-interface-checker": {
1375 | "version": "0.1.13",
1376 | "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
1377 | "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
1378 | },
1379 | "node_modules/tslib": {
1380 | "version": "2.5.2",
1381 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
1382 | "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA=="
1383 | },
1384 | "node_modules/update-browserslist-db": {
1385 | "version": "1.0.11",
1386 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
1387 | "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
1388 | "funding": [
1389 | {
1390 | "type": "opencollective",
1391 | "url": "https://opencollective.com/browserslist"
1392 | },
1393 | {
1394 | "type": "tidelift",
1395 | "url": "https://tidelift.com/funding/github/npm/browserslist"
1396 | },
1397 | {
1398 | "type": "github",
1399 | "url": "https://github.com/sponsors/ai"
1400 | }
1401 | ],
1402 | "dependencies": {
1403 | "escalade": "^3.1.1",
1404 | "picocolors": "^1.0.0"
1405 | },
1406 | "bin": {
1407 | "update-browserslist-db": "cli.js"
1408 | },
1409 | "peerDependencies": {
1410 | "browserslist": ">= 4.21.0"
1411 | }
1412 | },
1413 | "node_modules/util-deprecate": {
1414 | "version": "1.0.2",
1415 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1416 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
1417 | },
1418 | "node_modules/webidl-conversions": {
1419 | "version": "3.0.1",
1420 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
1421 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
1422 | },
1423 | "node_modules/whatwg-fetch": {
1424 | "version": "3.6.2",
1425 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
1426 | "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
1427 | },
1428 | "node_modules/whatwg-url": {
1429 | "version": "5.0.0",
1430 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
1431 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
1432 | "dependencies": {
1433 | "tr46": "~0.0.3",
1434 | "webidl-conversions": "^3.0.0"
1435 | }
1436 | },
1437 | "node_modules/wrappy": {
1438 | "version": "1.0.2",
1439 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1440 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
1441 | },
1442 | "node_modules/yaml": {
1443 | "version": "2.2.2",
1444 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
1445 | "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
1446 | "engines": {
1447 | "node": ">= 14"
1448 | }
1449 | },
1450 | "node_modules/zod": {
1451 | "version": "3.21.4",
1452 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
1453 | "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
1454 | "funding": {
1455 | "url": "https://github.com/sponsors/colinhacks"
1456 | }
1457 | }
1458 | }
1459 | }
1460 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shorten-url",
3 | "version": "6.0.0",
4 | "private": false,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "^1.34.9",
13 | "@vercel/kv": "^0.2.1",
14 | "autoprefixer": "10.4.14",
15 | "next": "^14.2.29",
16 | "postcss": "8.4.23",
17 | "react": "18.2.0",
18 | "react-dom": "18.2.0",
19 | "tailwindcss": "3.3.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '@/styles/globals.css'
2 |
3 | export default function App({ Component, pageProps }) {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/pages/_document.js:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/pages/api/v0/shorten.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 |
5 | // GET urls from POST body
6 | const urls = req.body.trim()
7 |
8 | // Split the URLs into an array, line break or ,
9 | const urlsArray = urls.split(/[\n,]+/)
10 |
11 | const results = []
12 |
13 | // Shorten each URL
14 | for (const url of urlsArray) {
15 | if (url === '') continue
16 |
17 | // radomly generate a key
18 | let key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
19 |
20 | // check if key already exists
21 | const checkKey = await kv.get(key)
22 |
23 | // while check key
24 | while (checkKey) {
25 | key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
26 | }
27 |
28 | // set the key
29 | await kv.set(key, url)
30 |
31 | // push the result to the array
32 | results.push({
33 | key: key,
34 | url: url
35 | })
36 | }
37 |
38 | // Return the results
39 | res.status(200).json(results)
40 | }
41 |
--------------------------------------------------------------------------------
/pages/api/v1/shorten.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 |
5 | const body = JSON.parse(req.body)
6 |
7 | // GET urls from POST body
8 | const urls = body.urls.trim()
9 |
10 | const password = body.password
11 |
12 | // Split the URLs into an array, line break or ,
13 | const urlsArray = urls.split(/[\n,]+/)
14 |
15 | const results = []
16 |
17 | // Shorten each URL
18 | for (const url of urlsArray) {
19 | if (url === '') continue
20 |
21 | // radomly generate a key
22 | let key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
23 |
24 | // check if key already exists
25 | const checkKey = await kv.get(key)
26 |
27 | // while check key
28 | while (checkKey) {
29 | key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
30 | }
31 |
32 | // if password is not null, set the password
33 | if (password !== "") {
34 | key = key + "$" + password
35 | }
36 |
37 | // set the key
38 | await kv.set(key, url)
39 |
40 | // push the result to the array
41 | results.push({
42 | key: key.split("$")[0],
43 | url: url
44 | })
45 | }
46 |
47 | // Return the results
48 | res.status(200).json(results)
49 | }
50 |
--------------------------------------------------------------------------------
/pages/api/v2/domain.js:
--------------------------------------------------------------------------------
1 | export default async function handler(req, res) {
2 | const { add } = req.query
3 |
4 | const response = await fetch(
5 | `https://api.vercel.com/v9/projects/${process.env.PROJECT_ID_VERCEL}/domains?teamId=${process.env.TEAM_ID_VERCEL}`,
6 | {
7 | body: `{\n "name": "${add.replaceAll('http://','').replaceAll('https://','')}"\n}`,
8 | headers: {
9 | Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN}`,
10 | 'Content-Type': 'application/json',
11 | },
12 | method: 'POST',
13 | }
14 | )
15 |
16 | const data = await response.json()
17 |
18 | if (data.error?.code == 'forbidden') {
19 | res.status(403).end()
20 | } else if (data.error?.code == 'domain_taken') {
21 | res.status(409).end()
22 | } else {
23 | res.status(200).end()
24 | }
25 | }
--------------------------------------------------------------------------------
/pages/api/v2/shorten.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 |
5 | const body = JSON.parse(req.body)
6 |
7 | // GET urls from POST body
8 | const urls = body.urls.trim()
9 |
10 | const password = body.password
11 |
12 | // Split the URLs into an array, line break or ,
13 | const urlsArray = urls.split(/[\n,]+/)
14 |
15 | const results = []
16 |
17 | // Shorten each URL
18 | for (const url of urlsArray) {
19 | if (url === '') continue
20 |
21 | // radomly generate a key
22 | let key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
23 |
24 | // check if key already exists
25 | const checkKey = await kv.get(key)
26 |
27 | // while check key
28 | while (checkKey) {
29 | key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
30 | }
31 |
32 | // if password is not null, set the password
33 | if (password !== "") {
34 | key = key + "$" + password
35 | }
36 |
37 | // set the key
38 | await kv.set(key, url)
39 |
40 | // push the result to the array
41 | results.push({
42 | key: key.split("$")[0],
43 | url: url
44 | })
45 | }
46 |
47 | // Return the results
48 | res.status(200).json(results)
49 | }
50 |
--------------------------------------------------------------------------------
/pages/api/v3/count.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 |
5 | let count = 0
6 |
7 | for await (const key of kv.scanIterator()) {
8 | count++
9 | }
10 |
11 | // Return the results
12 | res.status(200).json(count)
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/pages/api/v3/domain.js:
--------------------------------------------------------------------------------
1 | export default async function handler(req, res) {
2 | const { add } = req.query
3 |
4 | const response = await fetch(
5 | `https://api.vercel.com/v9/projects/${process.env.PROJECT_ID_VERCEL}/domains?teamId=${process.env.TEAM_ID_VERCEL}`,
6 | {
7 | body: `{\n "name": "${add.replaceAll('http://','').replaceAll('https://','')}"\n}`,
8 | headers: {
9 | Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN}`,
10 | 'Content-Type': 'application/json',
11 | },
12 | method: 'POST',
13 | }
14 | )
15 |
16 | const data = await response.json()
17 |
18 | if (data.error?.code == 'forbidden') {
19 | res.status(403).end()
20 | } else if (data.error?.code == 'domain_taken') {
21 | res.status(409).end()
22 | } else {
23 | res.status(200).end()
24 | }
25 | }
--------------------------------------------------------------------------------
/pages/api/v3/shorten.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 | import { stringify } from "postcss"
3 |
4 | export default async function handler(req, res) {
5 | console.log(req.body)
6 | // const body = req.body
7 | // parse body if string
8 | const body = typeof req.body === "string" ? JSON.parse(req.body) : req.body
9 |
10 | // GET urls from POST body
11 | const urls = body.urls.trim()
12 |
13 | const password = body.password
14 |
15 | // Split the URLs into an array, line break or ,
16 | const urlsArray = urls.split(/[\n,]+/)
17 |
18 | const results = []
19 |
20 | // Shorten each URL
21 | for (const url of urlsArray) {
22 | if (url === '') continue
23 |
24 | // radomly generate a key
25 | let key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
26 |
27 | // check if key already exists
28 | const checkKey = await kv.get(key)
29 |
30 | // while check key
31 | while (checkKey) {
32 | key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
33 | }
34 |
35 | // if password is not null, set the password
36 | if (password !== "") {
37 | key = key + "$" + password
38 | }
39 |
40 | // set the key
41 | await kv.set(key, url)
42 |
43 | // push the result to the array
44 | results.push({
45 | key: key.split("$")[0],
46 | url: url
47 | })
48 | }
49 |
50 | // Return the results
51 | res.status(200).json(results)
52 | }
53 |
--------------------------------------------------------------------------------
/pages/api/v4/count.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 |
5 | let count = 0
6 |
7 | for await (const key of kv.scanIterator()) {
8 | count++
9 | }
10 |
11 | // Return the results
12 | res.status(200).json(count)
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/pages/api/v4/dns.js:
--------------------------------------------------------------------------------
1 | import dns from 'dns';
2 |
3 | export default async function handler(req, res) {
4 | const { domain } = req.query;
5 | const { address: ip } = await dns.promises.lookup(domain);
6 |
7 | if (typeof ip !== 'string') {
8 | res.status(400).send('Invalid IP address');
9 | return;
10 | }
11 |
12 | // IP string start with 76.76.21.
13 | const validIP = ip.startsWith('76.76.21.');
14 |
15 | if (validIP) {
16 | res.status(200).send('Setup Completed');
17 | } else {
18 | res.status(400).send('Pending for Configuring DNS');
19 | }
20 | }
--------------------------------------------------------------------------------
/pages/api/v4/domain.js:
--------------------------------------------------------------------------------
1 | export default async function handler(req, res) {
2 | const { add } = req.query
3 |
4 | const response = await fetch(
5 | `https://api.vercel.com/v9/projects/${process.env.PROJECT_ID_VERCEL}/domains?teamId=${process.env.TEAM_ID_VERCEL}`,
6 | {
7 | body: `{\n "name": "${add.replaceAll('http://','').replaceAll('https://','')}"\n}`,
8 | headers: {
9 | Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN}`,
10 | 'Content-Type': 'application/json',
11 | },
12 | method: 'POST',
13 | }
14 | )
15 |
16 | const data = await response.json()
17 |
18 | if (data.error?.code == 'forbidden') {
19 | res.status(403).end()
20 | } else if (data.error?.code == 'domain_taken') {
21 | res.status(409).end()
22 | } else {
23 | res.status(200).end()
24 | }
25 | }
--------------------------------------------------------------------------------
/pages/api/v4/shorten.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 | const body = typeof req.body === "string" ? JSON.parse(req.body) : req.body
5 |
6 | // GET urls from POST body
7 | const urls = body.urls.trim()
8 |
9 | const password = body.password
10 |
11 | // Split the URLs into an array, line break or ,
12 | const urlsArray = urls.split(/[\n,]+/)
13 |
14 | const results = []
15 |
16 | // Shorten each URL
17 | for (const url of urlsArray) {
18 | if (url === '') continue
19 |
20 | // Randomly generate a key
21 | let key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
22 |
23 | //Check if the key already exists
24 | const checkKey = await kv.get(key)
25 |
26 | while (checkKey) {
27 | key = Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5)
28 | }
29 |
30 | // If a password is not null, set the password
31 | if (password !== "") {
32 | key = key + "$" + password
33 | }
34 |
35 | // Set the key
36 | await kv.set(key, url)
37 |
38 | // Push the result to the array
39 | results.push({
40 | key: key.split("$")[0],
41 | url: url
42 | })
43 | }
44 |
45 | // Return the results
46 | res.status(200).json(results)
47 | }
48 |
--------------------------------------------------------------------------------
/pages/api/v5/count.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 |
5 | let count = 0
6 |
7 | for await (const key of kv.scanIterator()) {
8 | count++
9 | }
10 |
11 | // Return the results
12 | res.status(200).json(count)
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/pages/api/v5/dns.js:
--------------------------------------------------------------------------------
1 | import dns from 'dns';
2 |
3 | export default async function handler(req, res) {
4 | const { domain } = req.query;
5 | const { address: ip } = await dns.promises.lookup(domain);
6 |
7 | if (typeof ip !== 'string') {
8 | res.status(400).send('Invalid IP address');
9 | return;
10 | }
11 |
12 | // IP string start with 76.76.21.
13 | const validIP = ip.startsWith('76.76.21.');
14 |
15 | if (validIP) {
16 | res.status(200).send('Setup Completed');
17 | } else {
18 | res.status(400).send('Pending for Configuring DNS');
19 | }
20 | }
--------------------------------------------------------------------------------
/pages/api/v5/domain.js:
--------------------------------------------------------------------------------
1 | export default async function handler(req, res) {
2 | const { add } = req.query
3 |
4 | const response = await fetch(
5 | `https://api.vercel.com/v9/projects/${process.env.PROJECT_ID_VERCEL}/domains?teamId=${process.env.TEAM_ID_VERCEL}`,
6 | {
7 | body: `{\n "name": "${add.replaceAll('http://','').replaceAll('https://','')}"\n}`,
8 | headers: {
9 | Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN}`,
10 | 'Content-Type': 'application/json',
11 | },
12 | method: 'POST',
13 | }
14 | )
15 |
16 | const data = await response.json()
17 |
18 | if (data.error?.code == 'forbidden') {
19 | res.status(403).end()
20 | } else if (data.error?.code == 'domain_taken') {
21 | res.status(409).end()
22 | } else {
23 | res.status(200).end()
24 | }
25 | }
--------------------------------------------------------------------------------
/pages/api/v5/shorten.js:
--------------------------------------------------------------------------------
1 | import { kv } from "@vercel/kv"
2 |
3 | export default async function handler(req, res) {
4 | const body = typeof req.body === "string" ? JSON.parse(req.body) : req.body
5 | const urls = body.urls.trim()
6 | const password = body.password
7 | const urlsArray = urls.split(/[\n,]+/)
8 |
9 | const promises = urlsArray.map(async (url) => {
10 | if (url === '') return null
11 |
12 | let keys = Array.from({length: 10}, () => Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5))
13 | let existingKeys = await Promise.all(keys.map(key => kv.get(key)))
14 | let key = keys.find((key, index) => !existingKeys[index])
15 |
16 | if (password !== "") {
17 | key = key + "$" + password
18 | }
19 |
20 | await kv.set(key, url)
21 |
22 | return {
23 | key: key.split("$")[0],
24 | url: url
25 | }
26 | })
27 |
28 | const results = await Promise.all(promises)
29 |
30 | res.status(200).json(results.filter(result => result !== null))
31 | }
--------------------------------------------------------------------------------
/pages/api/v6/count.js:
--------------------------------------------------------------------------------
1 | import { Redis } from '@upstash/redis';
2 |
3 | const redis = Redis.fromEnv();
4 | const CACHE_KEY = 'url_count_cache';
5 | const CACHE_EXPIRY = 60 * 5; // 5 minutes in seconds
6 |
7 | export default async function handler(req, res) {
8 | try {
9 | // Set Cache-Control header for HTTP caching (CDN and browsers)
10 | res.setHeader('Cache-Control', 'public, s-maxage=300, stale-while-revalidate=59');
11 |
12 | // Try to get the count from cache first
13 | const cachedCount = await redis.get(CACHE_KEY);
14 |
15 | if (cachedCount !== null) {
16 | // Return cached count if available
17 | return res.status(200).json(parseInt(cachedCount));
18 | }
19 |
20 | // If no cached value, calculate the count
21 | let count = 0;
22 | let cursor = "0";
23 | do {
24 | const [nextCursor, keys] = await redis.scan(cursor, { count: 100 });
25 | count += keys.length;
26 | cursor = nextCursor;
27 | } while (cursor !== "0");
28 |
29 | // Cache the new count
30 | await redis.set(CACHE_KEY, count.toString(), { ex: CACHE_EXPIRY });
31 |
32 | res.status(200).json(count);
33 | } catch (err) {
34 | res.status(500).json({ error: 'Internal Server Error', details: err.message });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/pages/api/v6/dns.js:
--------------------------------------------------------------------------------
1 | import dns from 'dns';
2 |
3 | export default async function handler(req, res) {
4 | const { domain } = req.query;
5 | const { address: ip } = await dns.promises.lookup(domain);
6 |
7 | if (typeof ip !== 'string') {
8 | res.status(400).send('Invalid IP address');
9 | return;
10 | }
11 |
12 | // IP string start with 76.76.21.
13 | const validIP = ip.startsWith('76.76.21.') || ip.startsWith('216.198.79.') || ip.startsWith('64.29.17.');
14 |
15 | if (validIP) {
16 | res.status(200).send('Setup Completed');
17 | } else {
18 | res.status(400).send('Pending for Configuring DNS');
19 | }
20 | }
--------------------------------------------------------------------------------
/pages/api/v6/domain.js:
--------------------------------------------------------------------------------
1 | export default async function handler(req, res) {
2 | const { add } = req.query
3 |
4 | try {
5 | const response = await fetch(
6 | `https://api.vercel.com/v9/projects/${process.env.PROJECT_ID_VERCEL}/domains?teamId=${process.env.TEAM_ID_VERCEL}`,
7 | {
8 | body: `{\n "name": "${add.replaceAll('http://','').replaceAll('https://','')}"\n}`,
9 | headers: {
10 | Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN}`,
11 | 'Content-Type': 'application/json',
12 | },
13 | method: 'POST',
14 | }
15 | )
16 |
17 | var data = await response.json()
18 |
19 | if (data.error) {
20 | const { projectName, teamName, ...cleanedData } = data.error
21 | data = cleanedData
22 | } else {
23 | const { projectId, ...cleanedData } = data
24 | data = cleanedData
25 | }
26 |
27 | return res.status(response.status).json(data)
28 |
29 | } catch (err) {
30 | return res.status(500).json({ error: 'Unexpected error occurred!', details: err.message })
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/pages/api/v6/shorten.js:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis"
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | export default async function handler(req, res) {
6 | const body = typeof req.body === "string" ? JSON.parse(req.body) : req.body
7 | const urls = body.urls.trim()
8 | const password = body.password
9 | const urlsArray = urls.split(/[\n,]+/)
10 |
11 | const promises = urlsArray.map(async (url) => {
12 | if (url === '') return null
13 |
14 | let keys = Array.from({length: 10}, () => Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5))
15 | let existingKeys = await Promise.all(keys.map(key => redis.get(key)))
16 | let key = keys.find((key, index) => !existingKeys[index])
17 |
18 | if (password !== "") {
19 | key = key + "$" + password
20 | }
21 |
22 | await redis.set(key, url)
23 |
24 | return {
25 | key: key.split("$")[0],
26 | url: url
27 | }
28 | })
29 |
30 | const results = await Promise.all(promises)
31 |
32 | res.status(200).json(results.filter(result => result !== null))
33 | }
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 |
3 | export default function Home() {
4 |
5 | const [results, setResults] = useState([])
6 | const [loading, setLoading] = useState(false)
7 |
8 | // Handle form submit
9 | const handleSubmit = async () => {
10 | // Prevent
11 | event.preventDefault()
12 |
13 | // Set loading
14 | setLoading(true)
15 |
16 | // Get the form data
17 | const form = document.getElementById('form')
18 | const formData = new FormData(form)
19 | const urls = formData.get('urls')
20 | const password = formData.get('password')
21 | const domain = formData.get('domain')
22 | const ref = formData.get('ref')
23 |
24 | // GET /api/{API Version}/domain?add={domain}
25 | if (domain !== "") {
26 | await fetch(`/api/v6/domain?add=${domain}`)
27 | .then(res => {
28 | if (res.status === 403) {
29 | alert('You are not authorized to add this domain.')
30 | } else if (res.status === 409) {
31 | alert('This domain/subdomain is already taken. Please remove it from your Vercel account and try again.')
32 | } else if (res.status === 200) {
33 | // check domain ip CNAME to cname.vercel-dns.com
34 | fetch(`https://dns.google/resolve?name=${domain}&type=CNAME`)
35 | .then(res => res.json())
36 | .then(data => {
37 | if ( (data.Answer && data.Answer[0].data === 'cname.vercel-dns.com.') || (data.Authority && data.Authority[0].name === 'vercel.app.') ) {
38 | }
39 | else {
40 | alert('Domain/Subdomain added successfully. Please use CNAME and point to cname.vercel-dns.com.')
41 | }
42 | })
43 | } else {
44 | alert('Something went wrong, please try again later.')
45 | }
46 | })
47 | .catch(err => {
48 | console.error(err)
49 | alert('Something went wrong, please try again later.')
50 | })
51 | }
52 |
53 | // POST to /api/{API Version}/shorten
54 | await fetch('/api/v6/shorten', {
55 | method: 'POST',
56 | body: JSON.stringify({
57 | urls: urls,
58 | password: password
59 | })
60 | })
61 | .then(res => res.json())
62 | .then(data => {
63 | setResults(data)
64 | setLoading(false)
65 | })
66 | .catch(err => {
67 | console.error(err)
68 | alert('Something went wrong, please try again later.')
69 | setLoading(false)
70 | })
71 | }
72 |
73 | // Download results as CSV
74 | const downloadCSV = () => {
75 | const csv = results.map(result => `${result.url},${window.location.origin}/${result.key}`).join('\n')
76 | const blob = new Blob([csv], { type: 'text/csv' })
77 | const url = window.URL.createObjectURL(blob)
78 | const a = document.createElement('a')
79 | a.setAttribute('hidden', '')
80 | a.setAttribute('href', url)
81 | a.setAttribute('download', 'shortenurl.csv')
82 | document.body.appendChild(a)
83 | a.click()
84 | document.body.removeChild(a)
85 | }
86 |
87 | // Download results as xlsx
88 | const downloadXLSX = () => {
89 | const xlsx = results.map(result => `${result.url},${window.location.origin}/${result.key}`).join('\n')
90 | const blob = new Blob([xlsx], { type: 'text/xlsx' })
91 | const url = window.URL.createObjectURL(blob)
92 | const a = document.createElement('a')
93 | a.setAttribute('hidden', '')
94 | a.setAttribute('href', url)
95 | a.setAttribute('download', 'shortenurl.xlsx')
96 | document.body.appendChild(a)
97 | a.click()
98 | document.body.removeChild(a)
99 | }
100 |
101 | // Download results as JSON
102 | const downloadJSON = () => {
103 | const json = JSON.stringify(results, null, 2)
104 | const blob = new Blob([json], { type: 'text/json' })
105 | const url = window.URL.createObjectURL(blob)
106 | const a = document.createElement('a')
107 | a.setAttribute('hidden', '')
108 | a.setAttribute('href', url)
109 | a.setAttribute('download', 'shortenurl.json')
110 | document.body.appendChild(a)
111 | a.click()
112 | document.body.removeChild(a)
113 | }
114 |
115 |
116 | // Get custom domain
117 | const customDomain = () => {
118 | const form = document.getElementById('form')
119 | const formData = new FormData(form)
120 | const domain = formData.get('domain')
121 | if (domain !== "") {
122 | return domain
123 | } else {
124 | return null
125 | }
126 | }
127 |
128 | const randomBG = () => {
129 | const bgList = [
130 | "img/city.jpg",
131 | "img/cityNight.jpeg",
132 | "img/mountain.jpeg",
133 | ]
134 | return bgList[Math.floor(Math.random() * bgList.length)];
135 | }
136 |
137 | useEffect(() => {
138 | // Autogrow textarea
139 | const textarea = document.getElementById('textarea')
140 | textarea.addEventListener('input', () => {
141 | textarea.style.height = 'auto'
142 | textarea.style.height = textarea.scrollHeight + 'px'
143 | })
144 |
145 | const interval = setInterval(() => {
146 | // Fade in and out
147 | document.getElementById("bg").classList.add("opacity-0");
148 | setTimeout(() => {
149 | document.getElementById("bg").classList.remove("opacity-0");
150 | }, 1000);
151 |
152 | document.getElementById("bg").src = randomBG();
153 | }, 30000);
154 | return () => clearInterval(interval);
155 | }, [])
156 |
157 | return (
158 |
159 |
160 |
161 |
162 |
163 |
164 |
172 |
173 | {/* Headings */}
174 |
Magic Teleport
175 |
176 | An URL Shortener Solution.
177 |
178 |
179 | {/* Form */}
180 |
206 |
207 |
208 | {/* Results */}
209 |
210 |
211 |
212 | {results.length > 0 ? 'Here are your shortened URLs:' : 'Your shortened URLs will appear here.'}
213 |
214 |
215 | {results.map((result, index) => (
216 |
217 |
218 |
219 |
220 | {result.url.replaceAll('http://','').replaceAll('https://','')}
221 |
222 | |
223 |
224 | {/* domain */}
225 | {/*
226 | { (customDomain() ?? window.location.origin).replaceAll('http://','').replaceAll('https://','') }/
227 | */}
228 | {/* Preview */}
229 |
230 | {`${result.key}`}
231 |
232 |
233 | /
234 | {/* Copy btn */}
235 |
244 | |
245 |
246 | ))}
247 |
248 |
249 | {results.length > 0 && (
250 |
251 |
255 |
259 |
263 |
264 | )}
265 |
266 |
267 |
268 |
269 |
270 |
272 |
273 | )
274 | }
275 |
--------------------------------------------------------------------------------
/pages/unlock.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { useRouter } from 'next/router'
3 |
4 | export default function Unlock() {
5 | const router = useRouter()
6 |
7 | const randomBG = () => {
8 | const bgList = [
9 | "img/city.jpg",
10 | "img/cityNight.jpeg",
11 | "img/mountain.jpeg",
12 | ]
13 | return bgList[Math.floor(Math.random() * bgList.length)];
14 | }
15 |
16 | // handleSubmit redirect to /{key}${password}
17 | const handleSubmit = async () => {
18 | // Prevent
19 | event.preventDefault()
20 |
21 | // Get the form data
22 | const form = document.getElementById('form')
23 | const formData = new FormData(form)
24 | const key = router.query.key
25 |
26 | const password = formData.get('password')
27 |
28 | // redirect to /${key}$${password}
29 | window.location.href = `/${key}$${password}`
30 | }
31 |
32 | useEffect(() => {
33 | const interval = setInterval(() => {
34 | // Fade in and out
35 | document.getElementById("bg").classList.add("opacity-0");
36 | setTimeout(() => {
37 | document.getElementById("bg").classList.remove("opacity-0");
38 | }, 1000);
39 |
40 | document.getElementById("bg").src = randomBG();
41 | }, 30000);
42 | return () => clearInterval(interval);
43 | }, [])
44 |
45 | return (
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
62 |
63 | {/* Headings */}
64 |
65 |
Magic Teleport Lock
66 |
67 | This page is secured. Please enter the password to continue.
68 |
69 |
70 | {/* Form */}
71 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | )
89 | }
90 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/img/city.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1998code/shorten-url/dd19c13182e851384491d4756ff8d78124fff823/public/img/city.jpg
--------------------------------------------------------------------------------
/public/img/cityNight.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1998code/shorten-url/dd19c13182e851384491d4756ff8d78124fff823/public/img/cityNight.jpeg
--------------------------------------------------------------------------------
/public/img/mountain.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1998code/shorten-url/dd19c13182e851384491d4756ff8d78124fff823/public/img/mountain.jpeg
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --foreground-rgb: 0, 0, 0;
7 | --background-start-rgb: 214, 219, 220;
8 | --background-end-rgb: 255, 255, 255;
9 | }
10 |
11 | @media (prefers-color-scheme: dark) {
12 | :root {
13 | --foreground-rgb: 255, 255, 255;
14 | --background-start-rgb: 0, 0, 0;
15 | --background-end-rgb: 0, 0, 0;
16 | }
17 | }
18 |
19 | body {
20 | color: rgb(var(--foreground-rgb));
21 | background: linear-gradient(
22 | to bottom,
23 | transparent,
24 | rgb(var(--background-end-rgb))
25 | )
26 | rgb(var(--background-start-rgb));
27 | }
28 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './pages/**/*.{js,ts,jsx,tsx,mdx}',
5 | './components/**/*.{js,ts,jsx,tsx,mdx}',
6 | './app/**/*.{js,ts,jsx,tsx,mdx}',
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
12 | 'gradient-conic':
13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
14 | },
15 | },
16 | },
17 | plugins: [],
18 | }
19 |
--------------------------------------------------------------------------------