├── public └── videos │ └── sample.mp4 ├── next.config.ts ├── package.json ├── .gitignore ├── tsconfig.json ├── README.md ├── app └── convert │ └── route.ts └── pnpm-lock.yaml /public/videos/sample.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel-labs/ffmpeg-on-vercel/HEAD/public/videos/sample.mp4 -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | outputFileTracingIncludes: { 5 | "/app/convert": [ 6 | "./public/videos/sample.mp4", 7 | "./node_modules/ffmpeg-static/ffmpeg", 8 | ], 9 | }, 10 | }; 11 | 12 | export default nextConfig; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ffmpeg-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "ffmpeg-static": "^5.2.0", 12 | "next": "15.3.8" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "^20", 16 | "@types/react": "^19", 17 | "typescript": "^5" 18 | }, 19 | "packageManager": "pnpm@8.15.7+sha512.c85cd21b6da10332156b1ca2aa79c0a61ee7ad2eb0453b88ab299289e9e8ca93e6091232b25c07cbf61f6df77128d9c849e5c9ac6e44854dbd211c49f3a67adc" 20 | } 21 | -------------------------------------------------------------------------------- /.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # env files (can opt-in for commiting if needed) 33 | .env* 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFmpeg on Vercel 2 | 3 | [FFmpeg](https://ffmpeg.org/) runs natively on Vercel's Fluid compute without modifications. 4 | 5 | See [route.ts](https://github.com/vercel-labs/ffmpeg-demo/blob/main/app/convert/route.ts) for sample usage based on [`ffmpeg-static` package](https://www.npmjs.com/package/ffmpeg-static) from npm. 6 | 7 | Note, that you need to [declare the binary to be included in your function](https://github.com/vercel-labs/ffmpeg-demo/blob/main/next.config.ts#L7). 8 | 9 | ## Recommendation 10 | 11 | Since FFmpeg takes full advantaged of available cores, we recommend increasing CPU/RAM with a configuration like this. Because Vercel's cost increases linearly with CPU availability, this increases performance, yet can be essentially cost-neutral for a CPU-bound workload like FFmpeg. 12 | 13 | `[vercel.json]` 14 | 15 | ```json 16 | { 17 | "$schema": "https://openapi.vercel.sh/vercel.json", 18 | "functions": { 19 | "app/convert/route.ts": { 20 | "memory": 3009 21 | } 22 | } 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /app/convert/route.ts: -------------------------------------------------------------------------------- 1 | import ffmpeg from "ffmpeg-static"; 2 | import { spawn } from "child_process"; 3 | import fs from "fs/promises"; 4 | import path from "path"; 5 | import { NextRequest, NextResponse } from "next/server"; 6 | 7 | export const maxDuration = 300; 8 | 9 | interface QualitySettings { 10 | [key: string]: string[]; 11 | } 12 | 13 | export async function GET(request: NextRequest): Promise { 14 | const { searchParams } = new URL(request.url); 15 | const inputFile = "sample.mp4"; 16 | const format = searchParams.get("format") || "webm"; 17 | const quality = searchParams.get("quality") || "medium"; 18 | 19 | if (!inputFile) { 20 | return NextResponse.json( 21 | { error: "Input file parameter required" }, 22 | { status: 400 } 23 | ); 24 | } 25 | 26 | try { 27 | // Define paths - assuming file is in public/videos/ 28 | const inputPath = path.join(process.cwd(), "public", "videos", inputFile); 29 | 30 | // Check if input file exists. 31 | try { 32 | await fs.access(inputPath); 33 | } catch { 34 | return NextResponse.json( 35 | { error: "Input file not found" }, 36 | { status: 404 } 37 | ); 38 | } 39 | 40 | // Set quality presets 41 | const qualitySettings: QualitySettings = { 42 | low: ["-crf", "28", "-preset", "fast"], 43 | medium: ["-crf", "23", "-preset", "medium"], 44 | high: ["-crf", "18", "-preset", "slow"], 45 | }; 46 | 47 | const videoCodec = format === "webm" ? "libvpx-vp9" : "libx264"; 48 | const audioCodec = format === "webm" ? "libopus" : "aac"; 49 | const fastStart = format === "webm" ? [] : ["-movflags", "+faststart"]; 50 | 51 | // FFmpeg arguments for conversion 52 | const ffmpegArgs = [ 53 | "-i", 54 | inputPath, 55 | "-c:v", 56 | videoCodec, 57 | "-c:a", 58 | audioCodec, 59 | ...qualitySettings[quality], 60 | ...fastStart, 61 | "-f", 62 | format, 63 | "pipe:1", // pipe to stdout 64 | ]; 65 | 66 | // Run FFmpeg conversion 67 | const readableStream = new ReadableStream({ 68 | start(controller) { 69 | if (!ffmpeg) { 70 | controller.error(new Error("FFmpeg binary not found")); 71 | return; 72 | } 73 | 74 | const process = spawn("./node_modules/ffmpeg-static/ffmpeg", ffmpegArgs, { stdio: 'pipe'}); 75 | 76 | let stderr = ""; 77 | let isClosed = false; 78 | 79 | process.stdout.on("data", (data: Buffer) => { 80 | if (!isClosed) { 81 | try { 82 | controller.enqueue(new Uint8Array(data)); 83 | } catch (error) { 84 | // Controller might be closed, ignore the error 85 | } 86 | } 87 | }); 88 | 89 | process.stderr.on("data", (data: Buffer) => { 90 | stderr += data.toString(); 91 | }); 92 | 93 | process.on("close", (code: number | null) => { 94 | if (!isClosed) { 95 | isClosed = true; 96 | if (code === 0) { 97 | try { 98 | if (controller.desiredSize !== null) { 99 | controller.close(); 100 | } 101 | } catch (error) { 102 | // Controller already closed by client 103 | } 104 | } else { 105 | try { 106 | if (controller.desiredSize !== null) { 107 | controller.error(new Error(`FFmpeg failed with code ${code}: ${stderr}`)); 108 | } 109 | } catch (error) { 110 | // Controller already closed by client 111 | } 112 | } 113 | } 114 | }); 115 | 116 | process.on("error", (error: Error) => { 117 | if (!isClosed) { 118 | isClosed = true; 119 | try { 120 | if (controller.desiredSize !== null) { 121 | controller.error(error); 122 | } 123 | } catch (error) { 124 | // Controller already closed by client 125 | } 126 | } 127 | }); 128 | } 129 | }); 130 | 131 | // Set appropriate headers for streaming 132 | const headers = new Headers(); 133 | headers.set("Content-Type", `video/${format}`); 134 | 135 | return new NextResponse(readableStream, { 136 | status: 200, 137 | headers, 138 | }); 139 | } catch (error) { 140 | console.error("Video conversion error:", error); 141 | 142 | const errorMessage = 143 | error instanceof Error ? error.message : "Unknown error occurred"; 144 | 145 | return NextResponse.json( 146 | { 147 | error: "Video conversion failed", 148 | details: errorMessage, 149 | }, 150 | { status: 500 } 151 | ); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | ffmpeg-static: 9 | specifier: ^5.2.0 10 | version: 5.2.0 11 | next: 12 | specifier: 15.3.8 13 | version: 15.3.8(react-dom@19.1.0)(react@19.1.0) 14 | 15 | devDependencies: 16 | '@types/node': 17 | specifier: ^20 18 | version: 20.17.50 19 | '@types/react': 20 | specifier: ^19 21 | version: 19.1.5 22 | typescript: 23 | specifier: ^5 24 | version: 5.8.3 25 | 26 | packages: 27 | 28 | /@derhuerst/http-basic@8.2.4: 29 | resolution: {integrity: sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==} 30 | engines: {node: '>=6.0.0'} 31 | dependencies: 32 | caseless: 0.12.0 33 | concat-stream: 2.0.0 34 | http-response-object: 3.0.2 35 | parse-cache-control: 1.0.1 36 | dev: false 37 | 38 | /@emnapi/runtime@1.7.1: 39 | resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} 40 | requiresBuild: true 41 | dependencies: 42 | tslib: 2.8.1 43 | dev: false 44 | optional: true 45 | 46 | /@img/colour@1.0.0: 47 | resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} 48 | engines: {node: '>=18'} 49 | requiresBuild: true 50 | dev: false 51 | optional: true 52 | 53 | /@img/sharp-darwin-arm64@0.34.5: 54 | resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} 55 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 56 | cpu: [arm64] 57 | os: [darwin] 58 | requiresBuild: true 59 | optionalDependencies: 60 | '@img/sharp-libvips-darwin-arm64': 1.2.4 61 | dev: false 62 | optional: true 63 | 64 | /@img/sharp-darwin-x64@0.34.5: 65 | resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} 66 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 67 | cpu: [x64] 68 | os: [darwin] 69 | requiresBuild: true 70 | optionalDependencies: 71 | '@img/sharp-libvips-darwin-x64': 1.2.4 72 | dev: false 73 | optional: true 74 | 75 | /@img/sharp-libvips-darwin-arm64@1.2.4: 76 | resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} 77 | cpu: [arm64] 78 | os: [darwin] 79 | requiresBuild: true 80 | dev: false 81 | optional: true 82 | 83 | /@img/sharp-libvips-darwin-x64@1.2.4: 84 | resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} 85 | cpu: [x64] 86 | os: [darwin] 87 | requiresBuild: true 88 | dev: false 89 | optional: true 90 | 91 | /@img/sharp-libvips-linux-arm64@1.2.4: 92 | resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} 93 | cpu: [arm64] 94 | os: [linux] 95 | requiresBuild: true 96 | dev: false 97 | optional: true 98 | 99 | /@img/sharp-libvips-linux-arm@1.2.4: 100 | resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} 101 | cpu: [arm] 102 | os: [linux] 103 | requiresBuild: true 104 | dev: false 105 | optional: true 106 | 107 | /@img/sharp-libvips-linux-ppc64@1.2.4: 108 | resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} 109 | cpu: [ppc64] 110 | os: [linux] 111 | requiresBuild: true 112 | dev: false 113 | optional: true 114 | 115 | /@img/sharp-libvips-linux-riscv64@1.2.4: 116 | resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} 117 | cpu: [riscv64] 118 | os: [linux] 119 | requiresBuild: true 120 | dev: false 121 | optional: true 122 | 123 | /@img/sharp-libvips-linux-s390x@1.2.4: 124 | resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} 125 | cpu: [s390x] 126 | os: [linux] 127 | requiresBuild: true 128 | dev: false 129 | optional: true 130 | 131 | /@img/sharp-libvips-linux-x64@1.2.4: 132 | resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} 133 | cpu: [x64] 134 | os: [linux] 135 | requiresBuild: true 136 | dev: false 137 | optional: true 138 | 139 | /@img/sharp-libvips-linuxmusl-arm64@1.2.4: 140 | resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} 141 | cpu: [arm64] 142 | os: [linux] 143 | requiresBuild: true 144 | dev: false 145 | optional: true 146 | 147 | /@img/sharp-libvips-linuxmusl-x64@1.2.4: 148 | resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} 149 | cpu: [x64] 150 | os: [linux] 151 | requiresBuild: true 152 | dev: false 153 | optional: true 154 | 155 | /@img/sharp-linux-arm64@0.34.5: 156 | resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} 157 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 158 | cpu: [arm64] 159 | os: [linux] 160 | requiresBuild: true 161 | optionalDependencies: 162 | '@img/sharp-libvips-linux-arm64': 1.2.4 163 | dev: false 164 | optional: true 165 | 166 | /@img/sharp-linux-arm@0.34.5: 167 | resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} 168 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 169 | cpu: [arm] 170 | os: [linux] 171 | requiresBuild: true 172 | optionalDependencies: 173 | '@img/sharp-libvips-linux-arm': 1.2.4 174 | dev: false 175 | optional: true 176 | 177 | /@img/sharp-linux-ppc64@0.34.5: 178 | resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} 179 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 180 | cpu: [ppc64] 181 | os: [linux] 182 | requiresBuild: true 183 | optionalDependencies: 184 | '@img/sharp-libvips-linux-ppc64': 1.2.4 185 | dev: false 186 | optional: true 187 | 188 | /@img/sharp-linux-riscv64@0.34.5: 189 | resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} 190 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 191 | cpu: [riscv64] 192 | os: [linux] 193 | requiresBuild: true 194 | optionalDependencies: 195 | '@img/sharp-libvips-linux-riscv64': 1.2.4 196 | dev: false 197 | optional: true 198 | 199 | /@img/sharp-linux-s390x@0.34.5: 200 | resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} 201 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 202 | cpu: [s390x] 203 | os: [linux] 204 | requiresBuild: true 205 | optionalDependencies: 206 | '@img/sharp-libvips-linux-s390x': 1.2.4 207 | dev: false 208 | optional: true 209 | 210 | /@img/sharp-linux-x64@0.34.5: 211 | resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} 212 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 213 | cpu: [x64] 214 | os: [linux] 215 | requiresBuild: true 216 | optionalDependencies: 217 | '@img/sharp-libvips-linux-x64': 1.2.4 218 | dev: false 219 | optional: true 220 | 221 | /@img/sharp-linuxmusl-arm64@0.34.5: 222 | resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} 223 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 224 | cpu: [arm64] 225 | os: [linux] 226 | requiresBuild: true 227 | optionalDependencies: 228 | '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 229 | dev: false 230 | optional: true 231 | 232 | /@img/sharp-linuxmusl-x64@0.34.5: 233 | resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} 234 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 235 | cpu: [x64] 236 | os: [linux] 237 | requiresBuild: true 238 | optionalDependencies: 239 | '@img/sharp-libvips-linuxmusl-x64': 1.2.4 240 | dev: false 241 | optional: true 242 | 243 | /@img/sharp-wasm32@0.34.5: 244 | resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} 245 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 246 | cpu: [wasm32] 247 | requiresBuild: true 248 | dependencies: 249 | '@emnapi/runtime': 1.7.1 250 | dev: false 251 | optional: true 252 | 253 | /@img/sharp-win32-arm64@0.34.5: 254 | resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} 255 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 256 | cpu: [arm64] 257 | os: [win32] 258 | requiresBuild: true 259 | dev: false 260 | optional: true 261 | 262 | /@img/sharp-win32-ia32@0.34.5: 263 | resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} 264 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 265 | cpu: [ia32] 266 | os: [win32] 267 | requiresBuild: true 268 | dev: false 269 | optional: true 270 | 271 | /@img/sharp-win32-x64@0.34.5: 272 | resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} 273 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 274 | cpu: [x64] 275 | os: [win32] 276 | requiresBuild: true 277 | dev: false 278 | optional: true 279 | 280 | /@next/env@15.3.8: 281 | resolution: {integrity: sha512-SAfHg0g91MQVMPioeFeDjE+8UPF3j3BvHjs8ZKJAUz1BG7eMPvfCKOAgNWJ6s1MLNeP6O2InKQRTNblxPWuq+Q==} 282 | dev: false 283 | 284 | /@next/swc-darwin-arm64@15.3.5: 285 | resolution: {integrity: sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w==} 286 | engines: {node: '>= 10'} 287 | cpu: [arm64] 288 | os: [darwin] 289 | requiresBuild: true 290 | dev: false 291 | optional: true 292 | 293 | /@next/swc-darwin-x64@15.3.5: 294 | resolution: {integrity: sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA==} 295 | engines: {node: '>= 10'} 296 | cpu: [x64] 297 | os: [darwin] 298 | requiresBuild: true 299 | dev: false 300 | optional: true 301 | 302 | /@next/swc-linux-arm64-gnu@15.3.5: 303 | resolution: {integrity: sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ==} 304 | engines: {node: '>= 10'} 305 | cpu: [arm64] 306 | os: [linux] 307 | requiresBuild: true 308 | dev: false 309 | optional: true 310 | 311 | /@next/swc-linux-arm64-musl@15.3.5: 312 | resolution: {integrity: sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A==} 313 | engines: {node: '>= 10'} 314 | cpu: [arm64] 315 | os: [linux] 316 | requiresBuild: true 317 | dev: false 318 | optional: true 319 | 320 | /@next/swc-linux-x64-gnu@15.3.5: 321 | resolution: {integrity: sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A==} 322 | engines: {node: '>= 10'} 323 | cpu: [x64] 324 | os: [linux] 325 | requiresBuild: true 326 | dev: false 327 | optional: true 328 | 329 | /@next/swc-linux-x64-musl@15.3.5: 330 | resolution: {integrity: sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg==} 331 | engines: {node: '>= 10'} 332 | cpu: [x64] 333 | os: [linux] 334 | requiresBuild: true 335 | dev: false 336 | optional: true 337 | 338 | /@next/swc-win32-arm64-msvc@15.3.5: 339 | resolution: {integrity: sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ==} 340 | engines: {node: '>= 10'} 341 | cpu: [arm64] 342 | os: [win32] 343 | requiresBuild: true 344 | dev: false 345 | optional: true 346 | 347 | /@next/swc-win32-x64-msvc@15.3.5: 348 | resolution: {integrity: sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw==} 349 | engines: {node: '>= 10'} 350 | cpu: [x64] 351 | os: [win32] 352 | requiresBuild: true 353 | dev: false 354 | optional: true 355 | 356 | /@swc/counter@0.1.3: 357 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 358 | dev: false 359 | 360 | /@swc/helpers@0.5.15: 361 | resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} 362 | dependencies: 363 | tslib: 2.8.1 364 | dev: false 365 | 366 | /@types/node@10.17.60: 367 | resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} 368 | dev: false 369 | 370 | /@types/node@20.17.50: 371 | resolution: {integrity: sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A==} 372 | dependencies: 373 | undici-types: 6.19.8 374 | dev: true 375 | 376 | /@types/react@19.1.5: 377 | resolution: {integrity: sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==} 378 | dependencies: 379 | csstype: 3.1.3 380 | dev: true 381 | 382 | /agent-base@6.0.2: 383 | resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} 384 | engines: {node: '>= 6.0.0'} 385 | dependencies: 386 | debug: 4.4.1 387 | transitivePeerDependencies: 388 | - supports-color 389 | dev: false 390 | 391 | /buffer-from@1.1.2: 392 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 393 | dev: false 394 | 395 | /busboy@1.6.0: 396 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 397 | engines: {node: '>=10.16.0'} 398 | dependencies: 399 | streamsearch: 1.1.0 400 | dev: false 401 | 402 | /caniuse-lite@1.0.30001718: 403 | resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} 404 | dev: false 405 | 406 | /caseless@0.12.0: 407 | resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} 408 | dev: false 409 | 410 | /client-only@0.0.1: 411 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 412 | dev: false 413 | 414 | /concat-stream@2.0.0: 415 | resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} 416 | engines: {'0': node >= 6.0} 417 | dependencies: 418 | buffer-from: 1.1.2 419 | inherits: 2.0.4 420 | readable-stream: 3.6.2 421 | typedarray: 0.0.6 422 | dev: false 423 | 424 | /csstype@3.1.3: 425 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 426 | dev: true 427 | 428 | /debug@4.4.1: 429 | resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 430 | engines: {node: '>=6.0'} 431 | peerDependencies: 432 | supports-color: '*' 433 | peerDependenciesMeta: 434 | supports-color: 435 | optional: true 436 | dependencies: 437 | ms: 2.1.3 438 | dev: false 439 | 440 | /detect-libc@2.1.2: 441 | resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} 442 | engines: {node: '>=8'} 443 | requiresBuild: true 444 | dev: false 445 | optional: true 446 | 447 | /env-paths@2.2.1: 448 | resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} 449 | engines: {node: '>=6'} 450 | dev: false 451 | 452 | /ffmpeg-static@5.2.0: 453 | resolution: {integrity: sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==} 454 | engines: {node: '>=16'} 455 | requiresBuild: true 456 | dependencies: 457 | '@derhuerst/http-basic': 8.2.4 458 | env-paths: 2.2.1 459 | https-proxy-agent: 5.0.1 460 | progress: 2.0.3 461 | transitivePeerDependencies: 462 | - supports-color 463 | dev: false 464 | 465 | /http-response-object@3.0.2: 466 | resolution: {integrity: sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==} 467 | dependencies: 468 | '@types/node': 10.17.60 469 | dev: false 470 | 471 | /https-proxy-agent@5.0.1: 472 | resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} 473 | engines: {node: '>= 6'} 474 | dependencies: 475 | agent-base: 6.0.2 476 | debug: 4.4.1 477 | transitivePeerDependencies: 478 | - supports-color 479 | dev: false 480 | 481 | /inherits@2.0.4: 482 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 483 | dev: false 484 | 485 | /ms@2.1.3: 486 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 487 | dev: false 488 | 489 | /nanoid@3.3.11: 490 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 491 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 492 | hasBin: true 493 | dev: false 494 | 495 | /next@15.3.8(react-dom@19.1.0)(react@19.1.0): 496 | resolution: {integrity: sha512-L+4c5Hlr84fuaNADZbB9+ceRX9/CzwxJ+obXIGHupboB/Q1OLbSUapFs4bO8hnS/E6zV/JDX7sG1QpKVR2bguA==} 497 | engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} 498 | hasBin: true 499 | peerDependencies: 500 | '@opentelemetry/api': ^1.1.0 501 | '@playwright/test': ^1.41.2 502 | babel-plugin-react-compiler: '*' 503 | react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 504 | react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 505 | sass: ^1.3.0 506 | peerDependenciesMeta: 507 | '@opentelemetry/api': 508 | optional: true 509 | '@playwright/test': 510 | optional: true 511 | babel-plugin-react-compiler: 512 | optional: true 513 | sass: 514 | optional: true 515 | dependencies: 516 | '@next/env': 15.3.8 517 | '@swc/counter': 0.1.3 518 | '@swc/helpers': 0.5.15 519 | busboy: 1.6.0 520 | caniuse-lite: 1.0.30001718 521 | postcss: 8.4.31 522 | react: 19.1.0 523 | react-dom: 19.1.0(react@19.1.0) 524 | styled-jsx: 5.1.6(react@19.1.0) 525 | optionalDependencies: 526 | '@next/swc-darwin-arm64': 15.3.5 527 | '@next/swc-darwin-x64': 15.3.5 528 | '@next/swc-linux-arm64-gnu': 15.3.5 529 | '@next/swc-linux-arm64-musl': 15.3.5 530 | '@next/swc-linux-x64-gnu': 15.3.5 531 | '@next/swc-linux-x64-musl': 15.3.5 532 | '@next/swc-win32-arm64-msvc': 15.3.5 533 | '@next/swc-win32-x64-msvc': 15.3.5 534 | sharp: 0.34.5 535 | transitivePeerDependencies: 536 | - '@babel/core' 537 | - babel-plugin-macros 538 | dev: false 539 | 540 | /parse-cache-control@1.0.1: 541 | resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} 542 | dev: false 543 | 544 | /picocolors@1.1.1: 545 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 546 | dev: false 547 | 548 | /postcss@8.4.31: 549 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 550 | engines: {node: ^10 || ^12 || >=14} 551 | dependencies: 552 | nanoid: 3.3.11 553 | picocolors: 1.1.1 554 | source-map-js: 1.2.1 555 | dev: false 556 | 557 | /progress@2.0.3: 558 | resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} 559 | engines: {node: '>=0.4.0'} 560 | dev: false 561 | 562 | /react-dom@19.1.0(react@19.1.0): 563 | resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} 564 | peerDependencies: 565 | react: ^19.1.0 566 | dependencies: 567 | react: 19.1.0 568 | scheduler: 0.26.0 569 | dev: false 570 | 571 | /react@19.1.0: 572 | resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} 573 | engines: {node: '>=0.10.0'} 574 | dev: false 575 | 576 | /readable-stream@3.6.2: 577 | resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} 578 | engines: {node: '>= 6'} 579 | dependencies: 580 | inherits: 2.0.4 581 | string_decoder: 1.3.0 582 | util-deprecate: 1.0.2 583 | dev: false 584 | 585 | /safe-buffer@5.2.1: 586 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 587 | dev: false 588 | 589 | /scheduler@0.26.0: 590 | resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} 591 | dev: false 592 | 593 | /semver@7.7.3: 594 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 595 | engines: {node: '>=10'} 596 | hasBin: true 597 | requiresBuild: true 598 | dev: false 599 | optional: true 600 | 601 | /sharp@0.34.5: 602 | resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} 603 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 604 | requiresBuild: true 605 | dependencies: 606 | '@img/colour': 1.0.0 607 | detect-libc: 2.1.2 608 | semver: 7.7.3 609 | optionalDependencies: 610 | '@img/sharp-darwin-arm64': 0.34.5 611 | '@img/sharp-darwin-x64': 0.34.5 612 | '@img/sharp-libvips-darwin-arm64': 1.2.4 613 | '@img/sharp-libvips-darwin-x64': 1.2.4 614 | '@img/sharp-libvips-linux-arm': 1.2.4 615 | '@img/sharp-libvips-linux-arm64': 1.2.4 616 | '@img/sharp-libvips-linux-ppc64': 1.2.4 617 | '@img/sharp-libvips-linux-riscv64': 1.2.4 618 | '@img/sharp-libvips-linux-s390x': 1.2.4 619 | '@img/sharp-libvips-linux-x64': 1.2.4 620 | '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 621 | '@img/sharp-libvips-linuxmusl-x64': 1.2.4 622 | '@img/sharp-linux-arm': 0.34.5 623 | '@img/sharp-linux-arm64': 0.34.5 624 | '@img/sharp-linux-ppc64': 0.34.5 625 | '@img/sharp-linux-riscv64': 0.34.5 626 | '@img/sharp-linux-s390x': 0.34.5 627 | '@img/sharp-linux-x64': 0.34.5 628 | '@img/sharp-linuxmusl-arm64': 0.34.5 629 | '@img/sharp-linuxmusl-x64': 0.34.5 630 | '@img/sharp-wasm32': 0.34.5 631 | '@img/sharp-win32-arm64': 0.34.5 632 | '@img/sharp-win32-ia32': 0.34.5 633 | '@img/sharp-win32-x64': 0.34.5 634 | dev: false 635 | optional: true 636 | 637 | /source-map-js@1.2.1: 638 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 639 | engines: {node: '>=0.10.0'} 640 | dev: false 641 | 642 | /streamsearch@1.1.0: 643 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 644 | engines: {node: '>=10.0.0'} 645 | dev: false 646 | 647 | /string_decoder@1.3.0: 648 | resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 649 | dependencies: 650 | safe-buffer: 5.2.1 651 | dev: false 652 | 653 | /styled-jsx@5.1.6(react@19.1.0): 654 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 655 | engines: {node: '>= 12.0.0'} 656 | peerDependencies: 657 | '@babel/core': '*' 658 | babel-plugin-macros: '*' 659 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 660 | peerDependenciesMeta: 661 | '@babel/core': 662 | optional: true 663 | babel-plugin-macros: 664 | optional: true 665 | dependencies: 666 | client-only: 0.0.1 667 | react: 19.1.0 668 | dev: false 669 | 670 | /tslib@2.8.1: 671 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 672 | dev: false 673 | 674 | /typedarray@0.0.6: 675 | resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} 676 | dev: false 677 | 678 | /typescript@5.8.3: 679 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 680 | engines: {node: '>=14.17'} 681 | hasBin: true 682 | dev: true 683 | 684 | /undici-types@6.19.8: 685 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 686 | dev: true 687 | 688 | /util-deprecate@1.0.2: 689 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 690 | dev: false 691 | --------------------------------------------------------------------------------