├── .dockerignore ├── hosts ├── nginx ├── certs │ ├── example │ │ ├── live │ │ │ ├── local.mproske.com │ │ │ │ ├── cert.pem │ │ │ │ ├── chain.pem │ │ │ │ ├── privkey.pem │ │ │ │ ├── fullchain.pem │ │ │ │ └── README │ │ │ └── README │ │ ├── archive │ │ │ └── local.mproske.com │ │ │ │ ├── privkey1.pem │ │ │ │ ├── cert1.pem │ │ │ │ ├── chain1.pem │ │ │ │ └── fullchain1.pem │ │ └── dhparam-2048.pem │ ├── README.md │ ├── get-wildcard-cert.sh │ └── get-wildcard-cert-windows.sh ├── .gitignore ├── Dockerfile ├── nginx.conf ├── ssl.conf ├── headers.conf └── sites-enabled │ └── default.conf ├── next-js-app ├── jsconfig.json ├── public │ ├── favicon.ico │ ├── img │ │ ├── fastblob.gif │ │ └── slowblob.gif │ ├── vercel.svg │ ├── thirteen.svg │ └── next.svg ├── src │ ├── pages │ │ ├── _app.js │ │ ├── api │ │ │ └── hello.js │ │ ├── _document.js │ │ └── index.js │ ├── lib │ │ └── postgres.js │ ├── components │ │ └── Blob.js │ └── styles │ │ ├── globals.css │ │ └── Home.module.css ├── .gitignore ├── next.config.js ├── package.json ├── dev.Dockerfile ├── prod-without-multistage.Dockerfile ├── prod.Dockerfile └── pnpm-lock.yaml ├── next-ts-app ├── public │ ├── favicon.ico │ ├── img │ │ ├── fastblob.gif │ │ └── slowblob.gif │ ├── vercel.svg │ ├── thirteen.svg │ └── next.svg ├── src │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── api │ │ │ └── hello.ts │ │ └── index.tsx │ ├── lib │ │ └── postgres.ts │ ├── components │ │ └── Blob.tsx │ └── styles │ │ ├── globals.css │ │ └── Home.module.css ├── package.json ├── next.config.js ├── .gitignore ├── tsconfig.json ├── dev.Dockerfile ├── prod-without-multistage.Dockerfile ├── prod.Dockerfile └── pnpm-lock.yaml ├── .vscode ├── extensions.json └── settings.json ├── postgres ├── dev.Dockerfile └── data.sql ├── .gitignore ├── prod.sh ├── prod-without-multistage.sh ├── dev.sh ├── .env ├── package.json ├── eslint.config.mjs ├── docker-compose.prod.yml ├── docker-compose.prod-without-multistage.yml ├── docker-compose.dev.yml └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | 127.0.0.1 local.mproske.com 2 | -------------------------------------------------------------------------------- /nginx/certs/example/live/local.mproske.com/cert.pem: -------------------------------------------------------------------------------- 1 | ../../archive/local.mproske.com/cert1.pem -------------------------------------------------------------------------------- /next-js-app/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "." 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /nginx/certs/example/live/local.mproske.com/chain.pem: -------------------------------------------------------------------------------- 1 | ../../archive/local.mproske.com/chain1.pem -------------------------------------------------------------------------------- /nginx/certs/example/live/local.mproske.com/privkey.pem: -------------------------------------------------------------------------------- 1 | ../../archive/local.mproske.com/privkey1.pem -------------------------------------------------------------------------------- /nginx/certs/example/live/local.mproske.com/fullchain.pem: -------------------------------------------------------------------------------- 1 | ../../archive/local.mproske.com/fullchain1.pem -------------------------------------------------------------------------------- /next-js-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxproske/nextjs-docker-production-kit/HEAD/next-js-app/public/favicon.ico -------------------------------------------------------------------------------- /next-ts-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxproske/nextjs-docker-production-kit/HEAD/next-ts-app/public/favicon.ico -------------------------------------------------------------------------------- /next-js-app/public/img/fastblob.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxproske/nextjs-docker-production-kit/HEAD/next-js-app/public/img/fastblob.gif -------------------------------------------------------------------------------- /next-js-app/public/img/slowblob.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxproske/nextjs-docker-production-kit/HEAD/next-js-app/public/img/slowblob.gif -------------------------------------------------------------------------------- /next-ts-app/public/img/fastblob.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxproske/nextjs-docker-production-kit/HEAD/next-ts-app/public/img/fastblob.gif -------------------------------------------------------------------------------- /next-ts-app/public/img/slowblob.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxproske/nextjs-docker-production-kit/HEAD/next-ts-app/public/img/slowblob.gif -------------------------------------------------------------------------------- /next-js-app/src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | export default function App({ Component, pageProps }) { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | // Nginx formatting 4 | "raynigon.nginx-formatter", 5 | // React formatting 6 | "dbaeumer.vscode-eslint", 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /postgres/dev.Dockerfile: -------------------------------------------------------------------------------- 1 | # Target your exact production database version 2 | FROM postgres:17-alpine 3 | 4 | # Seed database with placeholder data 5 | COPY *.sql /docker-entrypoint-initdb.d/ 6 | -------------------------------------------------------------------------------- /next-js-app/src/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /next-ts-app/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import type { AppProps } from 'next/app' 3 | 4 | export default function App({ Component, pageProps }: AppProps) { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # local env files 7 | .env.local 8 | .env.development.local 9 | .env.test.local 10 | .env.production.local 11 | -------------------------------------------------------------------------------- /nginx/certs/README.md: -------------------------------------------------------------------------------- 1 | Generate a wildcard SSL certificate for `your-domain.com` with LetsEncrypt: 2 | 3 | - macOS/Linux: Run `./get-wildcard-cert.sh your-domain.com` 4 | - Windows: Run `./get-wildcard-cert-windows.sh your-domain.com` 5 | 6 | Follow the instructions to add a TXT record to your DNS Zone file. 7 | -------------------------------------------------------------------------------- /nginx/certs/example/archive/local.mproske.com/privkey1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgvZ9H7PNPUKE9GI6y 3 | Rww6RKO8NxyIUHkIUnoU+QmHWR2hRANCAASMVf7jSvFLdpoK3W7tpt1YEM2W+t9o 4 | D9K/0hPvc6U/Gp5o9FZKWfvG91F3k8igaYQ//jXOlsebrbSN5cz9rxKv 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /next-js-app/src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /next-ts-app/src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /nginx/certs/get-wildcard-cert.sh: -------------------------------------------------------------------------------- 1 | docker run -it --rm \ 2 | -v "$PWD":/etc/letsencrypt \ 3 | certbot/certbot \ 4 | certonly --manual \ 5 | --preferred-challenges=dns \ 6 | --server https://acme-v02.api.letsencrypt.org/directory \ 7 | --email example@example.com \ 8 | --agree-tos \ 9 | -d *.$1 -d $1 10 | -------------------------------------------------------------------------------- /prod.sh: -------------------------------------------------------------------------------- 1 | # Create a network, which allows containers to communicate 2 | # with each other, by using their container name as a hostname 3 | docker network create my_network 4 | 5 | # Build prod 6 | docker compose -f docker-compose.prod.yml build 7 | 8 | # Up prod in detached mode 9 | docker compose -f docker-compose.prod.yml up -d 10 | -------------------------------------------------------------------------------- /next-ts-app/src/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler(_req: NextApiRequest, res: NextApiResponse) { 9 | res.status(200).json({ name: 'John Doe' }) 10 | } 11 | -------------------------------------------------------------------------------- /nginx/certs/get-wildcard-cert-windows.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | winpty docker run -it --rm \ 4 | -v /"$(pwd)":/etc/letsencrypt \ 5 | certbot/certbot \ 6 | certonly --manual \ 7 | --preferred-challenges=dns \ 8 | --server https://acme-v02.api.letsencrypt.org/directory \ 9 | --email example@example.com \ 10 | --agree-tos \ 11 | -d *.$1 -d $1 12 | -------------------------------------------------------------------------------- /nginx/.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | 3 | # All except the example cert are ignored from version control 4 | # If you wish to add your certs to version control, change your 5 | # repo to private before removing these lines: 6 | certs/* 7 | !certs/*.sh 8 | !certs/*.md 9 | !certs/example 10 | certs/example/accounts 11 | certs/example/csr 12 | certs/example/keys 13 | certs/example/renewal 14 | certs/example/renewal-hooks 15 | -------------------------------------------------------------------------------- /prod-without-multistage.sh: -------------------------------------------------------------------------------- 1 | # Create a network, which allows containers to communicate 2 | # with each other, by using their container name as a hostname 3 | docker network create my_network 4 | 5 | # Build prod without multistage 6 | docker compose -f docker-compose.prod-without-multistage.yml build 7 | 8 | # Up prod without multistage in detached mode 9 | docker compose -f docker-compose.prod-without-multistage.yml up -d 10 | -------------------------------------------------------------------------------- /next-js-app/src/lib/postgres.js: -------------------------------------------------------------------------------- 1 | import postgres from 'postgres' 2 | 3 | const sql = postgres({ 4 | host: process.env.POSTGRES_HOST, 5 | port: Number(process.env.POSTGRES_PORT), 6 | database: process.env.POSTGRES_DATABASE, 7 | username: process.env.POSTGRES_USER, 8 | password: process.env.POSTGRES_PASSWORD, 9 | // Transform the column names only from camel case 10 | transform: postgres.camel, 11 | }) 12 | 13 | export default sql 14 | -------------------------------------------------------------------------------- /next-ts-app/src/lib/postgres.ts: -------------------------------------------------------------------------------- 1 | import postgres from 'postgres' 2 | 3 | const sql = postgres({ 4 | host: process.env.POSTGRES_HOST, 5 | port: Number(process.env.POSTGRES_PORT), 6 | database: process.env.POSTGRES_DATABASE, 7 | username: process.env.POSTGRES_USER, 8 | password: process.env.POSTGRES_PASSWORD, 9 | // Transform the column names only from camel case 10 | transform: postgres.camel, 11 | }) 12 | 13 | export default sql 14 | -------------------------------------------------------------------------------- /nginx/certs/example/dhparam-2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz 3 | +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a 4 | 87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 5 | YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi 6 | 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD 7 | ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== 8 | -----END DH PARAMETERS----- -------------------------------------------------------------------------------- /next-js-app/.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 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /next-ts-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev --turbo", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "next": "^15.0.3", 10 | "postgres": "^3.4.5", 11 | "react": "^18.3.1", 12 | "react-dom": "^18.3.1" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "^22.9.0", 16 | "@types/react": "^18.3.12", 17 | "@types/react-dom": "^18.3.1", 18 | "typescript": "^5.6.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | # Stop all running containers 2 | docker kill $(docker ps -aq) && docker rm $(docker ps -aq) 3 | 4 | # Create a network, which allows containers to communicate 5 | # with each other, by using their container name as a hostname 6 | docker network create my_network 7 | 8 | # Build dev 9 | docker compose -f docker-compose.dev.yml build 10 | 11 | # Up dev 12 | # --renew-anon-volumes 13 | # postgres/mysql retrieve volumes from previous containers after being killed 14 | docker compose -f docker-compose.dev.yml up --renew-anon-volumes 15 | -------------------------------------------------------------------------------- /next-ts-app/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | output: 'standalone', 5 | basePath: '', 6 | webpack(config, { dev, isServer }) { 7 | // Required for Hot Reloading on Windows 8 | // https://github.com/vercel/next.js/issues/6417 9 | if (dev && !isServer) { 10 | config.watchOptions = { 11 | poll: 1000, 12 | aggregateTimeout: 300, 13 | } 14 | } 15 | return config 16 | }, 17 | } 18 | 19 | module.exports = nextConfig 20 | -------------------------------------------------------------------------------- /next-js-app/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | output: 'standalone', 5 | basePath: '/js', 6 | webpack(config, { dev, isServer }) { 7 | // Required for Hot Reloading on Windows 8 | // https://github.com/vercel/next.js/issues/6417 9 | if (dev && !isServer) { 10 | config.watchOptions = { 11 | poll: 1000, 12 | aggregateTimeout: 300, 13 | } 14 | } 15 | return config 16 | }, 17 | } 18 | 19 | module.exports = nextConfig 20 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # DO NOT ADD SECRETS TO THIS FILE. This is a good place for defaults. 2 | # If you want to add secrets use any of the following filenames instead, 3 | # which are automatically detected by Docker Compose: 4 | # .env.local 5 | # .env.development.local 6 | # .env.test.local 7 | # .env.production.local 8 | 9 | ENV_VARIABLE=production_server_only_variable 10 | NEXT_PUBLIC_ENV_VARIABLE=production_public_variable 11 | 12 | POSTGRES_HOST=postgres 13 | POSTGRES_PORT=5432 14 | POSTGRES_DATABASE=saas 15 | POSTGRES_USER=saas_user 16 | POSTGRES_PASSWORD=password 17 | -------------------------------------------------------------------------------- /next-ts-app/.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 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /next-js-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev --turbo", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "next": "^15.0.3", 10 | "postgres": "^3.4.5", 11 | "react": "^18.3.1", 12 | "react-dom": "^18.3.1" 13 | }, 14 | "devDependencies": { 15 | "@eslint/compat": "^1.2.3", 16 | "@eslint/js": "^9.15.0", 17 | "@types/eslint__js": "^8.42.3", 18 | "eslint": "^9.15.0", 19 | "eslint-plugin-react-hooks": "^5.0.0", 20 | "typescript": "^5.6.3", 21 | "typescript-eslint": "^8.14.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /next-js-app/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-ts-app/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.23 2 | 3 | WORKDIR /etc/nginx 4 | 5 | # Module for hiding headers 6 | RUN apt-get update && apt-get install -y nginx-extras 7 | 8 | # Copy config files 9 | COPY nginx.conf . 10 | COPY headers.conf . 11 | COPY ssl.conf . 12 | 13 | # Copy SSL certs 14 | COPY certs/example/live/local.mproske.com/fullchain.pem ./certs/local.mproske.com/fullchain.pem 15 | COPY certs/example/live/local.mproske.com/privkey.pem ./certs/local.mproske.com/privkey.pem 16 | COPY certs/example/dhparam-2048.pem ./certs/dhparam-2048.pem 17 | 18 | # Copy sites files 19 | COPY sites-enabled/ ./ 20 | 21 | # Create new files as www-data, not root 22 | RUN usermod -u 1000 www-data 23 | 24 | CMD nginx 25 | -------------------------------------------------------------------------------- /next-js-app/src/components/Blob.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import Image from 'next/image' 3 | import styles from '../styles/Home.module.css' 4 | import { useRouter } from 'next/router' 5 | 6 | export default function Blob() { 7 | const [isFast, setIsFast] = useState(false) 8 | const { basePath } = useRouter() 9 | 10 | return ( 11 |
setIsFast((prev) => !prev)}> 12 | blob 20 |
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /next-ts-app/src/components/Blob.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import Image from 'next/image' 3 | import styles from '../styles/Home.module.css' 4 | import { useRouter } from 'next/router' 5 | 6 | export default function Blob() { 7 | const [isFast, setIsFast] = useState(false) 8 | const { basePath } = useRouter() 9 | 10 | return ( 11 |
setIsFast((prev) => !prev)}> 12 | blob 20 |
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /next-ts-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "baseUrl": ".", 18 | "plugins": [ 19 | { 20 | "name": "next" 21 | } 22 | ] 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /nginx/certs/example/live/local.mproske.com/README: -------------------------------------------------------------------------------- 1 | This directory contains your keys and certificates. 2 | 3 | `privkey.pem` : the private key for your certificate. 4 | `fullchain.pem`: the certificate file used in most server software. 5 | `chain.pem` : used for OCSP stapling in Nginx >=1.3.7. 6 | `cert.pem` : will break many server configurations, and should not be used 7 | without reading further documentation (see link below). 8 | 9 | WARNING: DO NOT MOVE OR RENAME THESE FILES! 10 | Certbot expects these files to remain in this location in order 11 | to function properly! 12 | 13 | We recommend not moving these files. For more information, see the Certbot 14 | User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates. 15 | -------------------------------------------------------------------------------- /nginx/certs/example/live/README: -------------------------------------------------------------------------------- 1 | This directory contains your keys and certificates. 2 | 3 | `[cert name]/privkey.pem` : the private key for your certificate. 4 | `[cert name]/fullchain.pem`: the certificate file used in most server software. 5 | `[cert name]/chain.pem` : used for OCSP stapling in Nginx >=1.3.7. 6 | `[cert name]/cert.pem` : will break many server configurations, and should not be used 7 | without reading further documentation (see link below). 8 | 9 | WARNING: DO NOT MOVE OR RENAME THESE FILES! 10 | Certbot expects these files to remain in this location in order 11 | to function properly! 12 | 13 | We recommend not moving these files. For more information, see the Certbot 14 | User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates. 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-docker-production-kit", 3 | "version": "1.0.0", 4 | "repository": "https://github.com/maxproske/nextjs-docker-production-kit.git", 5 | "author": "Max Proske ", 6 | "license": "MIT", 7 | "devDependencies": { 8 | "@eslint/eslintrc": "^3.2.0", 9 | "@eslint/js": "^9.15.0", 10 | "@types/node": "^22.9.0", 11 | "@types/react": "^18.3.12", 12 | "@types/react-dom": "^18.3.1", 13 | "@typescript-eslint/eslint-plugin": "^8.14.0", 14 | "@typescript-eslint/parser": "^8.14.0", 15 | "eslint": "^9.15.0", 16 | "eslint-config-next": "^15.0.3", 17 | "eslint-config-prettier": "^9.1.0", 18 | "eslint-plugin-prettier": "^5.2.1", 19 | "eslint-plugin-react": "^7.37.2", 20 | "eslint-plugin-react-hooks": "^5.0.0", 21 | "next": "^15.0.3", 22 | "prettier": "^3.3.3", 23 | "react": "^18.3.1", 24 | "react-dom": "^18.3.1", 25 | "typescript": "^5.6.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | import js from '@eslint/js' 4 | import { FlatCompat } from '@eslint/eslintrc' 5 | 6 | const __filename = fileURLToPath(import.meta.url) 7 | const __dirname = path.dirname(__filename) 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | recommendedConfig: js.configs.recommended, 11 | allConfig: js.configs.all, 12 | }) 13 | 14 | export default [ 15 | ...compat.extends('eslint:recommended', 'next', 'plugin:prettier/recommended'), 16 | { 17 | rules: { 18 | 'react/react-in-jsx-scope': 'off', 19 | 'prettier/prettier': [ 20 | 'error', 21 | { 22 | singleQuote: true, 23 | bracketSpacing: true, 24 | arrowParens: 'always', 25 | printWidth: 120, 26 | semi: false, 27 | trailingComma: 'es5', 28 | tabWidth: 2, 29 | useTabs: false, 30 | endOfLine: 'auto', 31 | }, 32 | ], 33 | }, 34 | }, 35 | ] 36 | -------------------------------------------------------------------------------- /postgres/data.sql: -------------------------------------------------------------------------------- 1 | \connect saas saas_user 2 | 3 | -- Schema 4 | CREATE SCHEMA saas AUTHORIZATION saas_user; 5 | GRANT ALL PRIVILEGES ON SCHEMA saas TO saas_user; 6 | ALTER DEFAULT PRIVILEGES IN SCHEMA saas GRANT ALL PRIVILEGES ON TABLES TO saas_user; 7 | ALTER DEFAULT PRIVILEGES IN SCHEMA saas GRANT ALL PRIVILEGES ON SEQUENCES TO saas_user; 8 | ALTER DEFAULT PRIVILEGES IN SCHEMA saas GRANT ALL PRIVILEGES ON FUNCTIONS TO saas_user; 9 | 10 | -- Tables 11 | CREATE TABLE saas.users ( 12 | id SERIAL PRIMARY KEY, 13 | username character varying(32), 14 | created_at timestamptz NOT NULL DEFAULT now() 15 | ); 16 | 17 | -- Data 18 | INSERT INTO saas.users (id, username, created_at) 19 | VALUES (1, 'Test', '2023-01-01 00:00:00.000000'); 20 | 21 | -- Reset sequences in case placeholder data has explicit primary key inserts, 22 | -- regardless whether table has rows or not, otherwise UPDATEs may fail 23 | SELECT setval(pg_get_serial_sequence('saas.users', 'id'), coalesce(max(id), 0) + 1, false) FROM saas.users; 24 | 25 | -- Indexes 26 | CREATE INDEX ON saas.users (created_at); 27 | -------------------------------------------------------------------------------- /next-js-app/public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-ts-app/public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | # Required to use `more_clear_headers` 2 | load_module modules/ngx_http_headers_more_filter_module.so; 3 | 4 | http { 5 | # Hide Nginx version number in headers, including on error pages 6 | server_tokens off; 7 | 8 | # Hide Nginx server header 9 | more_clear_headers Server; 10 | 11 | # Hide other server headers (Node.js, PHP, etc.) 12 | more_clear_headers X-Powered-By; 13 | 14 | # More headers 15 | include /etc/nginx/headers.conf; 16 | 17 | # Recommended defaults 18 | include /etc/nginx/mime.types; 19 | default_type application/octet-stream; 20 | sendfile on; 21 | tcp_nodelay on; 22 | 23 | # Allow long domain names 24 | # Fixes "could not build server_names_hash, you should increase server_names_hash_bucket_size: 64" 25 | server_names_hash_bucket_size 128; 26 | 27 | # Sites 28 | include /etc/nginx/sites-enabled/*; 29 | } 30 | 31 | # More recommended defaults 32 | events { 33 | worker_connections 2048; 34 | multi_accept on; 35 | use epoll; 36 | } 37 | 38 | user www-data; 39 | worker_processes 4; 40 | pid /run/nginx.pid; 41 | daemon off; 42 | -------------------------------------------------------------------------------- /next-js-app/dev.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | # Install dependencies based on the preferred package manager 6 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 7 | RUN \ 8 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 9 | elif [ -f package-lock.json ]; then npm ci; \ 10 | elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ 11 | # Allow install without lockfile, so example works even without Node.js installed locally 12 | else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ 13 | fi 14 | 15 | COPY src ./src 16 | COPY public ./public 17 | COPY next.config.js . 18 | COPY jsconfig.json . 19 | 20 | # Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry 21 | # Uncomment the following line to disable telemetry at run time 22 | # ENV NEXT_TELEMETRY_DISABLED 1 23 | 24 | # Note: Don't expose ports here, Compose will handle that for us 25 | 26 | # Start Next.js in development mode based on the preferred package manager 27 | CMD \ 28 | if [ -f yarn.lock ]; then yarn dev; \ 29 | elif [ -f package-lock.json ]; then npm run dev; \ 30 | elif [ -f pnpm-lock.yaml ]; then pnpm dev; \ 31 | else yarn dev; \ 32 | fi 33 | -------------------------------------------------------------------------------- /next-ts-app/dev.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | # Install dependencies based on the preferred package manager 6 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 7 | RUN \ 8 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 9 | elif [ -f package-lock.json ]; then npm ci; \ 10 | elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ 11 | # Allow install without lockfile, so example works even without Node.js installed locally 12 | else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ 13 | fi 14 | 15 | COPY src ./src 16 | COPY public ./public 17 | COPY next.config.js . 18 | COPY tsconfig.json . 19 | 20 | # Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry 21 | # Uncomment the following line to disable telemetry at run time 22 | # ENV NEXT_TELEMETRY_DISABLED 1 23 | 24 | # Note: Don't expose ports here, Compose will handle that for us 25 | 26 | # Start Next.js in development mode based on the preferred package manager 27 | CMD \ 28 | if [ -f yarn.lock ]; then yarn dev; \ 29 | elif [ -f package-lock.json ]; then npm run dev; \ 30 | elif [ -f pnpm-lock.yaml ]; then pnpm dev; \ 31 | else yarn dev; \ 32 | fi 33 | -------------------------------------------------------------------------------- /next-js-app/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-ts-app/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/ssl.conf: -------------------------------------------------------------------------------- 1 | # Generate SSL config using "Intermediate" setting for good support 2 | # https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=intermediate&openssl=1.1.1d&guideline=5.6 3 | 4 | # Enable session resumption to improve HTTPS performance 5 | ssl_session_cache shared:MozSSL:10m; # about 40000 sessions 6 | ssl_session_timeout 1d; 7 | ssl_session_tickets off; 8 | 9 | # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits 10 | # Generate a new cert with `curl https://ssl-config.mozilla.org/ffdhe2048.txt` 11 | ssl_dhparam /etc/nginx/certs/dhparam-2048.pem; 12 | 13 | # Enable server-side protection from BEAST attacks 14 | ssl_prefer_server_ciphers on; 15 | 16 | # Protocols and ciphers chosen for compatibility 17 | ssl_protocols TLSv1.2 TLSv1.3; 18 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; 19 | # Enbale OCSP stapling to convey certificate revocation info in a privacy-preserving, scalable manner 20 | ssl_stapling on; 21 | ssl_stapling_verify on; 22 | 23 | # Note: Using add_header in `server` scope wipes out add_header in `http` scope, so include again 24 | # https://blog.g3rt.nl/nginx-add_header-pitfall.html 25 | include /etc/nginx/headers.conf; 26 | 27 | resolver 8.8.8.8 8.8.4.4; 28 | -------------------------------------------------------------------------------- /nginx/certs/example/archive/local.mproske.com/cert1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEXDCCA0SgAwIBAgISBGpthqxL9czlbtCInEjqO2IdMA0GCSqGSIb3DQEBCwUA 3 | MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD 4 | EwJSMzAeFw0yMjEyMTQwNTAyMDhaFw0yMzAzMTQwNTAyMDdaMBwxGjAYBgNVBAMT 5 | EWxvY2FsLm1wcm9za2UuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjFX+ 6 | 40rxS3aaCt1u7abdWBDNlvrfaA/Sv9IT73OlPxqeaPRWSln7xvdRd5PIoGmEP/41 7 | zpbHm620jeXM/a8Sr6OCAkswggJHMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAU 8 | BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUvrTS 9 | HlrsAUvto1KsitG0Nc1hQ9YwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsU 10 | wsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5j 11 | ci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wHAYDVR0R 12 | BBUwE4IRbG9jYWwubXByb3NrZS5jb20wTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYL 13 | KwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlw 14 | dC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgC3Pvsk35xNunXyOcW6WPRs 15 | XfxCz3qfNcSeHQmBJe20mQAAAYUPOd40AAAEAwBHMEUCIFEkCXsRofJHMxQEBi1w 16 | La8260zV4OzdzZ9zNUVru86bAiEA/H6CwcOd+9BJjHWrqg9KoHUQq9dZPWmasz1S 17 | e94ZkqEAdQDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYUPOd4p 18 | AAAEAwBGMEQCIGd7ddZ8HEVrCGzX77ArlEkj7/uNJVqbIK7QblueX5K0AiBSgdNz 19 | 7cmlgVNrNOVWkzohN95a79mNsh0rmGYY3wNdIDANBgkqhkiG9w0BAQsFAAOCAQEA 20 | Q0Pf05MKQ4G/ZJHVfvH0rbhq1eFzA3MkG19YopqrmZgx9uHkoB6iHU7UzPuD3wLT 21 | pz3sEjl5bTN/059iWk55aAevGqmG6WnAayef6g8tPYCksKnrC6upP3Yn/TY5FXSn 22 | rr/OvZ/qOSsLAFjL96S/fdgVbz5EHfsAEFtIzQN7GVar675KLLHkpBt1Kgk67eRA 23 | 6RTZVqqMp5LjK5RyhezJk/8RQN0rPhmbxBriJv8nYjbQ+wDVBKcGapyRoqSQhvl2 24 | QMG/3TN0HeI5hDQxmQLasZkCQjUd/36c3s6ttw9eikwqZ0VI6WoR/4ua9gHYfMOG 25 | mWE8w9aXIwOUSrd6GR4r5Q== 26 | -----END CERTIFICATE----- 27 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Automatic code formatting 3 | "editor.formatOnSave": false, 4 | "eslint.format.enable": true, 5 | "[json]": { 6 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 7 | "editor.formatOnSave": true 8 | }, 9 | "[jsonc]": { 10 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 11 | "editor.formatOnSave": true 12 | }, 13 | "[javascript]": { 14 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 15 | "editor.formatOnSave": true 16 | }, 17 | "[javascriptreact]": { 18 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 19 | "editor.formatOnSave": true 20 | }, 21 | "[typescript]": { 22 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 23 | "editor.formatOnSave": true 24 | }, 25 | "[typescriptreact]": { 26 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 27 | "editor.formatOnSave": true 28 | }, 29 | "[html]": { 30 | "editor.defaultFormatter": "esbenp.prettier-vscode", 31 | "editor.formatOnSave": true 32 | }, 33 | "[css]": { 34 | "editor.defaultFormatter": "esbenp.prettier-vscode", 35 | "editor.formatOnSave": true 36 | }, 37 | "[yaml]": { 38 | "editor.defaultFormatter": "esbenp.prettier-vscode", 39 | "editor.formatOnSave": true 40 | }, 41 | "[markdown]": { 42 | "editor.defaultFormatter": "esbenp.prettier-vscode", 43 | "editor.tabSize": 2, 44 | "editor.formatOnSave": false 45 | }, 46 | "[nginx]": { 47 | "editor.defaultFormatter": "raynigon.nginx-formatter", 48 | "editor.formatOnSave": true 49 | }, 50 | // Look for .eslintrc.json files here 51 | "eslint.workingDirectories": [ 52 | ".", 53 | ], 54 | "editor.codeActionsOnSave": { 55 | "source.fixAll.eslint": "explicit" 56 | }, 57 | "eslint.validate": ["javascript", "typescript"], 58 | "eslint.nodePath": "./node_modules/eslint" 59 | } 60 | -------------------------------------------------------------------------------- /nginx/headers.conf: -------------------------------------------------------------------------------- 1 | # Note: The `always` parameter includes header on HTTP 4XX-5XX responses 2 | # Burp Suite expects security headers on HTTP 404 3 | 4 | # Prevent page from being embedded within an iframe 5 | add_header X-Frame-Options SAMEORIGIN always; 6 | 7 | # Disable content-type sniffing on some browsers 8 | add_header X-Content-Type-Options nosniff always; 9 | 10 | # Enable the XSS filter built into most modern browsers 11 | add_header X-XSS-Protection "1; mode=block" always; 12 | 13 | # Enable HSTS to avoid SSL stripping, tell the browser to always use HTTPS 14 | # Fixes Burp Suite issue "Unencrypted communications" 15 | add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; 16 | 17 | # Restrict what resources can load to prevent XSS 18 | # Use a starter policy for all sites, then define stricter policy in 19 | # Read more: https://content-security-policy.com 20 | add_header Content-Security-Policy "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:;" always; 21 | 22 | # Keep referrer data off HTTP connections 23 | # Recommended header 24 | add_header Referrer-Policy "no-referrer-when-downgrade" always; 25 | 26 | # Disable certain web features on all sites 27 | # Recommended header 28 | add_header Permissions-Policy "magnetometer=(), microphone=(), payment=(), usb=()" always; 29 | 30 | # Upcoming required COOP/COEP headers recommended by https://securityheaders.com 31 | # Read more: https://scotthelme.co.uk/coop-and-coep 32 | 33 | # Prevent assets being loaded that don't grant permission to load them via CORS or CORP 34 | # Note: `require-corp` currently blocks reCAPTCHA requests in Network tab 35 | add_header Cross-Origin-Embedder-Policy "unsafe-none" always; 36 | 37 | # Opt-in to Cross-Origin Isolation 38 | add_header Cross-Origin-Opener-Policy "same-origin" always; 39 | 40 | # Specify who can load sub-resources 41 | add_header Cross-Origin-Resource-Policy "cross-origin" always; 42 | -------------------------------------------------------------------------------- /docker-compose.prod.yml: -------------------------------------------------------------------------------- 1 | # `version` is now deprecated 2 | # https://docs.docker.com/compose/compose-file/#version-top-level-element 3 | 4 | services: 5 | next-ts-app: 6 | container_name: next-ts-app 7 | build: 8 | context: ./next-ts-app 9 | dockerfile: prod.Dockerfile 10 | args: 11 | ENV_VARIABLE: ${ENV_VARIABLE} 12 | NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} 13 | POSTGRES_HOST: ${POSTGRES_HOST} 14 | POSTGRES_PORT: ${POSTGRES_PORT} 15 | POSTGRES_DATABASE: ${POSTGRES_DATABASE} 16 | POSTGRES_USER: ${POSTGRES_USER} 17 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 18 | restart: always 19 | networks: 20 | - my_network 21 | 22 | next-js-app: 23 | container_name: next-js-app 24 | build: 25 | context: ./next-js-app 26 | dockerfile: prod.Dockerfile 27 | args: 28 | ENV_VARIABLE: ${ENV_VARIABLE} 29 | NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} 30 | POSTGRES_HOST: ${POSTGRES_HOST} 31 | POSTGRES_PORT: ${POSTGRES_PORT} 32 | POSTGRES_DATABASE: ${POSTGRES_DATABASE} 33 | POSTGRES_USER: ${POSTGRES_USER} 34 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 35 | restart: always 36 | networks: 37 | - my_network 38 | 39 | nginx: 40 | container_name: nginx 41 | build: 42 | context: ./nginx 43 | dockerfile: Dockerfile 44 | volumes: 45 | - ./nginx/sites-enabled:/etc/nginx/sites-enabled 46 | - ./nginx/logs:/var/log/nginx 47 | ports: 48 | - 80:80 49 | - 443:443 50 | - 3000:3000 51 | depends_on: 52 | - next-ts-app 53 | - next-js-app 54 | restart: always 55 | networks: 56 | - my_network 57 | 58 | # Add more containers below 59 | 60 | # Define a network, which allows containers to communicate 61 | # with each other, by using their container name as a hostname 62 | networks: 63 | my_network: 64 | external: true 65 | -------------------------------------------------------------------------------- /docker-compose.prod-without-multistage.yml: -------------------------------------------------------------------------------- 1 | # `version` is now deprecated 2 | # https://docs.docker.com/compose/compose-file/#version-top-level-element 3 | 4 | services: 5 | next-ts-app: 6 | container_name: next-ts-app 7 | build: 8 | context: ./next-ts-app 9 | dockerfile: prod-without-multistage.Dockerfile 10 | args: 11 | ENV_VARIABLE: ${ENV_VARIABLE} 12 | NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} 13 | POSTGRES_HOST: ${POSTGRES_HOST} 14 | POSTGRES_PORT: ${POSTGRES_PORT} 15 | POSTGRES_DATABASE: ${POSTGRES_DATABASE} 16 | POSTGRES_USER: ${POSTGRES_USER} 17 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 18 | restart: always 19 | networks: 20 | - my_network 21 | 22 | next-js-app: 23 | container_name: next-js-app 24 | build: 25 | context: ./next-js-app 26 | dockerfile: prod-without-multistage.Dockerfile 27 | args: 28 | ENV_VARIABLE: ${ENV_VARIABLE} 29 | NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} 30 | POSTGRES_HOST: ${POSTGRES_HOST} 31 | POSTGRES_PORT: ${POSTGRES_PORT} 32 | POSTGRES_DATABASE: ${POSTGRES_DATABASE} 33 | POSTGRES_USER: ${POSTGRES_USER} 34 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 35 | restart: always 36 | networks: 37 | - my_network 38 | 39 | nginx: 40 | container_name: nginx 41 | build: 42 | context: ./nginx 43 | dockerfile: Dockerfile 44 | volumes: 45 | - ./nginx/sites-enabled:/etc/nginx/sites-enabled 46 | - ./nginx/logs:/var/log/nginx 47 | ports: 48 | - 80:80 49 | - 443:443 50 | - 3000:3000 51 | depends_on: 52 | - next-ts-app 53 | - next-js-app 54 | restart: always 55 | networks: 56 | - my_network 57 | 58 | # Add more containers below 59 | 60 | # Define a network, which allows containers to communicate 61 | # with each other, by using their container name as a hostname 62 | networks: 63 | my_network: 64 | external: true 65 | -------------------------------------------------------------------------------- /next-js-app/prod-without-multistage.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | # Install dependencies based on the preferred package manager 6 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 7 | # Include --production flag for JavaScript 8 | RUN \ 9 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 10 | elif [ -f package-lock.json ]; then npm ci; \ 11 | elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ 12 | # Allow install without lockfile, so example works even without Node.js installed locally 13 | else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ 14 | fi 15 | 16 | COPY src ./src 17 | COPY public ./public 18 | COPY next.config.js . 19 | COPY jsconfig.json . 20 | 21 | # Environment variables must be present at build time 22 | # https://github.com/vercel/next.js/discussions/14030 23 | ARG ENV_VARIABLE 24 | ENV ENV_VARIABLE=${ENV_VARIABLE} 25 | ARG NEXT_PUBLIC_ENV_VARIABLE 26 | ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} 27 | ARG POSTGRES_HOST 28 | ENV POSTGRES_HOST=${POSTGRES_HOST} 29 | ARG POSTGRES_PORT 30 | ENV POSTGRES_PORT=${POSTGRES_PORT} 31 | ARG POSTGRES_DATABASE 32 | ENV POSTGRES_DATABASE=${POSTGRES_DATABASE} 33 | ARG POSTGRES_USER 34 | ENV POSTGRES_USER=${POSTGRES_USER} 35 | ARG POSTGRES_PASSWORD 36 | ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 37 | 38 | # Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry 39 | # Uncomment the following line to disable telemetry at build time 40 | # ENV NEXT_TELEMETRY_DISABLED 1 41 | 42 | # Note: Don't expose ports here, Compose will handle that for us 43 | 44 | # Build Next.js based on the preferred package manager 45 | RUN \ 46 | if [ -f yarn.lock ]; then yarn build; \ 47 | elif [ -f package-lock.json ]; then npm run build; \ 48 | elif [ -f pnpm-lock.yaml ]; then pnpm build; \ 49 | else yarn build; \ 50 | fi 51 | 52 | # Start Next.js based on the preferred package manager 53 | CMD \ 54 | if [ -f yarn.lock ]; then yarn start; \ 55 | elif [ -f package-lock.json ]; then npm run start; \ 56 | elif [ -f pnpm-lock.yaml ]; then pnpm start; \ 57 | else yarn start; \ 58 | fi 59 | -------------------------------------------------------------------------------- /next-ts-app/prod-without-multistage.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | # Install dependencies based on the preferred package manager 6 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 7 | # Omit --production flag for TypeScript devDependencies 8 | RUN \ 9 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 10 | elif [ -f package-lock.json ]; then npm ci; \ 11 | elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ 12 | # Allow install without lockfile, so example works even without Node.js installed locally 13 | else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ 14 | fi 15 | 16 | COPY src ./src 17 | COPY public ./public 18 | COPY next.config.js . 19 | COPY tsconfig.json . 20 | 21 | # Environment variables must be present at build time 22 | # https://github.com/vercel/next.js/discussions/14030 23 | ARG ENV_VARIABLE 24 | ENV ENV_VARIABLE=${ENV_VARIABLE} 25 | ARG NEXT_PUBLIC_ENV_VARIABLE 26 | ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} 27 | ARG POSTGRES_HOST 28 | ENV POSTGRES_HOST=${POSTGRES_HOST} 29 | ARG POSTGRES_PORT 30 | ENV POSTGRES_PORT=${POSTGRES_PORT} 31 | ARG POSTGRES_DATABASE 32 | ENV POSTGRES_DATABASE=${POSTGRES_DATABASE} 33 | ARG POSTGRES_USER 34 | ENV POSTGRES_USER=${POSTGRES_USER} 35 | ARG POSTGRES_PASSWORD 36 | ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 37 | 38 | # Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry 39 | # Uncomment the following line to disable telemetry at build time 40 | # ENV NEXT_TELEMETRY_DISABLED 1 41 | 42 | # Note: Don't expose ports here, Compose will handle that for us 43 | 44 | # Build Next.js based on the preferred package manager 45 | RUN \ 46 | if [ -f yarn.lock ]; then yarn build; \ 47 | elif [ -f package-lock.json ]; then npm run build; \ 48 | elif [ -f pnpm-lock.yaml ]; then pnpm build; \ 49 | else yarn build; \ 50 | fi 51 | 52 | # Start Next.js based on the preferred package manager 53 | CMD \ 54 | if [ -f yarn.lock ]; then yarn start; \ 55 | elif [ -f package-lock.json ]; then npm run start; \ 56 | elif [ -f pnpm-lock.yaml ]; then pnpm start; \ 57 | else yarn start; \ 58 | fi 59 | -------------------------------------------------------------------------------- /nginx/sites-enabled/default.conf: -------------------------------------------------------------------------------- 1 | # Redirect http to https 2 | server { 3 | listen 80 default_server; 4 | listen [::]:80 default_server; 5 | 6 | location / { 7 | return 301 https://$host$request_uri; 8 | } 9 | } 10 | 11 | server { 12 | listen 443 ssl http2; 13 | listen [::]:443 ssl http2; 14 | 15 | # Your domain names here 16 | server_name 17 | local.mproske.com 18 | mproske.com; 19 | 20 | ssl_certificate /etc/nginx/certs/local.mproske.com/fullchain.pem; 21 | ssl_certificate_key /etc/nginx/certs/local.mproske.com/privkey.pem; 22 | include /etc/nginx/ssl.conf; 23 | 24 | error_log /var/log/nginx/local.mproske.com.error.log notice; 25 | access_log /var/log/nginx/local.mproske.com.access.log; 26 | 27 | location / { 28 | proxy_pass http://next-ts-app:3000; 29 | proxy_http_version 1.1; 30 | proxy_set_header Upgrade $http_upgrade; 31 | proxy_set_header Connection 'upgrade'; 32 | proxy_set_header Host $host; 33 | proxy_cache_bypass $http_upgrade; 34 | 35 | # Required for rate limiting 36 | proxy_set_header X-Real-IP $remote_addr; 37 | proxy_set_header X-Forwarded-For $remote_addr; 38 | 39 | } 40 | 41 | location /js { 42 | proxy_pass http://next-js-app:3000; 43 | proxy_http_version 1.1; 44 | proxy_set_header Upgrade $http_upgrade; 45 | proxy_set_header Connection 'upgrade'; 46 | proxy_set_header Host $host; 47 | proxy_cache_bypass $http_upgrade; 48 | 49 | # Required for rate limiting 50 | proxy_set_header X-Real-IP $remote_addr; 51 | proxy_set_header X-Forwarded-For $remote_addr; 52 | 53 | } 54 | } 55 | 56 | # Dev 57 | server { 58 | listen 3000; 59 | listen [::]:3000; 60 | 61 | server_name 127.0.0.1; 62 | 63 | location / { 64 | proxy_pass http://next-ts-app:3000; 65 | proxy_http_version 1.1; 66 | proxy_set_header Upgrade $http_upgrade; 67 | proxy_set_header Connection 'upgrade'; 68 | proxy_set_header Host $host; 69 | proxy_cache_bypass $http_upgrade; 70 | 71 | # Required for rate limiting 72 | proxy_set_header X-Real-IP $remote_addr; 73 | proxy_set_header X-Forwarded-For $remote_addr; 74 | } 75 | 76 | location /js { 77 | proxy_pass http://next-js-app:3000; 78 | proxy_http_version 1.1; 79 | proxy_set_header Upgrade $http_upgrade; 80 | proxy_set_header Connection 'upgrade'; 81 | proxy_set_header Host $host; 82 | proxy_cache_bypass $http_upgrade; 83 | 84 | # Required for rate limiting 85 | proxy_set_header X-Real-IP $remote_addr; 86 | proxy_set_header X-Forwarded-For $remote_addr; 87 | 88 | } 89 | } -------------------------------------------------------------------------------- /next-js-app/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/image' 3 | import Link from 'next/link' 4 | import { Inter } from 'next/font/google' 5 | import styles from 'src/styles/Home.module.css' 6 | import Blob from 'src/components/Blob' 7 | import sql from 'src/lib/postgres' 8 | 9 | const inter = Inter({ subsets: ['latin'] }) 10 | 11 | export async function getServerSideProps() { 12 | try { 13 | const result = await sql`select count(*)::int from saas.users` 14 | const numUsers = result[0].count 15 | return { props: { numUsers } } 16 | } catch (err) { 17 | console.error(err) 18 | } 19 | 20 | return { props: { numUsers: 0 } } 21 | } 22 | 23 | export default function Home({ numUsers }) { 24 | return ( 25 | <> 26 | 27 | Create Next App 28 | 29 | 30 | 31 | 32 |
33 |
34 |

35 | 36 | Get started by editing  37 | next-js-app/src/app/page.js,
and visiting the{' '} 38 | TypeScript app. 39 |
40 |

41 |
42 | 47 | Starter kit by Max Proske 48 | 49 |
50 |
51 | 52 |
53 | Next.js Logo 54 | 55 |
56 | 57 |
58 | 59 |

60 | TypeScript app -> 61 |

62 |

Visit the TypeScript app.

63 |
64 | 65 | 66 |

67 | Your app -> 68 |

69 |

70 | Your SaaS app here ({numUsers} {numUsers === 1 ? 'user' : 'users'}). 71 |

72 | 73 |
74 |
75 | 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /next-js-app/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --max-width: 1100px; 3 | --border-radius: 12px; 4 | --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", 5 | "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", 6 | "Fira Mono", "Droid Sans Mono", "Courier New", monospace; 7 | 8 | --foreground-rgb: 0, 0, 0; 9 | --background-start-rgb: 214, 219, 220; 10 | --background-end-rgb: 255, 255, 255; 11 | 12 | --primary-glow: conic-gradient( 13 | from 180deg at 50% 50%, 14 | #16abff33 0deg, 15 | #0885ff33 55deg, 16 | #54d6ff33 120deg, 17 | #0071ff33 160deg, 18 | transparent 360deg 19 | ); 20 | --secondary-glow: radial-gradient( 21 | rgba(255, 255, 255, 1), 22 | rgba(255, 255, 255, 0) 23 | ); 24 | 25 | --tile-start-rgb: 239, 245, 249; 26 | --tile-end-rgb: 228, 232, 233; 27 | --tile-border: conic-gradient( 28 | #00000080, 29 | #00000040, 30 | #00000030, 31 | #00000020, 32 | #00000010, 33 | #00000010, 34 | #00000080 35 | ); 36 | 37 | --callout-rgb: 238, 240, 241; 38 | --callout-border-rgb: 172, 175, 176; 39 | --card-rgb: 180, 185, 188; 40 | --card-border-rgb: 131, 134, 135; 41 | } 42 | 43 | @media (prefers-color-scheme: dark) { 44 | :root { 45 | --foreground-rgb: 255, 255, 255; 46 | --background-start-rgb: 0, 0, 0; 47 | --background-end-rgb: 0, 0, 0; 48 | 49 | --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); 50 | --secondary-glow: linear-gradient( 51 | to bottom right, 52 | rgba(1, 65, 255, 0), 53 | rgba(1, 65, 255, 0), 54 | rgba(1, 65, 255, 0.3) 55 | ); 56 | 57 | --tile-start-rgb: 2, 13, 46; 58 | --tile-end-rgb: 2, 5, 19; 59 | --tile-border: conic-gradient( 60 | #ffffff80, 61 | #ffffff40, 62 | #ffffff30, 63 | #ffffff20, 64 | #ffffff10, 65 | #ffffff10, 66 | #ffffff80 67 | ); 68 | 69 | --callout-rgb: 20, 20, 20; 70 | --callout-border-rgb: 108, 108, 108; 71 | --card-rgb: 100, 100, 100; 72 | --card-border-rgb: 200, 200, 200; 73 | } 74 | } 75 | 76 | * { 77 | box-sizing: border-box; 78 | padding: 0; 79 | margin: 0; 80 | } 81 | 82 | html, 83 | body { 84 | max-width: 100vw; 85 | overflow-x: hidden; 86 | } 87 | 88 | body { 89 | color: rbg(--foreground-rgb); 90 | background: linear-gradient( 91 | to bottom, 92 | transparent, 93 | rgb(var(--background-end-rgb)) 94 | ) 95 | rgb(var(--background-start-rgb)); 96 | } 97 | 98 | a { 99 | color: inherit; 100 | text-decoration: none; 101 | } 102 | 103 | @media (prefers-color-scheme: dark) { 104 | html { 105 | color-scheme: dark; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /next-ts-app/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --max-width: 1100px; 3 | --border-radius: 12px; 4 | --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", 5 | "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", 6 | "Fira Mono", "Droid Sans Mono", "Courier New", monospace; 7 | 8 | --foreground-rgb: 0, 0, 0; 9 | --background-start-rgb: 214, 219, 220; 10 | --background-end-rgb: 255, 255, 255; 11 | 12 | --primary-glow: conic-gradient( 13 | from 180deg at 50% 50%, 14 | #16abff33 0deg, 15 | #0885ff33 55deg, 16 | #54d6ff33 120deg, 17 | #0071ff33 160deg, 18 | transparent 360deg 19 | ); 20 | --secondary-glow: radial-gradient( 21 | rgba(255, 255, 255, 1), 22 | rgba(255, 255, 255, 0) 23 | ); 24 | 25 | --tile-start-rgb: 239, 245, 249; 26 | --tile-end-rgb: 228, 232, 233; 27 | --tile-border: conic-gradient( 28 | #00000080, 29 | #00000040, 30 | #00000030, 31 | #00000020, 32 | #00000010, 33 | #00000010, 34 | #00000080 35 | ); 36 | 37 | --callout-rgb: 238, 240, 241; 38 | --callout-border-rgb: 172, 175, 176; 39 | --card-rgb: 180, 185, 188; 40 | --card-border-rgb: 131, 134, 135; 41 | } 42 | 43 | @media (prefers-color-scheme: dark) { 44 | :root { 45 | --foreground-rgb: 255, 255, 255; 46 | --background-start-rgb: 0, 0, 0; 47 | --background-end-rgb: 0, 0, 0; 48 | 49 | --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); 50 | --secondary-glow: linear-gradient( 51 | to bottom right, 52 | rgba(1, 65, 255, 0), 53 | rgba(1, 65, 255, 0), 54 | rgba(1, 65, 255, 0.3) 55 | ); 56 | 57 | --tile-start-rgb: 2, 13, 46; 58 | --tile-end-rgb: 2, 5, 19; 59 | --tile-border: conic-gradient( 60 | #ffffff80, 61 | #ffffff40, 62 | #ffffff30, 63 | #ffffff20, 64 | #ffffff10, 65 | #ffffff10, 66 | #ffffff80 67 | ); 68 | 69 | --callout-rgb: 20, 20, 20; 70 | --callout-border-rgb: 108, 108, 108; 71 | --card-rgb: 100, 100, 100; 72 | --card-border-rgb: 200, 200, 200; 73 | } 74 | } 75 | 76 | * { 77 | box-sizing: border-box; 78 | padding: 0; 79 | margin: 0; 80 | } 81 | 82 | html, 83 | body { 84 | max-width: 100vw; 85 | overflow-x: hidden; 86 | } 87 | 88 | body { 89 | color: rbg(--foreground-rgb); 90 | background: linear-gradient( 91 | to bottom, 92 | transparent, 93 | rgb(var(--background-end-rgb)) 94 | ) 95 | rgb(var(--background-start-rgb)); 96 | } 97 | 98 | a { 99 | color: inherit; 100 | text-decoration: none; 101 | } 102 | 103 | @media (prefers-color-scheme: dark) { 104 | html { 105 | color-scheme: dark; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | # `version` is now deprecated 2 | # https://docs.docker.com/compose/compose-file/#version-top-level-element 3 | 4 | services: 5 | next-ts-app: 6 | container_name: next-ts-app 7 | build: 8 | context: ./next-ts-app 9 | dockerfile: dev.Dockerfile 10 | environment: 11 | ENV_VARIABLE: ${ENV_VARIABLE} 12 | NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} 13 | POSTGRES_HOST: ${POSTGRES_HOST} 14 | POSTGRES_PORT: ${POSTGRES_PORT} 15 | POSTGRES_DATABASE: ${POSTGRES_DATABASE} 16 | POSTGRES_USER: ${POSTGRES_USER} 17 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 18 | volumes: 19 | - ./next-ts-app/src:/app/src 20 | - ./next-ts-app/public:/app/public 21 | # It's not necessary to sync the `.next` directory 22 | depends_on: 23 | - postgres 24 | restart: always 25 | networks: 26 | - my_network 27 | 28 | next-js-app: 29 | container_name: next-js-app 30 | build: 31 | context: ./next-js-app 32 | dockerfile: dev.Dockerfile 33 | environment: 34 | ENV_VARIABLE: ${ENV_VARIABLE} 35 | NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} 36 | POSTGRES_HOST: ${POSTGRES_HOST} 37 | POSTGRES_PORT: ${POSTGRES_PORT} 38 | POSTGRES_DATABASE: ${POSTGRES_DATABASE} 39 | POSTGRES_USER: ${POSTGRES_USER} 40 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 41 | volumes: 42 | - ./next-js-app/src:/app/src 43 | - ./next-js-app/public:/app/public 44 | # It's not necessary to sync the `.next` directory 45 | depends_on: 46 | - postgres 47 | restart: always 48 | networks: 49 | - my_network 50 | 51 | nginx: 52 | container_name: nginx 53 | build: 54 | context: ./nginx 55 | dockerfile: Dockerfile 56 | volumes: 57 | - ./nginx/sites-enabled:/etc/nginx/sites-enabled 58 | - ./nginx/logs:/var/log/nginx 59 | ports: 60 | - 80:80 61 | - 443:443 62 | - 3000:3000 63 | depends_on: 64 | - next-ts-app 65 | - next-js-app 66 | restart: always 67 | networks: 68 | - my_network 69 | 70 | postgres: 71 | container_name: postgres 72 | build: 73 | context: ./postgres 74 | dockerfile: dev.Dockerfile 75 | environment: 76 | # The only variable required is `POSTGRES_PASSWORD`, the rest are optional 77 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 78 | POSTGRES_USER: ${POSTGRES_USER} 79 | POSTGRES_DB: ${POSTGRES_DATABASE} 80 | restart: always 81 | networks: 82 | - my_network 83 | 84 | # Add more containers below 85 | 86 | # Define a network, which allows containers to communicate 87 | # with each other, by using their container name as a hostname 88 | networks: 89 | my_network: 90 | external: true 91 | -------------------------------------------------------------------------------- /next-ts-app/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { GetServerSideProps, InferGetServerSidePropsType } from 'next' 2 | import Head from 'next/head' 3 | import Image from 'next/image' 4 | import Link from 'next/link' 5 | import { Inter } from 'next/font/google' 6 | import styles from '../styles/Home.module.css' 7 | import Blob from '../components/Blob' 8 | import sql from '../lib/postgres' 9 | 10 | const inter = Inter({ subsets: ['latin'] }) 11 | 12 | type HomeProps = { 13 | numUsers: number 14 | } 15 | 16 | export const getServerSideProps: GetServerSideProps = async () => { 17 | try { 18 | const result = await sql`select count(*)::int from saas.users` 19 | const numUsers = result[0].count 20 | return { props: { numUsers } } 21 | } catch (err) { 22 | console.error(err) 23 | } 24 | 25 | return { props: { numUsers: 0 } } 26 | } 27 | 28 | export default function Home({ numUsers }: InferGetServerSidePropsType) { 29 | return ( 30 | <> 31 | 32 | Create Next App 33 | 34 | 35 | 36 | 37 |
38 |
39 |

40 | 41 | Get started by editing  42 | next-ts-app/src/app/page.tsx,
and visiting the{' '} 43 | JavaScript app. 44 |
45 |

46 |
47 | 52 | Starter kit by Max Proske 53 | 54 |
55 |
56 | 57 |
58 | Next.js Logo 59 | 60 |
61 | 62 |
63 | 64 |

65 | JavaScript app -> 66 |

67 |

Visit the JavaScript app.

68 | 69 | 70 | 71 |

72 | Your app -> 73 |

74 |

75 | Your SaaS app here ({numUsers} {numUsers === 1 ? 'user' : 'users'}). 76 |

77 | 78 |
79 |
80 | 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /next-js-app/prod.Dockerfile: -------------------------------------------------------------------------------- 1 | # Step 1. Rebuild the source code only when needed 2 | FROM node:20-alpine AS builder 3 | 4 | WORKDIR /app 5 | 6 | # Install dependencies based on the preferred package manager 7 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 8 | # Omit --production flag for TypeScript devDependencies 9 | RUN \ 10 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 11 | elif [ -f package-lock.json ]; then npm ci; \ 12 | elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ 13 | # Allow install without lockfile, so example works even without Node.js installed locally 14 | else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ 15 | fi 16 | 17 | COPY src ./src 18 | COPY public ./public 19 | COPY next.config.js . 20 | COPY jsconfig.json . 21 | 22 | # Environment variables must be present at build time 23 | # https://github.com/vercel/next.js/discussions/14030 24 | ARG ENV_VARIABLE 25 | ENV ENV_VARIABLE=${ENV_VARIABLE} 26 | ARG NEXT_PUBLIC_ENV_VARIABLE 27 | ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} 28 | ARG POSTGRES_HOST 29 | ENV POSTGRES_HOST=${POSTGRES_HOST} 30 | ARG POSTGRES_PORT 31 | ENV POSTGRES_PORT=${POSTGRES_PORT} 32 | ARG POSTGRES_DATABASE 33 | ENV POSTGRES_DATABASE=${POSTGRES_DATABASE} 34 | ARG POSTGRES_USER 35 | ENV POSTGRES_USER=${POSTGRES_USER} 36 | ARG POSTGRES_PASSWORD 37 | ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 38 | 39 | # Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry 40 | # Uncomment the following line to disable telemetry at build time 41 | # ENV NEXT_TELEMETRY_DISABLED 1 42 | 43 | # Build Next.js based on the preferred package manager 44 | RUN \ 45 | if [ -f yarn.lock ]; then yarn build; \ 46 | elif [ -f package-lock.json ]; then npm run build; \ 47 | elif [ -f pnpm-lock.yaml ]; then pnpm build; \ 48 | else yarn build; \ 49 | fi 50 | 51 | # Note: It is not necessary to add an intermediate step that does a full copy of `node_modules` here 52 | 53 | # Step 2. Production image, copy all the files and run next 54 | FROM node:20-alpine AS runner 55 | 56 | WORKDIR /app 57 | 58 | # Don't run production as root 59 | RUN addgroup --system --gid 1001 nodejs 60 | RUN adduser --system --uid 1001 nextjs 61 | USER nextjs 62 | 63 | COPY --from=builder /app/public ./public 64 | 65 | # Automatically leverage output traces to reduce image size 66 | # https://nextjs.org/docs/advanced-features/output-file-tracing 67 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ 68 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static 69 | 70 | # Environment variables must be redefined at run time 71 | ARG ENV_VARIABLE 72 | ENV ENV_VARIABLE=${ENV_VARIABLE} 73 | ARG NEXT_PUBLIC_ENV_VARIABLE 74 | ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} 75 | ARG POSTGRES_HOST 76 | ENV POSTGRES_HOST=${POSTGRES_HOST} 77 | ARG POSTGRES_PORT 78 | ENV POSTGRES_PORT=${POSTGRES_PORT} 79 | ARG POSTGRES_DATABASE 80 | ENV POSTGRES_DATABASE=${POSTGRES_DATABASE} 81 | ARG POSTGRES_USER 82 | ENV POSTGRES_USER=${POSTGRES_USER} 83 | ARG POSTGRES_PASSWORD 84 | ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 85 | 86 | # Uncomment the following line to disable telemetry at run time 87 | # ENV NEXT_TELEMETRY_DISABLED 1 88 | 89 | # Note: Don't expose ports here, Compose will handle that for us 90 | 91 | # We can use the node process itself here 92 | CMD node server.js 93 | -------------------------------------------------------------------------------- /next-ts-app/prod.Dockerfile: -------------------------------------------------------------------------------- 1 | # Step 1. Rebuild the source code only when needed 2 | FROM node:20-alpine AS builder 3 | 4 | WORKDIR /app 5 | 6 | # Install dependencies based on the preferred package manager 7 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 8 | # Omit --production flag for TypeScript devDependencies 9 | RUN \ 10 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 11 | elif [ -f package-lock.json ]; then npm ci; \ 12 | elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ 13 | # Allow install without lockfile, so example works even without Node.js installed locally 14 | else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ 15 | fi 16 | 17 | COPY src ./src 18 | COPY public ./public 19 | COPY next.config.js . 20 | COPY tsconfig.json . 21 | 22 | # Environment variables must be present at build time 23 | # https://github.com/vercel/next.js/discussions/14030 24 | ARG ENV_VARIABLE 25 | ENV ENV_VARIABLE=${ENV_VARIABLE} 26 | ARG NEXT_PUBLIC_ENV_VARIABLE 27 | ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} 28 | ARG POSTGRES_HOST 29 | ENV POSTGRES_HOST=${POSTGRES_HOST} 30 | ARG POSTGRES_PORT 31 | ENV POSTGRES_PORT=${POSTGRES_PORT} 32 | ARG POSTGRES_DATABASE 33 | ENV POSTGRES_DATABASE=${POSTGRES_DATABASE} 34 | ARG POSTGRES_USER 35 | ENV POSTGRES_USER=${POSTGRES_USER} 36 | ARG POSTGRES_PASSWORD 37 | ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 38 | 39 | # Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry 40 | # Uncomment the following line to disable telemetry at build time 41 | # ENV NEXT_TELEMETRY_DISABLED 1 42 | 43 | # Build Next.js based on the preferred package manager 44 | RUN \ 45 | if [ -f yarn.lock ]; then yarn build; \ 46 | elif [ -f package-lock.json ]; then npm run build; \ 47 | elif [ -f pnpm-lock.yaml ]; then pnpm build; \ 48 | else yarn build; \ 49 | fi 50 | 51 | # Note: It is not necessary to add an intermediate step that does a full copy of `node_modules` here 52 | 53 | # Step 2. Production image, copy all the files and run next 54 | FROM node:20-alpine AS runner 55 | 56 | WORKDIR /app 57 | 58 | # Don't run production as root 59 | RUN addgroup --system --gid 1001 nodejs 60 | RUN adduser --system --uid 1001 nextjs 61 | USER nextjs 62 | 63 | COPY --from=builder /app/public ./public 64 | 65 | # Automatically leverage output traces to reduce image size 66 | # https://nextjs.org/docs/advanced-features/output-file-tracing 67 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ 68 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static 69 | 70 | # Environment variables must be redefined at run time 71 | ARG ENV_VARIABLE 72 | ENV ENV_VARIABLE=${ENV_VARIABLE} 73 | ARG NEXT_PUBLIC_ENV_VARIABLE 74 | ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} 75 | ARG POSTGRES_HOST 76 | ENV POSTGRES_HOST=${POSTGRES_HOST} 77 | ARG POSTGRES_PORT 78 | ENV POSTGRES_PORT=${POSTGRES_PORT} 79 | ARG POSTGRES_DATABASE 80 | ENV POSTGRES_DATABASE=${POSTGRES_DATABASE} 81 | ARG POSTGRES_USER 82 | ENV POSTGRES_USER=${POSTGRES_USER} 83 | ARG POSTGRES_PASSWORD 84 | ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 85 | 86 | # Uncomment the following line to disable telemetry at run time 87 | # ENV NEXT_TELEMETRY_DISABLED 1 88 | 89 | # Note: Don't expose ports here, Compose will handle that for us 90 | 91 | # We can use the node process itself here 92 | CMD node server.js 93 | -------------------------------------------------------------------------------- /nginx/certs/example/archive/local.mproske.com/chain1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw 3 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 4 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw 5 | WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg 6 | RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 7 | AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP 8 | R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx 9 | sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm 10 | NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg 11 | Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG 12 | /kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC 13 | AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB 14 | Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA 15 | FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw 16 | AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw 17 | Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB 18 | gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W 19 | PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl 20 | ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz 21 | CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm 22 | lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 23 | avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 24 | yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O 25 | yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids 26 | hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ 27 | HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv 28 | MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX 29 | nLRbwHOoq7hHwg== 30 | -----END CERTIFICATE----- 31 | -----BEGIN CERTIFICATE----- 32 | MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ 33 | MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT 34 | DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow 35 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 36 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB 37 | AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC 38 | ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL 39 | wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D 40 | LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK 41 | 4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5 42 | bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y 43 | sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ 44 | Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4 45 | FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc 46 | SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql 47 | PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND 48 | TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw 49 | SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1 50 | c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx 51 | +tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB 52 | ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu 53 | b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E 54 | U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu 55 | MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC 56 | 5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW 57 | 9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG 58 | WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O 59 | he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC 60 | Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 61 | -----END CERTIFICATE----- 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js + Docker Production Kit 2 | 3 | Finally, a production-ready starter kit for Next.js and Docker Compose! 4 | 5 | Based on my [official Next.js example](https://github.com/vercel/next.js/tree/canary/examples/with-docker-compose), with multiple JavaScript and TypeScript apps, Postgres, SSL with Nginx, and tons of best practice defaults from over 5 years of tweaking. 6 | 7 | Issues/pull requests and ⭐ stars welcome as a show of support! Keep coming back, steal ideas for your development and production environment, and share your best practices as we continually make this example better. 8 | 9 |
10 | Show Preview 11 | Preview 12 |
13 | 14 | ## Why use Compose with Next.js? 15 | 16 | Check out the [YouTube video](https://www.youtube.com/watch?v=-iaLmOGZuD4)! 17 | 18 | - 100% reproducable environment across macOS, Windows, and Linux teams. 19 | - Easy to develop, test, and run multiple Next.js apps, databases, and other services together. 20 | - Development and production environments are code. Compose file can be extended with any technology. If it exists, there's a Docker image of it. No exceptions. 21 | - Made with simple, best-in-class technologies that developers of all skill levels can quickly pick up. 22 | 23 | ## How to use 24 | 25 | Click the ![Fork][fork-icon] Fork button in the header of this repo before continuing. When finished, you'll be taken to your copy of the repo. 26 | 27 | Next, you'll need to clone your forked repo to your computer. In VSCode, press ⌘+SHIFT+P and search for `Git: Clone`, then enter `https://github.com/YOUR_GITHUB_USERNAME/nextjs-docker-production-kit.git`. If successful, a popup will prompt you to open your cloned repo. 28 | 29 | ## Development 30 | 31 | Make sure you have [Docker Desktop](https://docs.docker.com/get-docker) installed. 32 | 33 | In the root of your project, run your development environment: 34 | 35 | ```bash 36 | ./dev.sh 37 | ``` 38 | 39 | Visit [http://localhost:3000](http://localhost:3000) to see the result. 40 | 41 | You can start editing the page by modifying `home/src/app/page.tsx`. The page auto-updates as you save the file. 42 | 43 | ### SSL ✨ 44 | 45 | This example comes pre-configured with SSL. That is, your development environment is accessible using a secure HTTPS connection. You will need to append the contents of [hosts](hosts) to your `/etc/hosts` file. 46 | - [How do I find and update my `/etc/hosts` file?](https://docs.rackspace.com/support/how-to/modify-your-hosts-file). 47 | 48 | Visit [https://local.mproske.com](https://local.mproske.com) to develop locally on SSL. 49 | 50 | ### Auto-formatting ✨ 51 | 52 | Install the recommended VSCode exensions. Then, install ESLint and Prettier by running `npm install`, `yarn install`, or `pnpm install` in the root of your project. Your JS and TS code should now auto-format on save. 53 | 54 | ## Production 55 | 56 | Run your production environment: 57 | 58 | ```bash 59 | ./prod.sh 60 | ``` 61 | 62 | Visit [http://localhost:3000](http://localhost:3000) or [https://local.mproske.com](https://local.mproske.com). 63 | 64 | Alternatively, you can run production without multistage. 65 | 66 | ```bash 67 | ./prod-without-multistage.sh 68 | ``` 69 | 70 | [Docker Multi-stage Builds](https://docs.docker.com/build/building/multi-stage) is recommended in production, because combined with [Next.js Output Standalone](https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files), only the minimum `node_modules` files required are copied into the final Docker image. This results in up to 85% smaller apps (Approximately 110 MB, compared to 1 GB with `create-next-app`). But this can be a good alternative for developers who don't want to get into the weeds. Because when you work in Docker, everything should be easily understandable. 71 | 72 | ## Deploy 73 | 74 | Spin up a One-Click Docker app, that's ready to run in about 1 minute. Login to your server, run the production command, and you're up and running! 75 | 76 | - [Digital Ocean](https://marketplace.digitalocean.com/apps/docker) 77 | - [Vultr](https://www.vultr.com/apps/docker) 78 | 79 | Visit http://YOUR_SERVERS_IP_ADDRESS:3000 80 | 81 | ## What now? 82 | 83 | Add any apps or technologies you like! Try copying your Next.js app to the root of the project, copy over the Dockerfiles from the `next-ts-app` or `next-js-app` directory. 84 | 85 | ## Gotchas 86 | 87 | - ??? 88 | 89 | If you notice any gotchas with this setup, please [open an issue](https://github.com/maxproske/nextjs-docker-production-kit/issues/new) so I can add them to this list. 90 | 91 | ## Useful commands 92 | 93 | ```bash 94 | # Stop all running containers 95 | docker kill $(docker ps -aq) && docker rm $(docker ps -aq) 96 | 97 | # Free up space 98 | docker system prune -af --volumes 99 | ``` 100 | 101 | 102 | [fork-icon]: https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/svg/repo-forked.svg 103 | -------------------------------------------------------------------------------- /nginx/certs/example/archive/local.mproske.com/fullchain1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEXDCCA0SgAwIBAgISBGpthqxL9czlbtCInEjqO2IdMA0GCSqGSIb3DQEBCwUA 3 | MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD 4 | EwJSMzAeFw0yMjEyMTQwNTAyMDhaFw0yMzAzMTQwNTAyMDdaMBwxGjAYBgNVBAMT 5 | EWxvY2FsLm1wcm9za2UuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjFX+ 6 | 40rxS3aaCt1u7abdWBDNlvrfaA/Sv9IT73OlPxqeaPRWSln7xvdRd5PIoGmEP/41 7 | zpbHm620jeXM/a8Sr6OCAkswggJHMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAU 8 | BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUvrTS 9 | HlrsAUvto1KsitG0Nc1hQ9YwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsU 10 | wsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5j 11 | ci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wHAYDVR0R 12 | BBUwE4IRbG9jYWwubXByb3NrZS5jb20wTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYL 13 | KwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlw 14 | dC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgC3Pvsk35xNunXyOcW6WPRs 15 | XfxCz3qfNcSeHQmBJe20mQAAAYUPOd40AAAEAwBHMEUCIFEkCXsRofJHMxQEBi1w 16 | La8260zV4OzdzZ9zNUVru86bAiEA/H6CwcOd+9BJjHWrqg9KoHUQq9dZPWmasz1S 17 | e94ZkqEAdQDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYUPOd4p 18 | AAAEAwBGMEQCIGd7ddZ8HEVrCGzX77ArlEkj7/uNJVqbIK7QblueX5K0AiBSgdNz 19 | 7cmlgVNrNOVWkzohN95a79mNsh0rmGYY3wNdIDANBgkqhkiG9w0BAQsFAAOCAQEA 20 | Q0Pf05MKQ4G/ZJHVfvH0rbhq1eFzA3MkG19YopqrmZgx9uHkoB6iHU7UzPuD3wLT 21 | pz3sEjl5bTN/059iWk55aAevGqmG6WnAayef6g8tPYCksKnrC6upP3Yn/TY5FXSn 22 | rr/OvZ/qOSsLAFjL96S/fdgVbz5EHfsAEFtIzQN7GVar675KLLHkpBt1Kgk67eRA 23 | 6RTZVqqMp5LjK5RyhezJk/8RQN0rPhmbxBriJv8nYjbQ+wDVBKcGapyRoqSQhvl2 24 | QMG/3TN0HeI5hDQxmQLasZkCQjUd/36c3s6ttw9eikwqZ0VI6WoR/4ua9gHYfMOG 25 | mWE8w9aXIwOUSrd6GR4r5Q== 26 | -----END CERTIFICATE----- 27 | -----BEGIN CERTIFICATE----- 28 | MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw 29 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 30 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw 31 | WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg 32 | RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 33 | AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP 34 | R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx 35 | sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm 36 | NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg 37 | Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG 38 | /kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC 39 | AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB 40 | Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA 41 | FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw 42 | AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw 43 | Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB 44 | gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W 45 | PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl 46 | ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz 47 | CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm 48 | lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 49 | avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 50 | yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O 51 | yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids 52 | hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ 53 | HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv 54 | MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX 55 | nLRbwHOoq7hHwg== 56 | -----END CERTIFICATE----- 57 | -----BEGIN CERTIFICATE----- 58 | MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ 59 | MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT 60 | DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow 61 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 62 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB 63 | AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC 64 | ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL 65 | wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D 66 | LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK 67 | 4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5 68 | bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y 69 | sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ 70 | Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4 71 | FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc 72 | SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql 73 | PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND 74 | TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw 75 | SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1 76 | c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx 77 | +tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB 78 | ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu 79 | b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E 80 | U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu 81 | MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC 82 | 5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW 83 | 9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG 84 | WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O 85 | he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC 86 | Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 87 | -----END CERTIFICATE----- 88 | -------------------------------------------------------------------------------- /next-js-app/src/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 6rem; 7 | min-height: 100vh; 8 | } 9 | 10 | .description { 11 | display: inherit; 12 | justify-content: inherit; 13 | align-items: inherit; 14 | font-size: 0.85rem; 15 | max-width: var(--max-width); 16 | width: 100%; 17 | z-index: 2; 18 | font-family: var(--font-mono); 19 | } 20 | 21 | .description a { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | gap: 0.5rem; 26 | } 27 | 28 | .description p { 29 | position: relative; 30 | margin: 0; 31 | padding: 1rem; 32 | background-color: rgba(var(--callout-rgb), 0.5); 33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3); 34 | border-radius: var(--border-radius); 35 | } 36 | 37 | .code { 38 | font-weight: 700; 39 | font-family: var(--font-mono); 40 | } 41 | 42 | .grid { 43 | display: grid; 44 | grid-template-columns: repeat(3, minmax(33%, auto)); 45 | width: var(--max-width); 46 | max-width: 100%; 47 | } 48 | 49 | .card { 50 | padding: 1rem 1.2rem; 51 | border-radius: var(--border-radius); 52 | background: rgba(var(--card-rgb), 0); 53 | border: 1px solid rgba(var(--card-border-rgb), 0); 54 | transition: background 200ms, border 200ms; 55 | } 56 | 57 | .card span { 58 | display: inline-block; 59 | transition: transform 200ms; 60 | } 61 | 62 | .card h2 { 63 | font-weight: 600; 64 | margin-bottom: 0.7rem; 65 | } 66 | 67 | .card p { 68 | margin: 0; 69 | opacity: 0.6; 70 | font-size: 0.9rem; 71 | line-height: 1.5; 72 | max-width: 34ch; 73 | } 74 | 75 | .center { 76 | display: flex; 77 | justify-content: center; 78 | align-items: center; 79 | position: relative; 80 | padding: 4rem 0; 81 | } 82 | 83 | .center::before { 84 | background: var(--secondary-glow); 85 | border-radius: 50%; 86 | width: 480px; 87 | height: 360px; 88 | margin-left: -400px; 89 | } 90 | 91 | .center::after { 92 | background: var(--primary-glow); 93 | width: 240px; 94 | height: 180px; 95 | z-index: -1; 96 | } 97 | 98 | .center::before, 99 | .center::after { 100 | content: ""; 101 | left: 50%; 102 | position: absolute; 103 | filter: blur(45px); 104 | transform: translateZ(0); 105 | } 106 | 107 | .logo, 108 | .thirteen { 109 | position: relative; 110 | } 111 | 112 | .thirteen { 113 | display: flex; 114 | justify-content: center; 115 | align-items: center; 116 | width: 75px; 117 | height: 75px; 118 | padding: 25px 10px; 119 | margin-left: 16px; 120 | transform: translateZ(0); 121 | border-radius: var(--border-radius); 122 | overflow: hidden; 123 | /* box-shadow: 0px 2px 8px -1px #0000001a; */ 124 | } 125 | 126 | .thirteen:hover { 127 | cursor: pointer; 128 | } 129 | 130 | .thirteen::before, 131 | .thirteen::after { 132 | content: ""; 133 | position: absolute; 134 | z-index: -1; 135 | } 136 | 137 | /* Conic Gradient Animation */ 138 | .thirteen::before { 139 | animation: 6s rotate linear infinite; 140 | width: 200%; 141 | height: 200%; 142 | background: var(--tile-border); 143 | } 144 | 145 | /* Inner Square */ 146 | .thirteen::after { 147 | inset: 0; 148 | padding: 1px; 149 | border-radius: var(--border-radius); 150 | background: linear-gradient( 151 | to bottom right, 152 | rgba(var(--tile-start-rgb), 1), 153 | rgba(var(--tile-end-rgb), 1) 154 | ); 155 | background-clip: content-box; 156 | } 157 | 158 | /* Enable hover only on non-touch devices */ 159 | @media (hover: hover) and (pointer: fine) { 160 | .card:hover { 161 | background: rgba(var(--card-rgb), 0.1); 162 | border: 1px solid rgba(var(--card-border-rgb), 0.15); 163 | } 164 | 165 | .card:hover span { 166 | transform: translateX(4px); 167 | } 168 | } 169 | 170 | @media (prefers-reduced-motion) { 171 | .thirteen::before { 172 | animation: none; 173 | } 174 | 175 | .card:hover span { 176 | transform: none; 177 | } 178 | } 179 | 180 | /* Mobile and Tablet */ 181 | @media (max-width: 1023px) { 182 | .content { 183 | padding: 4rem; 184 | } 185 | 186 | .grid { 187 | grid-template-columns: 1fr; 188 | margin-bottom: 120px; 189 | max-width: 320px; 190 | text-align: center; 191 | } 192 | 193 | .card { 194 | padding: 1rem 2.5rem; 195 | } 196 | 197 | .card h2 { 198 | margin-bottom: 0.5rem; 199 | } 200 | 201 | .center { 202 | padding: 8rem 0 6rem; 203 | } 204 | 205 | .center::before { 206 | transform: none; 207 | height: 300px; 208 | } 209 | 210 | .description { 211 | font-size: 0.8rem; 212 | } 213 | 214 | .description a { 215 | padding: 1rem; 216 | } 217 | 218 | .description p, 219 | .description div { 220 | display: flex; 221 | justify-content: center; 222 | position: fixed; 223 | width: 100%; 224 | } 225 | 226 | .description p { 227 | align-items: center; 228 | inset: 0 0 auto; 229 | padding: 2rem 1rem 1.4rem; 230 | border-radius: 0; 231 | border: none; 232 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); 233 | background: linear-gradient( 234 | to bottom, 235 | rgba(var(--background-start-rgb), 1), 236 | rgba(var(--callout-rgb), 0.5) 237 | ); 238 | background-clip: padding-box; 239 | backdrop-filter: blur(24px); 240 | } 241 | 242 | .description div { 243 | align-items: flex-end; 244 | pointer-events: none; 245 | inset: auto 0 0; 246 | padding: 2rem; 247 | height: 200px; 248 | background: linear-gradient( 249 | to bottom, 250 | transparent 0%, 251 | rgb(var(--background-end-rgb)) 40% 252 | ); 253 | z-index: 1; 254 | } 255 | } 256 | 257 | @media (prefers-color-scheme: dark) { 258 | .vercelLogo { 259 | filter: invert(1); 260 | } 261 | 262 | .logo { 263 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); 264 | } 265 | } 266 | 267 | @keyframes rotate { 268 | from { 269 | transform: rotate(360deg); 270 | } 271 | to { 272 | transform: rotate(0deg); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /next-ts-app/src/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 6rem; 7 | min-height: 100vh; 8 | } 9 | 10 | .description { 11 | display: inherit; 12 | justify-content: inherit; 13 | align-items: inherit; 14 | font-size: 0.85rem; 15 | max-width: var(--max-width); 16 | width: 100%; 17 | z-index: 2; 18 | font-family: var(--font-mono); 19 | } 20 | 21 | .description a { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | gap: 0.5rem; 26 | } 27 | 28 | .description p { 29 | position: relative; 30 | margin: 0; 31 | padding: 1rem; 32 | background-color: rgba(var(--callout-rgb), 0.5); 33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3); 34 | border-radius: var(--border-radius); 35 | } 36 | 37 | .code { 38 | font-weight: 700; 39 | font-family: var(--font-mono); 40 | } 41 | 42 | .grid { 43 | display: grid; 44 | grid-template-columns: repeat(3, minmax(33%, auto)); 45 | width: var(--max-width); 46 | max-width: 100%; 47 | } 48 | 49 | .card { 50 | padding: 1rem 1.2rem; 51 | border-radius: var(--border-radius); 52 | background: rgba(var(--card-rgb), 0); 53 | border: 1px solid rgba(var(--card-border-rgb), 0); 54 | transition: background 200ms, border 200ms; 55 | } 56 | 57 | .card span { 58 | display: inline-block; 59 | transition: transform 200ms; 60 | } 61 | 62 | .card h2 { 63 | font-weight: 600; 64 | margin-bottom: 0.7rem; 65 | } 66 | 67 | .card p { 68 | margin: 0; 69 | opacity: 0.6; 70 | font-size: 0.9rem; 71 | line-height: 1.5; 72 | max-width: 34ch; 73 | } 74 | 75 | .center { 76 | display: flex; 77 | justify-content: center; 78 | align-items: center; 79 | position: relative; 80 | padding: 4rem 0; 81 | } 82 | 83 | .center::before { 84 | background: var(--secondary-glow); 85 | border-radius: 50%; 86 | width: 480px; 87 | height: 360px; 88 | margin-left: -400px; 89 | } 90 | 91 | .center::after { 92 | background: var(--primary-glow); 93 | width: 240px; 94 | height: 180px; 95 | z-index: -1; 96 | } 97 | 98 | .center::before, 99 | .center::after { 100 | content: ""; 101 | left: 50%; 102 | position: absolute; 103 | filter: blur(45px); 104 | transform: translateZ(0); 105 | } 106 | 107 | .logo, 108 | .thirteen { 109 | position: relative; 110 | } 111 | 112 | .thirteen { 113 | display: flex; 114 | justify-content: center; 115 | align-items: center; 116 | width: 75px; 117 | height: 75px; 118 | padding: 25px 10px; 119 | margin-left: 16px; 120 | transform: translateZ(0); 121 | border-radius: var(--border-radius); 122 | overflow: hidden; 123 | /* box-shadow: 0px 2px 8px -1px #0000001a; */ 124 | } 125 | 126 | .thirteen:hover { 127 | cursor: pointer; 128 | } 129 | 130 | .thirteen::before, 131 | .thirteen::after { 132 | content: ""; 133 | position: absolute; 134 | z-index: -1; 135 | } 136 | 137 | /* Conic Gradient Animation */ 138 | .thirteen::before { 139 | animation: 6s rotate linear infinite; 140 | width: 200%; 141 | height: 200%; 142 | background: var(--tile-border); 143 | } 144 | 145 | /* Inner Square */ 146 | .thirteen::after { 147 | inset: 0; 148 | padding: 1px; 149 | border-radius: var(--border-radius); 150 | background: linear-gradient( 151 | to bottom right, 152 | rgba(var(--tile-start-rgb), 1), 153 | rgba(var(--tile-end-rgb), 1) 154 | ); 155 | background-clip: content-box; 156 | } 157 | 158 | /* Enable hover only on non-touch devices */ 159 | @media (hover: hover) and (pointer: fine) { 160 | .card:hover { 161 | background: rgba(var(--card-rgb), 0.1); 162 | border: 1px solid rgba(var(--card-border-rgb), 0.15); 163 | } 164 | 165 | .card:hover span { 166 | transform: translateX(4px); 167 | } 168 | } 169 | 170 | @media (prefers-reduced-motion) { 171 | .thirteen::before { 172 | animation: none; 173 | } 174 | 175 | .card:hover span { 176 | transform: none; 177 | } 178 | } 179 | 180 | /* Mobile and Tablet */ 181 | @media (max-width: 1023px) { 182 | .content { 183 | padding: 4rem; 184 | } 185 | 186 | .grid { 187 | grid-template-columns: 1fr; 188 | margin-bottom: 120px; 189 | max-width: 320px; 190 | text-align: center; 191 | } 192 | 193 | .card { 194 | padding: 1rem 2.5rem; 195 | } 196 | 197 | .card h2 { 198 | margin-bottom: 0.5rem; 199 | } 200 | 201 | .center { 202 | padding: 8rem 0 6rem; 203 | } 204 | 205 | .center::before { 206 | transform: none; 207 | height: 300px; 208 | } 209 | 210 | .description { 211 | font-size: 0.8rem; 212 | } 213 | 214 | .description a { 215 | padding: 1rem; 216 | } 217 | 218 | .description p, 219 | .description div { 220 | display: flex; 221 | justify-content: center; 222 | position: fixed; 223 | width: 100%; 224 | } 225 | 226 | .description p { 227 | align-items: center; 228 | inset: 0 0 auto; 229 | padding: 2rem 1rem 1.4rem; 230 | border-radius: 0; 231 | border: none; 232 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); 233 | background: linear-gradient( 234 | to bottom, 235 | rgba(var(--background-start-rgb), 1), 236 | rgba(var(--callout-rgb), 0.5) 237 | ); 238 | background-clip: padding-box; 239 | backdrop-filter: blur(24px); 240 | } 241 | 242 | .description div { 243 | align-items: flex-end; 244 | pointer-events: none; 245 | inset: auto 0 0; 246 | padding: 2rem; 247 | height: 200px; 248 | background: linear-gradient( 249 | to bottom, 250 | transparent 0%, 251 | rgb(var(--background-end-rgb)) 40% 252 | ); 253 | z-index: 1; 254 | } 255 | } 256 | 257 | @media (prefers-color-scheme: dark) { 258 | .vercelLogo { 259 | filter: invert(1); 260 | } 261 | 262 | .logo { 263 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); 264 | } 265 | } 266 | 267 | @keyframes rotate { 268 | from { 269 | transform: rotate(360deg); 270 | } 271 | to { 272 | transform: rotate(0deg); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /next-ts-app/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | next: 12 | specifier: ^15.0.3 13 | version: 15.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 14 | postgres: 15 | specifier: ^3.4.5 16 | version: 3.4.5 17 | react: 18 | specifier: ^18.3.1 19 | version: 18.3.1 20 | react-dom: 21 | specifier: ^18.3.1 22 | version: 18.3.1(react@18.3.1) 23 | devDependencies: 24 | '@types/node': 25 | specifier: ^22.9.0 26 | version: 22.9.0 27 | '@types/react': 28 | specifier: ^18.3.12 29 | version: 18.3.12 30 | '@types/react-dom': 31 | specifier: ^18.3.1 32 | version: 18.3.1 33 | typescript: 34 | specifier: ^5.6.3 35 | version: 5.6.3 36 | 37 | packages: 38 | 39 | '@emnapi/runtime@1.3.1': 40 | resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} 41 | 42 | '@img/sharp-darwin-arm64@0.33.5': 43 | resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} 44 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 45 | cpu: [arm64] 46 | os: [darwin] 47 | 48 | '@img/sharp-darwin-x64@0.33.5': 49 | resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} 50 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 51 | cpu: [x64] 52 | os: [darwin] 53 | 54 | '@img/sharp-libvips-darwin-arm64@1.0.4': 55 | resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} 56 | cpu: [arm64] 57 | os: [darwin] 58 | 59 | '@img/sharp-libvips-darwin-x64@1.0.4': 60 | resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} 61 | cpu: [x64] 62 | os: [darwin] 63 | 64 | '@img/sharp-libvips-linux-arm64@1.0.4': 65 | resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} 66 | cpu: [arm64] 67 | os: [linux] 68 | 69 | '@img/sharp-libvips-linux-arm@1.0.5': 70 | resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} 71 | cpu: [arm] 72 | os: [linux] 73 | 74 | '@img/sharp-libvips-linux-s390x@1.0.4': 75 | resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} 76 | cpu: [s390x] 77 | os: [linux] 78 | 79 | '@img/sharp-libvips-linux-x64@1.0.4': 80 | resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} 81 | cpu: [x64] 82 | os: [linux] 83 | 84 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 85 | resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} 86 | cpu: [arm64] 87 | os: [linux] 88 | 89 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 90 | resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} 91 | cpu: [x64] 92 | os: [linux] 93 | 94 | '@img/sharp-linux-arm64@0.33.5': 95 | resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} 96 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 97 | cpu: [arm64] 98 | os: [linux] 99 | 100 | '@img/sharp-linux-arm@0.33.5': 101 | resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} 102 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 103 | cpu: [arm] 104 | os: [linux] 105 | 106 | '@img/sharp-linux-s390x@0.33.5': 107 | resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} 108 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 109 | cpu: [s390x] 110 | os: [linux] 111 | 112 | '@img/sharp-linux-x64@0.33.5': 113 | resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} 114 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 115 | cpu: [x64] 116 | os: [linux] 117 | 118 | '@img/sharp-linuxmusl-arm64@0.33.5': 119 | resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} 120 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 121 | cpu: [arm64] 122 | os: [linux] 123 | 124 | '@img/sharp-linuxmusl-x64@0.33.5': 125 | resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} 126 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 127 | cpu: [x64] 128 | os: [linux] 129 | 130 | '@img/sharp-wasm32@0.33.5': 131 | resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} 132 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 133 | cpu: [wasm32] 134 | 135 | '@img/sharp-win32-ia32@0.33.5': 136 | resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} 137 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 138 | cpu: [ia32] 139 | os: [win32] 140 | 141 | '@img/sharp-win32-x64@0.33.5': 142 | resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} 143 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 144 | cpu: [x64] 145 | os: [win32] 146 | 147 | '@next/env@15.0.3': 148 | resolution: {integrity: sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==} 149 | 150 | '@next/swc-darwin-arm64@15.0.3': 151 | resolution: {integrity: sha512-s3Q/NOorCsLYdCKvQlWU+a+GeAd3C8Rb3L1YnetsgwXzhc3UTWrtQpB/3eCjFOdGUj5QmXfRak12uocd1ZiiQw==} 152 | engines: {node: '>= 10'} 153 | cpu: [arm64] 154 | os: [darwin] 155 | 156 | '@next/swc-darwin-x64@15.0.3': 157 | resolution: {integrity: sha512-Zxl/TwyXVZPCFSf0u2BNj5sE0F2uR6iSKxWpq4Wlk/Sv9Ob6YCKByQTkV2y6BCic+fkabp9190hyrDdPA/dNrw==} 158 | engines: {node: '>= 10'} 159 | cpu: [x64] 160 | os: [darwin] 161 | 162 | '@next/swc-linux-arm64-gnu@15.0.3': 163 | resolution: {integrity: sha512-T5+gg2EwpsY3OoaLxUIofmMb7ohAUlcNZW0fPQ6YAutaWJaxt1Z1h+8zdl4FRIOr5ABAAhXtBcpkZNwUcKI2fw==} 164 | engines: {node: '>= 10'} 165 | cpu: [arm64] 166 | os: [linux] 167 | 168 | '@next/swc-linux-arm64-musl@15.0.3': 169 | resolution: {integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==} 170 | engines: {node: '>= 10'} 171 | cpu: [arm64] 172 | os: [linux] 173 | 174 | '@next/swc-linux-x64-gnu@15.0.3': 175 | resolution: {integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==} 176 | engines: {node: '>= 10'} 177 | cpu: [x64] 178 | os: [linux] 179 | 180 | '@next/swc-linux-x64-musl@15.0.3': 181 | resolution: {integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==} 182 | engines: {node: '>= 10'} 183 | cpu: [x64] 184 | os: [linux] 185 | 186 | '@next/swc-win32-arm64-msvc@15.0.3': 187 | resolution: {integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==} 188 | engines: {node: '>= 10'} 189 | cpu: [arm64] 190 | os: [win32] 191 | 192 | '@next/swc-win32-x64-msvc@15.0.3': 193 | resolution: {integrity: sha512-VNAz+HN4OGgvZs6MOoVfnn41kBzT+M+tB+OK4cww6DNyWS6wKaDpaAm/qLeOUbnMh0oVx1+mg0uoYARF69dJyA==} 194 | engines: {node: '>= 10'} 195 | cpu: [x64] 196 | os: [win32] 197 | 198 | '@swc/counter@0.1.3': 199 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 200 | 201 | '@swc/helpers@0.5.13': 202 | resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} 203 | 204 | '@types/node@22.9.0': 205 | resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} 206 | 207 | '@types/prop-types@15.7.13': 208 | resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} 209 | 210 | '@types/react-dom@18.3.1': 211 | resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} 212 | 213 | '@types/react@18.3.12': 214 | resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} 215 | 216 | busboy@1.6.0: 217 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 218 | engines: {node: '>=10.16.0'} 219 | 220 | caniuse-lite@1.0.30001680: 221 | resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} 222 | 223 | client-only@0.0.1: 224 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 225 | 226 | color-convert@2.0.1: 227 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 228 | engines: {node: '>=7.0.0'} 229 | 230 | color-name@1.1.4: 231 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 232 | 233 | color-string@1.9.1: 234 | resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} 235 | 236 | color@4.2.3: 237 | resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} 238 | engines: {node: '>=12.5.0'} 239 | 240 | csstype@3.1.3: 241 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 242 | 243 | detect-libc@2.0.3: 244 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} 245 | engines: {node: '>=8'} 246 | 247 | is-arrayish@0.3.2: 248 | resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} 249 | 250 | js-tokens@4.0.0: 251 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 252 | 253 | loose-envify@1.4.0: 254 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 255 | hasBin: true 256 | 257 | nanoid@3.3.7: 258 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 259 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 260 | hasBin: true 261 | 262 | next@15.0.3: 263 | resolution: {integrity: sha512-ontCbCRKJUIoivAdGB34yCaOcPgYXr9AAkV/IwqFfWWTXEPUgLYkSkqBhIk9KK7gGmgjc64B+RdoeIDM13Irnw==} 264 | engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} 265 | hasBin: true 266 | peerDependencies: 267 | '@opentelemetry/api': ^1.1.0 268 | '@playwright/test': ^1.41.2 269 | babel-plugin-react-compiler: '*' 270 | react: ^18.2.0 || 19.0.0-rc-66855b96-20241106 271 | react-dom: ^18.2.0 || 19.0.0-rc-66855b96-20241106 272 | sass: ^1.3.0 273 | peerDependenciesMeta: 274 | '@opentelemetry/api': 275 | optional: true 276 | '@playwright/test': 277 | optional: true 278 | babel-plugin-react-compiler: 279 | optional: true 280 | sass: 281 | optional: true 282 | 283 | picocolors@1.1.1: 284 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 285 | 286 | postcss@8.4.31: 287 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 288 | engines: {node: ^10 || ^12 || >=14} 289 | 290 | postgres@3.4.5: 291 | resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} 292 | engines: {node: '>=12'} 293 | 294 | react-dom@18.3.1: 295 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 296 | peerDependencies: 297 | react: ^18.3.1 298 | 299 | react@18.3.1: 300 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} 301 | engines: {node: '>=0.10.0'} 302 | 303 | scheduler@0.23.2: 304 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} 305 | 306 | semver@7.6.3: 307 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 308 | engines: {node: '>=10'} 309 | hasBin: true 310 | 311 | sharp@0.33.5: 312 | resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} 313 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 314 | 315 | simple-swizzle@0.2.2: 316 | resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} 317 | 318 | source-map-js@1.2.1: 319 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 320 | engines: {node: '>=0.10.0'} 321 | 322 | streamsearch@1.1.0: 323 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 324 | engines: {node: '>=10.0.0'} 325 | 326 | styled-jsx@5.1.6: 327 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 328 | engines: {node: '>= 12.0.0'} 329 | peerDependencies: 330 | '@babel/core': '*' 331 | babel-plugin-macros: '*' 332 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 333 | peerDependenciesMeta: 334 | '@babel/core': 335 | optional: true 336 | babel-plugin-macros: 337 | optional: true 338 | 339 | tslib@2.8.1: 340 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 341 | 342 | typescript@5.6.3: 343 | resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} 344 | engines: {node: '>=14.17'} 345 | hasBin: true 346 | 347 | undici-types@6.19.8: 348 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 349 | 350 | snapshots: 351 | 352 | '@emnapi/runtime@1.3.1': 353 | dependencies: 354 | tslib: 2.8.1 355 | optional: true 356 | 357 | '@img/sharp-darwin-arm64@0.33.5': 358 | optionalDependencies: 359 | '@img/sharp-libvips-darwin-arm64': 1.0.4 360 | optional: true 361 | 362 | '@img/sharp-darwin-x64@0.33.5': 363 | optionalDependencies: 364 | '@img/sharp-libvips-darwin-x64': 1.0.4 365 | optional: true 366 | 367 | '@img/sharp-libvips-darwin-arm64@1.0.4': 368 | optional: true 369 | 370 | '@img/sharp-libvips-darwin-x64@1.0.4': 371 | optional: true 372 | 373 | '@img/sharp-libvips-linux-arm64@1.0.4': 374 | optional: true 375 | 376 | '@img/sharp-libvips-linux-arm@1.0.5': 377 | optional: true 378 | 379 | '@img/sharp-libvips-linux-s390x@1.0.4': 380 | optional: true 381 | 382 | '@img/sharp-libvips-linux-x64@1.0.4': 383 | optional: true 384 | 385 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 386 | optional: true 387 | 388 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 389 | optional: true 390 | 391 | '@img/sharp-linux-arm64@0.33.5': 392 | optionalDependencies: 393 | '@img/sharp-libvips-linux-arm64': 1.0.4 394 | optional: true 395 | 396 | '@img/sharp-linux-arm@0.33.5': 397 | optionalDependencies: 398 | '@img/sharp-libvips-linux-arm': 1.0.5 399 | optional: true 400 | 401 | '@img/sharp-linux-s390x@0.33.5': 402 | optionalDependencies: 403 | '@img/sharp-libvips-linux-s390x': 1.0.4 404 | optional: true 405 | 406 | '@img/sharp-linux-x64@0.33.5': 407 | optionalDependencies: 408 | '@img/sharp-libvips-linux-x64': 1.0.4 409 | optional: true 410 | 411 | '@img/sharp-linuxmusl-arm64@0.33.5': 412 | optionalDependencies: 413 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 414 | optional: true 415 | 416 | '@img/sharp-linuxmusl-x64@0.33.5': 417 | optionalDependencies: 418 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 419 | optional: true 420 | 421 | '@img/sharp-wasm32@0.33.5': 422 | dependencies: 423 | '@emnapi/runtime': 1.3.1 424 | optional: true 425 | 426 | '@img/sharp-win32-ia32@0.33.5': 427 | optional: true 428 | 429 | '@img/sharp-win32-x64@0.33.5': 430 | optional: true 431 | 432 | '@next/env@15.0.3': {} 433 | 434 | '@next/swc-darwin-arm64@15.0.3': 435 | optional: true 436 | 437 | '@next/swc-darwin-x64@15.0.3': 438 | optional: true 439 | 440 | '@next/swc-linux-arm64-gnu@15.0.3': 441 | optional: true 442 | 443 | '@next/swc-linux-arm64-musl@15.0.3': 444 | optional: true 445 | 446 | '@next/swc-linux-x64-gnu@15.0.3': 447 | optional: true 448 | 449 | '@next/swc-linux-x64-musl@15.0.3': 450 | optional: true 451 | 452 | '@next/swc-win32-arm64-msvc@15.0.3': 453 | optional: true 454 | 455 | '@next/swc-win32-x64-msvc@15.0.3': 456 | optional: true 457 | 458 | '@swc/counter@0.1.3': {} 459 | 460 | '@swc/helpers@0.5.13': 461 | dependencies: 462 | tslib: 2.8.1 463 | 464 | '@types/node@22.9.0': 465 | dependencies: 466 | undici-types: 6.19.8 467 | 468 | '@types/prop-types@15.7.13': {} 469 | 470 | '@types/react-dom@18.3.1': 471 | dependencies: 472 | '@types/react': 18.3.12 473 | 474 | '@types/react@18.3.12': 475 | dependencies: 476 | '@types/prop-types': 15.7.13 477 | csstype: 3.1.3 478 | 479 | busboy@1.6.0: 480 | dependencies: 481 | streamsearch: 1.1.0 482 | 483 | caniuse-lite@1.0.30001680: {} 484 | 485 | client-only@0.0.1: {} 486 | 487 | color-convert@2.0.1: 488 | dependencies: 489 | color-name: 1.1.4 490 | optional: true 491 | 492 | color-name@1.1.4: 493 | optional: true 494 | 495 | color-string@1.9.1: 496 | dependencies: 497 | color-name: 1.1.4 498 | simple-swizzle: 0.2.2 499 | optional: true 500 | 501 | color@4.2.3: 502 | dependencies: 503 | color-convert: 2.0.1 504 | color-string: 1.9.1 505 | optional: true 506 | 507 | csstype@3.1.3: {} 508 | 509 | detect-libc@2.0.3: 510 | optional: true 511 | 512 | is-arrayish@0.3.2: 513 | optional: true 514 | 515 | js-tokens@4.0.0: {} 516 | 517 | loose-envify@1.4.0: 518 | dependencies: 519 | js-tokens: 4.0.0 520 | 521 | nanoid@3.3.7: {} 522 | 523 | next@15.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 524 | dependencies: 525 | '@next/env': 15.0.3 526 | '@swc/counter': 0.1.3 527 | '@swc/helpers': 0.5.13 528 | busboy: 1.6.0 529 | caniuse-lite: 1.0.30001680 530 | postcss: 8.4.31 531 | react: 18.3.1 532 | react-dom: 18.3.1(react@18.3.1) 533 | styled-jsx: 5.1.6(react@18.3.1) 534 | optionalDependencies: 535 | '@next/swc-darwin-arm64': 15.0.3 536 | '@next/swc-darwin-x64': 15.0.3 537 | '@next/swc-linux-arm64-gnu': 15.0.3 538 | '@next/swc-linux-arm64-musl': 15.0.3 539 | '@next/swc-linux-x64-gnu': 15.0.3 540 | '@next/swc-linux-x64-musl': 15.0.3 541 | '@next/swc-win32-arm64-msvc': 15.0.3 542 | '@next/swc-win32-x64-msvc': 15.0.3 543 | sharp: 0.33.5 544 | transitivePeerDependencies: 545 | - '@babel/core' 546 | - babel-plugin-macros 547 | 548 | picocolors@1.1.1: {} 549 | 550 | postcss@8.4.31: 551 | dependencies: 552 | nanoid: 3.3.7 553 | picocolors: 1.1.1 554 | source-map-js: 1.2.1 555 | 556 | postgres@3.4.5: {} 557 | 558 | react-dom@18.3.1(react@18.3.1): 559 | dependencies: 560 | loose-envify: 1.4.0 561 | react: 18.3.1 562 | scheduler: 0.23.2 563 | 564 | react@18.3.1: 565 | dependencies: 566 | loose-envify: 1.4.0 567 | 568 | scheduler@0.23.2: 569 | dependencies: 570 | loose-envify: 1.4.0 571 | 572 | semver@7.6.3: 573 | optional: true 574 | 575 | sharp@0.33.5: 576 | dependencies: 577 | color: 4.2.3 578 | detect-libc: 2.0.3 579 | semver: 7.6.3 580 | optionalDependencies: 581 | '@img/sharp-darwin-arm64': 0.33.5 582 | '@img/sharp-darwin-x64': 0.33.5 583 | '@img/sharp-libvips-darwin-arm64': 1.0.4 584 | '@img/sharp-libvips-darwin-x64': 1.0.4 585 | '@img/sharp-libvips-linux-arm': 1.0.5 586 | '@img/sharp-libvips-linux-arm64': 1.0.4 587 | '@img/sharp-libvips-linux-s390x': 1.0.4 588 | '@img/sharp-libvips-linux-x64': 1.0.4 589 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 590 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 591 | '@img/sharp-linux-arm': 0.33.5 592 | '@img/sharp-linux-arm64': 0.33.5 593 | '@img/sharp-linux-s390x': 0.33.5 594 | '@img/sharp-linux-x64': 0.33.5 595 | '@img/sharp-linuxmusl-arm64': 0.33.5 596 | '@img/sharp-linuxmusl-x64': 0.33.5 597 | '@img/sharp-wasm32': 0.33.5 598 | '@img/sharp-win32-ia32': 0.33.5 599 | '@img/sharp-win32-x64': 0.33.5 600 | optional: true 601 | 602 | simple-swizzle@0.2.2: 603 | dependencies: 604 | is-arrayish: 0.3.2 605 | optional: true 606 | 607 | source-map-js@1.2.1: {} 608 | 609 | streamsearch@1.1.0: {} 610 | 611 | styled-jsx@5.1.6(react@18.3.1): 612 | dependencies: 613 | client-only: 0.0.1 614 | react: 18.3.1 615 | 616 | tslib@2.8.1: {} 617 | 618 | typescript@5.6.3: {} 619 | 620 | undici-types@6.19.8: {} 621 | -------------------------------------------------------------------------------- /next-js-app/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | next: 12 | specifier: ^15.0.3 13 | version: 15.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 14 | postgres: 15 | specifier: ^3.4.5 16 | version: 3.4.5 17 | react: 18 | specifier: ^18.3.1 19 | version: 18.3.1 20 | react-dom: 21 | specifier: ^18.3.1 22 | version: 18.3.1(react@18.3.1) 23 | devDependencies: 24 | '@eslint/compat': 25 | specifier: ^1.2.3 26 | version: 1.2.3(eslint@9.15.0) 27 | '@eslint/js': 28 | specifier: ^9.15.0 29 | version: 9.15.0 30 | '@types/eslint__js': 31 | specifier: ^8.42.3 32 | version: 8.42.3 33 | eslint: 34 | specifier: ^9.15.0 35 | version: 9.15.0 36 | eslint-plugin-react-hooks: 37 | specifier: ^5.0.0 38 | version: 5.0.0(eslint@9.15.0) 39 | typescript: 40 | specifier: ^5.6.3 41 | version: 5.6.3 42 | typescript-eslint: 43 | specifier: ^8.14.0 44 | version: 8.14.0(eslint@9.15.0)(typescript@5.6.3) 45 | 46 | packages: 47 | 48 | '@emnapi/runtime@1.3.1': 49 | resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} 50 | 51 | '@eslint-community/eslint-utils@4.4.1': 52 | resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} 53 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 54 | peerDependencies: 55 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 56 | 57 | '@eslint-community/regexpp@4.12.1': 58 | resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 59 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 60 | 61 | '@eslint/compat@1.2.3': 62 | resolution: {integrity: sha512-wlZhwlDFxkxIZ571aH0FoK4h4Vwx7P3HJx62Gp8hTc10bfpwT2x0nULuAHmQSJBOWPgPeVf+9YtnD4j50zVHmA==} 63 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 64 | peerDependencies: 65 | eslint: ^9.10.0 66 | peerDependenciesMeta: 67 | eslint: 68 | optional: true 69 | 70 | '@eslint/config-array@0.19.0': 71 | resolution: {integrity: sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==} 72 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 73 | 74 | '@eslint/core@0.9.0': 75 | resolution: {integrity: sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==} 76 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 77 | 78 | '@eslint/eslintrc@3.2.0': 79 | resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} 80 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 81 | 82 | '@eslint/js@9.15.0': 83 | resolution: {integrity: sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==} 84 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 85 | 86 | '@eslint/object-schema@2.1.4': 87 | resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} 88 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 89 | 90 | '@eslint/plugin-kit@0.2.3': 91 | resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==} 92 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 93 | 94 | '@humanfs/core@0.19.1': 95 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 96 | engines: {node: '>=18.18.0'} 97 | 98 | '@humanfs/node@0.16.6': 99 | resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} 100 | engines: {node: '>=18.18.0'} 101 | 102 | '@humanwhocodes/module-importer@1.0.1': 103 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 104 | engines: {node: '>=12.22'} 105 | 106 | '@humanwhocodes/retry@0.3.1': 107 | resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} 108 | engines: {node: '>=18.18'} 109 | 110 | '@humanwhocodes/retry@0.4.1': 111 | resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} 112 | engines: {node: '>=18.18'} 113 | 114 | '@img/sharp-darwin-arm64@0.33.5': 115 | resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} 116 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 117 | cpu: [arm64] 118 | os: [darwin] 119 | 120 | '@img/sharp-darwin-x64@0.33.5': 121 | resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} 122 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 123 | cpu: [x64] 124 | os: [darwin] 125 | 126 | '@img/sharp-libvips-darwin-arm64@1.0.4': 127 | resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} 128 | cpu: [arm64] 129 | os: [darwin] 130 | 131 | '@img/sharp-libvips-darwin-x64@1.0.4': 132 | resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} 133 | cpu: [x64] 134 | os: [darwin] 135 | 136 | '@img/sharp-libvips-linux-arm64@1.0.4': 137 | resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} 138 | cpu: [arm64] 139 | os: [linux] 140 | 141 | '@img/sharp-libvips-linux-arm@1.0.5': 142 | resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} 143 | cpu: [arm] 144 | os: [linux] 145 | 146 | '@img/sharp-libvips-linux-s390x@1.0.4': 147 | resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} 148 | cpu: [s390x] 149 | os: [linux] 150 | 151 | '@img/sharp-libvips-linux-x64@1.0.4': 152 | resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} 153 | cpu: [x64] 154 | os: [linux] 155 | 156 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 157 | resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} 158 | cpu: [arm64] 159 | os: [linux] 160 | 161 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 162 | resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} 163 | cpu: [x64] 164 | os: [linux] 165 | 166 | '@img/sharp-linux-arm64@0.33.5': 167 | resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} 168 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 169 | cpu: [arm64] 170 | os: [linux] 171 | 172 | '@img/sharp-linux-arm@0.33.5': 173 | resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} 174 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 175 | cpu: [arm] 176 | os: [linux] 177 | 178 | '@img/sharp-linux-s390x@0.33.5': 179 | resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} 180 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 181 | cpu: [s390x] 182 | os: [linux] 183 | 184 | '@img/sharp-linux-x64@0.33.5': 185 | resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} 186 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 187 | cpu: [x64] 188 | os: [linux] 189 | 190 | '@img/sharp-linuxmusl-arm64@0.33.5': 191 | resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} 192 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 193 | cpu: [arm64] 194 | os: [linux] 195 | 196 | '@img/sharp-linuxmusl-x64@0.33.5': 197 | resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} 198 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 199 | cpu: [x64] 200 | os: [linux] 201 | 202 | '@img/sharp-wasm32@0.33.5': 203 | resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} 204 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 205 | cpu: [wasm32] 206 | 207 | '@img/sharp-win32-ia32@0.33.5': 208 | resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} 209 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 210 | cpu: [ia32] 211 | os: [win32] 212 | 213 | '@img/sharp-win32-x64@0.33.5': 214 | resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} 215 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 216 | cpu: [x64] 217 | os: [win32] 218 | 219 | '@next/env@15.0.3': 220 | resolution: {integrity: sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==} 221 | 222 | '@next/swc-darwin-arm64@15.0.3': 223 | resolution: {integrity: sha512-s3Q/NOorCsLYdCKvQlWU+a+GeAd3C8Rb3L1YnetsgwXzhc3UTWrtQpB/3eCjFOdGUj5QmXfRak12uocd1ZiiQw==} 224 | engines: {node: '>= 10'} 225 | cpu: [arm64] 226 | os: [darwin] 227 | 228 | '@next/swc-darwin-x64@15.0.3': 229 | resolution: {integrity: sha512-Zxl/TwyXVZPCFSf0u2BNj5sE0F2uR6iSKxWpq4Wlk/Sv9Ob6YCKByQTkV2y6BCic+fkabp9190hyrDdPA/dNrw==} 230 | engines: {node: '>= 10'} 231 | cpu: [x64] 232 | os: [darwin] 233 | 234 | '@next/swc-linux-arm64-gnu@15.0.3': 235 | resolution: {integrity: sha512-T5+gg2EwpsY3OoaLxUIofmMb7ohAUlcNZW0fPQ6YAutaWJaxt1Z1h+8zdl4FRIOr5ABAAhXtBcpkZNwUcKI2fw==} 236 | engines: {node: '>= 10'} 237 | cpu: [arm64] 238 | os: [linux] 239 | 240 | '@next/swc-linux-arm64-musl@15.0.3': 241 | resolution: {integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==} 242 | engines: {node: '>= 10'} 243 | cpu: [arm64] 244 | os: [linux] 245 | 246 | '@next/swc-linux-x64-gnu@15.0.3': 247 | resolution: {integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==} 248 | engines: {node: '>= 10'} 249 | cpu: [x64] 250 | os: [linux] 251 | 252 | '@next/swc-linux-x64-musl@15.0.3': 253 | resolution: {integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==} 254 | engines: {node: '>= 10'} 255 | cpu: [x64] 256 | os: [linux] 257 | 258 | '@next/swc-win32-arm64-msvc@15.0.3': 259 | resolution: {integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==} 260 | engines: {node: '>= 10'} 261 | cpu: [arm64] 262 | os: [win32] 263 | 264 | '@next/swc-win32-x64-msvc@15.0.3': 265 | resolution: {integrity: sha512-VNAz+HN4OGgvZs6MOoVfnn41kBzT+M+tB+OK4cww6DNyWS6wKaDpaAm/qLeOUbnMh0oVx1+mg0uoYARF69dJyA==} 266 | engines: {node: '>= 10'} 267 | cpu: [x64] 268 | os: [win32] 269 | 270 | '@nodelib/fs.scandir@2.1.5': 271 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 272 | engines: {node: '>= 8'} 273 | 274 | '@nodelib/fs.stat@2.0.5': 275 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 276 | engines: {node: '>= 8'} 277 | 278 | '@nodelib/fs.walk@1.2.8': 279 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 280 | engines: {node: '>= 8'} 281 | 282 | '@swc/counter@0.1.3': 283 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 284 | 285 | '@swc/helpers@0.5.13': 286 | resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} 287 | 288 | '@types/eslint@9.6.1': 289 | resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} 290 | 291 | '@types/eslint__js@8.42.3': 292 | resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==} 293 | 294 | '@types/estree@1.0.6': 295 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 296 | 297 | '@types/json-schema@7.0.15': 298 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 299 | 300 | '@typescript-eslint/eslint-plugin@8.14.0': 301 | resolution: {integrity: sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==} 302 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 303 | peerDependencies: 304 | '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 305 | eslint: ^8.57.0 || ^9.0.0 306 | typescript: '*' 307 | peerDependenciesMeta: 308 | typescript: 309 | optional: true 310 | 311 | '@typescript-eslint/parser@8.14.0': 312 | resolution: {integrity: sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==} 313 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 314 | peerDependencies: 315 | eslint: ^8.57.0 || ^9.0.0 316 | typescript: '*' 317 | peerDependenciesMeta: 318 | typescript: 319 | optional: true 320 | 321 | '@typescript-eslint/scope-manager@8.14.0': 322 | resolution: {integrity: sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==} 323 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 324 | 325 | '@typescript-eslint/type-utils@8.14.0': 326 | resolution: {integrity: sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==} 327 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 328 | peerDependencies: 329 | typescript: '*' 330 | peerDependenciesMeta: 331 | typescript: 332 | optional: true 333 | 334 | '@typescript-eslint/types@8.14.0': 335 | resolution: {integrity: sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==} 336 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 337 | 338 | '@typescript-eslint/typescript-estree@8.14.0': 339 | resolution: {integrity: sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==} 340 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 341 | peerDependencies: 342 | typescript: '*' 343 | peerDependenciesMeta: 344 | typescript: 345 | optional: true 346 | 347 | '@typescript-eslint/utils@8.14.0': 348 | resolution: {integrity: sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==} 349 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 350 | peerDependencies: 351 | eslint: ^8.57.0 || ^9.0.0 352 | 353 | '@typescript-eslint/visitor-keys@8.14.0': 354 | resolution: {integrity: sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==} 355 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 356 | 357 | acorn-jsx@5.3.2: 358 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 359 | peerDependencies: 360 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 361 | 362 | acorn@8.14.0: 363 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 364 | engines: {node: '>=0.4.0'} 365 | hasBin: true 366 | 367 | ajv@6.12.6: 368 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 369 | 370 | ansi-styles@4.3.0: 371 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 372 | engines: {node: '>=8'} 373 | 374 | argparse@2.0.1: 375 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 376 | 377 | balanced-match@1.0.2: 378 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 379 | 380 | brace-expansion@1.1.11: 381 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 382 | 383 | brace-expansion@2.0.1: 384 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 385 | 386 | braces@3.0.3: 387 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 388 | engines: {node: '>=8'} 389 | 390 | busboy@1.6.0: 391 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 392 | engines: {node: '>=10.16.0'} 393 | 394 | callsites@3.1.0: 395 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 396 | engines: {node: '>=6'} 397 | 398 | caniuse-lite@1.0.30001680: 399 | resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} 400 | 401 | chalk@4.1.2: 402 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 403 | engines: {node: '>=10'} 404 | 405 | client-only@0.0.1: 406 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 407 | 408 | color-convert@2.0.1: 409 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 410 | engines: {node: '>=7.0.0'} 411 | 412 | color-name@1.1.4: 413 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 414 | 415 | color-string@1.9.1: 416 | resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} 417 | 418 | color@4.2.3: 419 | resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} 420 | engines: {node: '>=12.5.0'} 421 | 422 | concat-map@0.0.1: 423 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 424 | 425 | cross-spawn@7.0.5: 426 | resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} 427 | engines: {node: '>= 8'} 428 | 429 | debug@4.3.7: 430 | resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} 431 | engines: {node: '>=6.0'} 432 | peerDependencies: 433 | supports-color: '*' 434 | peerDependenciesMeta: 435 | supports-color: 436 | optional: true 437 | 438 | deep-is@0.1.4: 439 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 440 | 441 | detect-libc@2.0.3: 442 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} 443 | engines: {node: '>=8'} 444 | 445 | escape-string-regexp@4.0.0: 446 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 447 | engines: {node: '>=10'} 448 | 449 | eslint-plugin-react-hooks@5.0.0: 450 | resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} 451 | engines: {node: '>=10'} 452 | peerDependencies: 453 | eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 454 | 455 | eslint-scope@8.2.0: 456 | resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} 457 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 458 | 459 | eslint-visitor-keys@3.4.3: 460 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 461 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 462 | 463 | eslint-visitor-keys@4.2.0: 464 | resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} 465 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 466 | 467 | eslint@9.15.0: 468 | resolution: {integrity: sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==} 469 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 470 | hasBin: true 471 | peerDependencies: 472 | jiti: '*' 473 | peerDependenciesMeta: 474 | jiti: 475 | optional: true 476 | 477 | espree@10.3.0: 478 | resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} 479 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 480 | 481 | esquery@1.6.0: 482 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 483 | engines: {node: '>=0.10'} 484 | 485 | esrecurse@4.3.0: 486 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 487 | engines: {node: '>=4.0'} 488 | 489 | estraverse@5.3.0: 490 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 491 | engines: {node: '>=4.0'} 492 | 493 | esutils@2.0.3: 494 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 495 | engines: {node: '>=0.10.0'} 496 | 497 | fast-deep-equal@3.1.3: 498 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 499 | 500 | fast-glob@3.3.2: 501 | resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} 502 | engines: {node: '>=8.6.0'} 503 | 504 | fast-json-stable-stringify@2.1.0: 505 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 506 | 507 | fast-levenshtein@2.0.6: 508 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 509 | 510 | fastq@1.17.1: 511 | resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} 512 | 513 | file-entry-cache@8.0.0: 514 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 515 | engines: {node: '>=16.0.0'} 516 | 517 | fill-range@7.1.1: 518 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 519 | engines: {node: '>=8'} 520 | 521 | find-up@5.0.0: 522 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 523 | engines: {node: '>=10'} 524 | 525 | flat-cache@4.0.1: 526 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 527 | engines: {node: '>=16'} 528 | 529 | flatted@3.3.1: 530 | resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} 531 | 532 | glob-parent@5.1.2: 533 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 534 | engines: {node: '>= 6'} 535 | 536 | glob-parent@6.0.2: 537 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 538 | engines: {node: '>=10.13.0'} 539 | 540 | globals@14.0.0: 541 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 542 | engines: {node: '>=18'} 543 | 544 | graphemer@1.4.0: 545 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 546 | 547 | has-flag@4.0.0: 548 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 549 | engines: {node: '>=8'} 550 | 551 | ignore@5.3.2: 552 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 553 | engines: {node: '>= 4'} 554 | 555 | import-fresh@3.3.0: 556 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 557 | engines: {node: '>=6'} 558 | 559 | imurmurhash@0.1.4: 560 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 561 | engines: {node: '>=0.8.19'} 562 | 563 | is-arrayish@0.3.2: 564 | resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} 565 | 566 | is-extglob@2.1.1: 567 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 568 | engines: {node: '>=0.10.0'} 569 | 570 | is-glob@4.0.3: 571 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 572 | engines: {node: '>=0.10.0'} 573 | 574 | is-number@7.0.0: 575 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 576 | engines: {node: '>=0.12.0'} 577 | 578 | isexe@2.0.0: 579 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 580 | 581 | js-tokens@4.0.0: 582 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 583 | 584 | js-yaml@4.1.0: 585 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 586 | hasBin: true 587 | 588 | json-buffer@3.0.1: 589 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 590 | 591 | json-schema-traverse@0.4.1: 592 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 593 | 594 | json-stable-stringify-without-jsonify@1.0.1: 595 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 596 | 597 | keyv@4.5.4: 598 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 599 | 600 | levn@0.4.1: 601 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 602 | engines: {node: '>= 0.8.0'} 603 | 604 | locate-path@6.0.0: 605 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 606 | engines: {node: '>=10'} 607 | 608 | lodash.merge@4.6.2: 609 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 610 | 611 | loose-envify@1.4.0: 612 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 613 | hasBin: true 614 | 615 | merge2@1.4.1: 616 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 617 | engines: {node: '>= 8'} 618 | 619 | micromatch@4.0.8: 620 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 621 | engines: {node: '>=8.6'} 622 | 623 | minimatch@3.1.2: 624 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 625 | 626 | minimatch@9.0.5: 627 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 628 | engines: {node: '>=16 || 14 >=14.17'} 629 | 630 | ms@2.1.3: 631 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 632 | 633 | nanoid@3.3.7: 634 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 635 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 636 | hasBin: true 637 | 638 | natural-compare@1.4.0: 639 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 640 | 641 | next@15.0.3: 642 | resolution: {integrity: sha512-ontCbCRKJUIoivAdGB34yCaOcPgYXr9AAkV/IwqFfWWTXEPUgLYkSkqBhIk9KK7gGmgjc64B+RdoeIDM13Irnw==} 643 | engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} 644 | hasBin: true 645 | peerDependencies: 646 | '@opentelemetry/api': ^1.1.0 647 | '@playwright/test': ^1.41.2 648 | babel-plugin-react-compiler: '*' 649 | react: ^18.2.0 || 19.0.0-rc-66855b96-20241106 650 | react-dom: ^18.2.0 || 19.0.0-rc-66855b96-20241106 651 | sass: ^1.3.0 652 | peerDependenciesMeta: 653 | '@opentelemetry/api': 654 | optional: true 655 | '@playwright/test': 656 | optional: true 657 | babel-plugin-react-compiler: 658 | optional: true 659 | sass: 660 | optional: true 661 | 662 | optionator@0.9.4: 663 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 664 | engines: {node: '>= 0.8.0'} 665 | 666 | p-limit@3.1.0: 667 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 668 | engines: {node: '>=10'} 669 | 670 | p-locate@5.0.0: 671 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 672 | engines: {node: '>=10'} 673 | 674 | parent-module@1.0.1: 675 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 676 | engines: {node: '>=6'} 677 | 678 | path-exists@4.0.0: 679 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 680 | engines: {node: '>=8'} 681 | 682 | path-key@3.1.1: 683 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 684 | engines: {node: '>=8'} 685 | 686 | picocolors@1.1.1: 687 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 688 | 689 | picomatch@2.3.1: 690 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 691 | engines: {node: '>=8.6'} 692 | 693 | postcss@8.4.31: 694 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 695 | engines: {node: ^10 || ^12 || >=14} 696 | 697 | postgres@3.4.5: 698 | resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} 699 | engines: {node: '>=12'} 700 | 701 | prelude-ls@1.2.1: 702 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 703 | engines: {node: '>= 0.8.0'} 704 | 705 | punycode@2.3.1: 706 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 707 | engines: {node: '>=6'} 708 | 709 | queue-microtask@1.2.3: 710 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 711 | 712 | react-dom@18.3.1: 713 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 714 | peerDependencies: 715 | react: ^18.3.1 716 | 717 | react@18.3.1: 718 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} 719 | engines: {node: '>=0.10.0'} 720 | 721 | resolve-from@4.0.0: 722 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 723 | engines: {node: '>=4'} 724 | 725 | reusify@1.0.4: 726 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 727 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 728 | 729 | run-parallel@1.2.0: 730 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 731 | 732 | scheduler@0.23.2: 733 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} 734 | 735 | semver@7.6.3: 736 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 737 | engines: {node: '>=10'} 738 | hasBin: true 739 | 740 | sharp@0.33.5: 741 | resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} 742 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 743 | 744 | shebang-command@2.0.0: 745 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 746 | engines: {node: '>=8'} 747 | 748 | shebang-regex@3.0.0: 749 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 750 | engines: {node: '>=8'} 751 | 752 | simple-swizzle@0.2.2: 753 | resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} 754 | 755 | source-map-js@1.2.1: 756 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 757 | engines: {node: '>=0.10.0'} 758 | 759 | streamsearch@1.1.0: 760 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 761 | engines: {node: '>=10.0.0'} 762 | 763 | strip-json-comments@3.1.1: 764 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 765 | engines: {node: '>=8'} 766 | 767 | styled-jsx@5.1.6: 768 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 769 | engines: {node: '>= 12.0.0'} 770 | peerDependencies: 771 | '@babel/core': '*' 772 | babel-plugin-macros: '*' 773 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 774 | peerDependenciesMeta: 775 | '@babel/core': 776 | optional: true 777 | babel-plugin-macros: 778 | optional: true 779 | 780 | supports-color@7.2.0: 781 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 782 | engines: {node: '>=8'} 783 | 784 | to-regex-range@5.0.1: 785 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 786 | engines: {node: '>=8.0'} 787 | 788 | ts-api-utils@1.4.0: 789 | resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} 790 | engines: {node: '>=16'} 791 | peerDependencies: 792 | typescript: '>=4.2.0' 793 | 794 | tslib@2.8.1: 795 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 796 | 797 | type-check@0.4.0: 798 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 799 | engines: {node: '>= 0.8.0'} 800 | 801 | typescript-eslint@8.14.0: 802 | resolution: {integrity: sha512-K8fBJHxVL3kxMmwByvz8hNdBJ8a0YqKzKDX6jRlrjMuNXyd5T2V02HIq37+OiWXvUUOXgOOGiSSOh26Mh8pC3w==} 803 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 804 | peerDependencies: 805 | typescript: '*' 806 | peerDependenciesMeta: 807 | typescript: 808 | optional: true 809 | 810 | typescript@5.6.3: 811 | resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} 812 | engines: {node: '>=14.17'} 813 | hasBin: true 814 | 815 | uri-js@4.4.1: 816 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 817 | 818 | which@2.0.2: 819 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 820 | engines: {node: '>= 8'} 821 | hasBin: true 822 | 823 | word-wrap@1.2.5: 824 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 825 | engines: {node: '>=0.10.0'} 826 | 827 | yocto-queue@0.1.0: 828 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 829 | engines: {node: '>=10'} 830 | 831 | snapshots: 832 | 833 | '@emnapi/runtime@1.3.1': 834 | dependencies: 835 | tslib: 2.8.1 836 | optional: true 837 | 838 | '@eslint-community/eslint-utils@4.4.1(eslint@9.15.0)': 839 | dependencies: 840 | eslint: 9.15.0 841 | eslint-visitor-keys: 3.4.3 842 | 843 | '@eslint-community/regexpp@4.12.1': {} 844 | 845 | '@eslint/compat@1.2.3(eslint@9.15.0)': 846 | optionalDependencies: 847 | eslint: 9.15.0 848 | 849 | '@eslint/config-array@0.19.0': 850 | dependencies: 851 | '@eslint/object-schema': 2.1.4 852 | debug: 4.3.7 853 | minimatch: 3.1.2 854 | transitivePeerDependencies: 855 | - supports-color 856 | 857 | '@eslint/core@0.9.0': {} 858 | 859 | '@eslint/eslintrc@3.2.0': 860 | dependencies: 861 | ajv: 6.12.6 862 | debug: 4.3.7 863 | espree: 10.3.0 864 | globals: 14.0.0 865 | ignore: 5.3.2 866 | import-fresh: 3.3.0 867 | js-yaml: 4.1.0 868 | minimatch: 3.1.2 869 | strip-json-comments: 3.1.1 870 | transitivePeerDependencies: 871 | - supports-color 872 | 873 | '@eslint/js@9.15.0': {} 874 | 875 | '@eslint/object-schema@2.1.4': {} 876 | 877 | '@eslint/plugin-kit@0.2.3': 878 | dependencies: 879 | levn: 0.4.1 880 | 881 | '@humanfs/core@0.19.1': {} 882 | 883 | '@humanfs/node@0.16.6': 884 | dependencies: 885 | '@humanfs/core': 0.19.1 886 | '@humanwhocodes/retry': 0.3.1 887 | 888 | '@humanwhocodes/module-importer@1.0.1': {} 889 | 890 | '@humanwhocodes/retry@0.3.1': {} 891 | 892 | '@humanwhocodes/retry@0.4.1': {} 893 | 894 | '@img/sharp-darwin-arm64@0.33.5': 895 | optionalDependencies: 896 | '@img/sharp-libvips-darwin-arm64': 1.0.4 897 | optional: true 898 | 899 | '@img/sharp-darwin-x64@0.33.5': 900 | optionalDependencies: 901 | '@img/sharp-libvips-darwin-x64': 1.0.4 902 | optional: true 903 | 904 | '@img/sharp-libvips-darwin-arm64@1.0.4': 905 | optional: true 906 | 907 | '@img/sharp-libvips-darwin-x64@1.0.4': 908 | optional: true 909 | 910 | '@img/sharp-libvips-linux-arm64@1.0.4': 911 | optional: true 912 | 913 | '@img/sharp-libvips-linux-arm@1.0.5': 914 | optional: true 915 | 916 | '@img/sharp-libvips-linux-s390x@1.0.4': 917 | optional: true 918 | 919 | '@img/sharp-libvips-linux-x64@1.0.4': 920 | optional: true 921 | 922 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 923 | optional: true 924 | 925 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 926 | optional: true 927 | 928 | '@img/sharp-linux-arm64@0.33.5': 929 | optionalDependencies: 930 | '@img/sharp-libvips-linux-arm64': 1.0.4 931 | optional: true 932 | 933 | '@img/sharp-linux-arm@0.33.5': 934 | optionalDependencies: 935 | '@img/sharp-libvips-linux-arm': 1.0.5 936 | optional: true 937 | 938 | '@img/sharp-linux-s390x@0.33.5': 939 | optionalDependencies: 940 | '@img/sharp-libvips-linux-s390x': 1.0.4 941 | optional: true 942 | 943 | '@img/sharp-linux-x64@0.33.5': 944 | optionalDependencies: 945 | '@img/sharp-libvips-linux-x64': 1.0.4 946 | optional: true 947 | 948 | '@img/sharp-linuxmusl-arm64@0.33.5': 949 | optionalDependencies: 950 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 951 | optional: true 952 | 953 | '@img/sharp-linuxmusl-x64@0.33.5': 954 | optionalDependencies: 955 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 956 | optional: true 957 | 958 | '@img/sharp-wasm32@0.33.5': 959 | dependencies: 960 | '@emnapi/runtime': 1.3.1 961 | optional: true 962 | 963 | '@img/sharp-win32-ia32@0.33.5': 964 | optional: true 965 | 966 | '@img/sharp-win32-x64@0.33.5': 967 | optional: true 968 | 969 | '@next/env@15.0.3': {} 970 | 971 | '@next/swc-darwin-arm64@15.0.3': 972 | optional: true 973 | 974 | '@next/swc-darwin-x64@15.0.3': 975 | optional: true 976 | 977 | '@next/swc-linux-arm64-gnu@15.0.3': 978 | optional: true 979 | 980 | '@next/swc-linux-arm64-musl@15.0.3': 981 | optional: true 982 | 983 | '@next/swc-linux-x64-gnu@15.0.3': 984 | optional: true 985 | 986 | '@next/swc-linux-x64-musl@15.0.3': 987 | optional: true 988 | 989 | '@next/swc-win32-arm64-msvc@15.0.3': 990 | optional: true 991 | 992 | '@next/swc-win32-x64-msvc@15.0.3': 993 | optional: true 994 | 995 | '@nodelib/fs.scandir@2.1.5': 996 | dependencies: 997 | '@nodelib/fs.stat': 2.0.5 998 | run-parallel: 1.2.0 999 | 1000 | '@nodelib/fs.stat@2.0.5': {} 1001 | 1002 | '@nodelib/fs.walk@1.2.8': 1003 | dependencies: 1004 | '@nodelib/fs.scandir': 2.1.5 1005 | fastq: 1.17.1 1006 | 1007 | '@swc/counter@0.1.3': {} 1008 | 1009 | '@swc/helpers@0.5.13': 1010 | dependencies: 1011 | tslib: 2.8.1 1012 | 1013 | '@types/eslint@9.6.1': 1014 | dependencies: 1015 | '@types/estree': 1.0.6 1016 | '@types/json-schema': 7.0.15 1017 | 1018 | '@types/eslint__js@8.42.3': 1019 | dependencies: 1020 | '@types/eslint': 9.6.1 1021 | 1022 | '@types/estree@1.0.6': {} 1023 | 1024 | '@types/json-schema@7.0.15': {} 1025 | 1026 | '@typescript-eslint/eslint-plugin@8.14.0(@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3)': 1027 | dependencies: 1028 | '@eslint-community/regexpp': 4.12.1 1029 | '@typescript-eslint/parser': 8.14.0(eslint@9.15.0)(typescript@5.6.3) 1030 | '@typescript-eslint/scope-manager': 8.14.0 1031 | '@typescript-eslint/type-utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) 1032 | '@typescript-eslint/utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) 1033 | '@typescript-eslint/visitor-keys': 8.14.0 1034 | eslint: 9.15.0 1035 | graphemer: 1.4.0 1036 | ignore: 5.3.2 1037 | natural-compare: 1.4.0 1038 | ts-api-utils: 1.4.0(typescript@5.6.3) 1039 | optionalDependencies: 1040 | typescript: 5.6.3 1041 | transitivePeerDependencies: 1042 | - supports-color 1043 | 1044 | '@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3)': 1045 | dependencies: 1046 | '@typescript-eslint/scope-manager': 8.14.0 1047 | '@typescript-eslint/types': 8.14.0 1048 | '@typescript-eslint/typescript-estree': 8.14.0(typescript@5.6.3) 1049 | '@typescript-eslint/visitor-keys': 8.14.0 1050 | debug: 4.3.7 1051 | eslint: 9.15.0 1052 | optionalDependencies: 1053 | typescript: 5.6.3 1054 | transitivePeerDependencies: 1055 | - supports-color 1056 | 1057 | '@typescript-eslint/scope-manager@8.14.0': 1058 | dependencies: 1059 | '@typescript-eslint/types': 8.14.0 1060 | '@typescript-eslint/visitor-keys': 8.14.0 1061 | 1062 | '@typescript-eslint/type-utils@8.14.0(eslint@9.15.0)(typescript@5.6.3)': 1063 | dependencies: 1064 | '@typescript-eslint/typescript-estree': 8.14.0(typescript@5.6.3) 1065 | '@typescript-eslint/utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) 1066 | debug: 4.3.7 1067 | ts-api-utils: 1.4.0(typescript@5.6.3) 1068 | optionalDependencies: 1069 | typescript: 5.6.3 1070 | transitivePeerDependencies: 1071 | - eslint 1072 | - supports-color 1073 | 1074 | '@typescript-eslint/types@8.14.0': {} 1075 | 1076 | '@typescript-eslint/typescript-estree@8.14.0(typescript@5.6.3)': 1077 | dependencies: 1078 | '@typescript-eslint/types': 8.14.0 1079 | '@typescript-eslint/visitor-keys': 8.14.0 1080 | debug: 4.3.7 1081 | fast-glob: 3.3.2 1082 | is-glob: 4.0.3 1083 | minimatch: 9.0.5 1084 | semver: 7.6.3 1085 | ts-api-utils: 1.4.0(typescript@5.6.3) 1086 | optionalDependencies: 1087 | typescript: 5.6.3 1088 | transitivePeerDependencies: 1089 | - supports-color 1090 | 1091 | '@typescript-eslint/utils@8.14.0(eslint@9.15.0)(typescript@5.6.3)': 1092 | dependencies: 1093 | '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) 1094 | '@typescript-eslint/scope-manager': 8.14.0 1095 | '@typescript-eslint/types': 8.14.0 1096 | '@typescript-eslint/typescript-estree': 8.14.0(typescript@5.6.3) 1097 | eslint: 9.15.0 1098 | transitivePeerDependencies: 1099 | - supports-color 1100 | - typescript 1101 | 1102 | '@typescript-eslint/visitor-keys@8.14.0': 1103 | dependencies: 1104 | '@typescript-eslint/types': 8.14.0 1105 | eslint-visitor-keys: 3.4.3 1106 | 1107 | acorn-jsx@5.3.2(acorn@8.14.0): 1108 | dependencies: 1109 | acorn: 8.14.0 1110 | 1111 | acorn@8.14.0: {} 1112 | 1113 | ajv@6.12.6: 1114 | dependencies: 1115 | fast-deep-equal: 3.1.3 1116 | fast-json-stable-stringify: 2.1.0 1117 | json-schema-traverse: 0.4.1 1118 | uri-js: 4.4.1 1119 | 1120 | ansi-styles@4.3.0: 1121 | dependencies: 1122 | color-convert: 2.0.1 1123 | 1124 | argparse@2.0.1: {} 1125 | 1126 | balanced-match@1.0.2: {} 1127 | 1128 | brace-expansion@1.1.11: 1129 | dependencies: 1130 | balanced-match: 1.0.2 1131 | concat-map: 0.0.1 1132 | 1133 | brace-expansion@2.0.1: 1134 | dependencies: 1135 | balanced-match: 1.0.2 1136 | 1137 | braces@3.0.3: 1138 | dependencies: 1139 | fill-range: 7.1.1 1140 | 1141 | busboy@1.6.0: 1142 | dependencies: 1143 | streamsearch: 1.1.0 1144 | 1145 | callsites@3.1.0: {} 1146 | 1147 | caniuse-lite@1.0.30001680: {} 1148 | 1149 | chalk@4.1.2: 1150 | dependencies: 1151 | ansi-styles: 4.3.0 1152 | supports-color: 7.2.0 1153 | 1154 | client-only@0.0.1: {} 1155 | 1156 | color-convert@2.0.1: 1157 | dependencies: 1158 | color-name: 1.1.4 1159 | 1160 | color-name@1.1.4: {} 1161 | 1162 | color-string@1.9.1: 1163 | dependencies: 1164 | color-name: 1.1.4 1165 | simple-swizzle: 0.2.2 1166 | optional: true 1167 | 1168 | color@4.2.3: 1169 | dependencies: 1170 | color-convert: 2.0.1 1171 | color-string: 1.9.1 1172 | optional: true 1173 | 1174 | concat-map@0.0.1: {} 1175 | 1176 | cross-spawn@7.0.5: 1177 | dependencies: 1178 | path-key: 3.1.1 1179 | shebang-command: 2.0.0 1180 | which: 2.0.2 1181 | 1182 | debug@4.3.7: 1183 | dependencies: 1184 | ms: 2.1.3 1185 | 1186 | deep-is@0.1.4: {} 1187 | 1188 | detect-libc@2.0.3: 1189 | optional: true 1190 | 1191 | escape-string-regexp@4.0.0: {} 1192 | 1193 | eslint-plugin-react-hooks@5.0.0(eslint@9.15.0): 1194 | dependencies: 1195 | eslint: 9.15.0 1196 | 1197 | eslint-scope@8.2.0: 1198 | dependencies: 1199 | esrecurse: 4.3.0 1200 | estraverse: 5.3.0 1201 | 1202 | eslint-visitor-keys@3.4.3: {} 1203 | 1204 | eslint-visitor-keys@4.2.0: {} 1205 | 1206 | eslint@9.15.0: 1207 | dependencies: 1208 | '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) 1209 | '@eslint-community/regexpp': 4.12.1 1210 | '@eslint/config-array': 0.19.0 1211 | '@eslint/core': 0.9.0 1212 | '@eslint/eslintrc': 3.2.0 1213 | '@eslint/js': 9.15.0 1214 | '@eslint/plugin-kit': 0.2.3 1215 | '@humanfs/node': 0.16.6 1216 | '@humanwhocodes/module-importer': 1.0.1 1217 | '@humanwhocodes/retry': 0.4.1 1218 | '@types/estree': 1.0.6 1219 | '@types/json-schema': 7.0.15 1220 | ajv: 6.12.6 1221 | chalk: 4.1.2 1222 | cross-spawn: 7.0.5 1223 | debug: 4.3.7 1224 | escape-string-regexp: 4.0.0 1225 | eslint-scope: 8.2.0 1226 | eslint-visitor-keys: 4.2.0 1227 | espree: 10.3.0 1228 | esquery: 1.6.0 1229 | esutils: 2.0.3 1230 | fast-deep-equal: 3.1.3 1231 | file-entry-cache: 8.0.0 1232 | find-up: 5.0.0 1233 | glob-parent: 6.0.2 1234 | ignore: 5.3.2 1235 | imurmurhash: 0.1.4 1236 | is-glob: 4.0.3 1237 | json-stable-stringify-without-jsonify: 1.0.1 1238 | lodash.merge: 4.6.2 1239 | minimatch: 3.1.2 1240 | natural-compare: 1.4.0 1241 | optionator: 0.9.4 1242 | transitivePeerDependencies: 1243 | - supports-color 1244 | 1245 | espree@10.3.0: 1246 | dependencies: 1247 | acorn: 8.14.0 1248 | acorn-jsx: 5.3.2(acorn@8.14.0) 1249 | eslint-visitor-keys: 4.2.0 1250 | 1251 | esquery@1.6.0: 1252 | dependencies: 1253 | estraverse: 5.3.0 1254 | 1255 | esrecurse@4.3.0: 1256 | dependencies: 1257 | estraverse: 5.3.0 1258 | 1259 | estraverse@5.3.0: {} 1260 | 1261 | esutils@2.0.3: {} 1262 | 1263 | fast-deep-equal@3.1.3: {} 1264 | 1265 | fast-glob@3.3.2: 1266 | dependencies: 1267 | '@nodelib/fs.stat': 2.0.5 1268 | '@nodelib/fs.walk': 1.2.8 1269 | glob-parent: 5.1.2 1270 | merge2: 1.4.1 1271 | micromatch: 4.0.8 1272 | 1273 | fast-json-stable-stringify@2.1.0: {} 1274 | 1275 | fast-levenshtein@2.0.6: {} 1276 | 1277 | fastq@1.17.1: 1278 | dependencies: 1279 | reusify: 1.0.4 1280 | 1281 | file-entry-cache@8.0.0: 1282 | dependencies: 1283 | flat-cache: 4.0.1 1284 | 1285 | fill-range@7.1.1: 1286 | dependencies: 1287 | to-regex-range: 5.0.1 1288 | 1289 | find-up@5.0.0: 1290 | dependencies: 1291 | locate-path: 6.0.0 1292 | path-exists: 4.0.0 1293 | 1294 | flat-cache@4.0.1: 1295 | dependencies: 1296 | flatted: 3.3.1 1297 | keyv: 4.5.4 1298 | 1299 | flatted@3.3.1: {} 1300 | 1301 | glob-parent@5.1.2: 1302 | dependencies: 1303 | is-glob: 4.0.3 1304 | 1305 | glob-parent@6.0.2: 1306 | dependencies: 1307 | is-glob: 4.0.3 1308 | 1309 | globals@14.0.0: {} 1310 | 1311 | graphemer@1.4.0: {} 1312 | 1313 | has-flag@4.0.0: {} 1314 | 1315 | ignore@5.3.2: {} 1316 | 1317 | import-fresh@3.3.0: 1318 | dependencies: 1319 | parent-module: 1.0.1 1320 | resolve-from: 4.0.0 1321 | 1322 | imurmurhash@0.1.4: {} 1323 | 1324 | is-arrayish@0.3.2: 1325 | optional: true 1326 | 1327 | is-extglob@2.1.1: {} 1328 | 1329 | is-glob@4.0.3: 1330 | dependencies: 1331 | is-extglob: 2.1.1 1332 | 1333 | is-number@7.0.0: {} 1334 | 1335 | isexe@2.0.0: {} 1336 | 1337 | js-tokens@4.0.0: {} 1338 | 1339 | js-yaml@4.1.0: 1340 | dependencies: 1341 | argparse: 2.0.1 1342 | 1343 | json-buffer@3.0.1: {} 1344 | 1345 | json-schema-traverse@0.4.1: {} 1346 | 1347 | json-stable-stringify-without-jsonify@1.0.1: {} 1348 | 1349 | keyv@4.5.4: 1350 | dependencies: 1351 | json-buffer: 3.0.1 1352 | 1353 | levn@0.4.1: 1354 | dependencies: 1355 | prelude-ls: 1.2.1 1356 | type-check: 0.4.0 1357 | 1358 | locate-path@6.0.0: 1359 | dependencies: 1360 | p-locate: 5.0.0 1361 | 1362 | lodash.merge@4.6.2: {} 1363 | 1364 | loose-envify@1.4.0: 1365 | dependencies: 1366 | js-tokens: 4.0.0 1367 | 1368 | merge2@1.4.1: {} 1369 | 1370 | micromatch@4.0.8: 1371 | dependencies: 1372 | braces: 3.0.3 1373 | picomatch: 2.3.1 1374 | 1375 | minimatch@3.1.2: 1376 | dependencies: 1377 | brace-expansion: 1.1.11 1378 | 1379 | minimatch@9.0.5: 1380 | dependencies: 1381 | brace-expansion: 2.0.1 1382 | 1383 | ms@2.1.3: {} 1384 | 1385 | nanoid@3.3.7: {} 1386 | 1387 | natural-compare@1.4.0: {} 1388 | 1389 | next@15.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 1390 | dependencies: 1391 | '@next/env': 15.0.3 1392 | '@swc/counter': 0.1.3 1393 | '@swc/helpers': 0.5.13 1394 | busboy: 1.6.0 1395 | caniuse-lite: 1.0.30001680 1396 | postcss: 8.4.31 1397 | react: 18.3.1 1398 | react-dom: 18.3.1(react@18.3.1) 1399 | styled-jsx: 5.1.6(react@18.3.1) 1400 | optionalDependencies: 1401 | '@next/swc-darwin-arm64': 15.0.3 1402 | '@next/swc-darwin-x64': 15.0.3 1403 | '@next/swc-linux-arm64-gnu': 15.0.3 1404 | '@next/swc-linux-arm64-musl': 15.0.3 1405 | '@next/swc-linux-x64-gnu': 15.0.3 1406 | '@next/swc-linux-x64-musl': 15.0.3 1407 | '@next/swc-win32-arm64-msvc': 15.0.3 1408 | '@next/swc-win32-x64-msvc': 15.0.3 1409 | sharp: 0.33.5 1410 | transitivePeerDependencies: 1411 | - '@babel/core' 1412 | - babel-plugin-macros 1413 | 1414 | optionator@0.9.4: 1415 | dependencies: 1416 | deep-is: 0.1.4 1417 | fast-levenshtein: 2.0.6 1418 | levn: 0.4.1 1419 | prelude-ls: 1.2.1 1420 | type-check: 0.4.0 1421 | word-wrap: 1.2.5 1422 | 1423 | p-limit@3.1.0: 1424 | dependencies: 1425 | yocto-queue: 0.1.0 1426 | 1427 | p-locate@5.0.0: 1428 | dependencies: 1429 | p-limit: 3.1.0 1430 | 1431 | parent-module@1.0.1: 1432 | dependencies: 1433 | callsites: 3.1.0 1434 | 1435 | path-exists@4.0.0: {} 1436 | 1437 | path-key@3.1.1: {} 1438 | 1439 | picocolors@1.1.1: {} 1440 | 1441 | picomatch@2.3.1: {} 1442 | 1443 | postcss@8.4.31: 1444 | dependencies: 1445 | nanoid: 3.3.7 1446 | picocolors: 1.1.1 1447 | source-map-js: 1.2.1 1448 | 1449 | postgres@3.4.5: {} 1450 | 1451 | prelude-ls@1.2.1: {} 1452 | 1453 | punycode@2.3.1: {} 1454 | 1455 | queue-microtask@1.2.3: {} 1456 | 1457 | react-dom@18.3.1(react@18.3.1): 1458 | dependencies: 1459 | loose-envify: 1.4.0 1460 | react: 18.3.1 1461 | scheduler: 0.23.2 1462 | 1463 | react@18.3.1: 1464 | dependencies: 1465 | loose-envify: 1.4.0 1466 | 1467 | resolve-from@4.0.0: {} 1468 | 1469 | reusify@1.0.4: {} 1470 | 1471 | run-parallel@1.2.0: 1472 | dependencies: 1473 | queue-microtask: 1.2.3 1474 | 1475 | scheduler@0.23.2: 1476 | dependencies: 1477 | loose-envify: 1.4.0 1478 | 1479 | semver@7.6.3: {} 1480 | 1481 | sharp@0.33.5: 1482 | dependencies: 1483 | color: 4.2.3 1484 | detect-libc: 2.0.3 1485 | semver: 7.6.3 1486 | optionalDependencies: 1487 | '@img/sharp-darwin-arm64': 0.33.5 1488 | '@img/sharp-darwin-x64': 0.33.5 1489 | '@img/sharp-libvips-darwin-arm64': 1.0.4 1490 | '@img/sharp-libvips-darwin-x64': 1.0.4 1491 | '@img/sharp-libvips-linux-arm': 1.0.5 1492 | '@img/sharp-libvips-linux-arm64': 1.0.4 1493 | '@img/sharp-libvips-linux-s390x': 1.0.4 1494 | '@img/sharp-libvips-linux-x64': 1.0.4 1495 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 1496 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 1497 | '@img/sharp-linux-arm': 0.33.5 1498 | '@img/sharp-linux-arm64': 0.33.5 1499 | '@img/sharp-linux-s390x': 0.33.5 1500 | '@img/sharp-linux-x64': 0.33.5 1501 | '@img/sharp-linuxmusl-arm64': 0.33.5 1502 | '@img/sharp-linuxmusl-x64': 0.33.5 1503 | '@img/sharp-wasm32': 0.33.5 1504 | '@img/sharp-win32-ia32': 0.33.5 1505 | '@img/sharp-win32-x64': 0.33.5 1506 | optional: true 1507 | 1508 | shebang-command@2.0.0: 1509 | dependencies: 1510 | shebang-regex: 3.0.0 1511 | 1512 | shebang-regex@3.0.0: {} 1513 | 1514 | simple-swizzle@0.2.2: 1515 | dependencies: 1516 | is-arrayish: 0.3.2 1517 | optional: true 1518 | 1519 | source-map-js@1.2.1: {} 1520 | 1521 | streamsearch@1.1.0: {} 1522 | 1523 | strip-json-comments@3.1.1: {} 1524 | 1525 | styled-jsx@5.1.6(react@18.3.1): 1526 | dependencies: 1527 | client-only: 0.0.1 1528 | react: 18.3.1 1529 | 1530 | supports-color@7.2.0: 1531 | dependencies: 1532 | has-flag: 4.0.0 1533 | 1534 | to-regex-range@5.0.1: 1535 | dependencies: 1536 | is-number: 7.0.0 1537 | 1538 | ts-api-utils@1.4.0(typescript@5.6.3): 1539 | dependencies: 1540 | typescript: 5.6.3 1541 | 1542 | tslib@2.8.1: {} 1543 | 1544 | type-check@0.4.0: 1545 | dependencies: 1546 | prelude-ls: 1.2.1 1547 | 1548 | typescript-eslint@8.14.0(eslint@9.15.0)(typescript@5.6.3): 1549 | dependencies: 1550 | '@typescript-eslint/eslint-plugin': 8.14.0(@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3) 1551 | '@typescript-eslint/parser': 8.14.0(eslint@9.15.0)(typescript@5.6.3) 1552 | '@typescript-eslint/utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) 1553 | optionalDependencies: 1554 | typescript: 5.6.3 1555 | transitivePeerDependencies: 1556 | - eslint 1557 | - supports-color 1558 | 1559 | typescript@5.6.3: {} 1560 | 1561 | uri-js@4.4.1: 1562 | dependencies: 1563 | punycode: 2.3.1 1564 | 1565 | which@2.0.2: 1566 | dependencies: 1567 | isexe: 2.0.0 1568 | 1569 | word-wrap@1.2.5: {} 1570 | 1571 | yocto-queue@0.1.0: {} 1572 | --------------------------------------------------------------------------------