├── .env.example ├── .github └── dependabot.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── biome.jsonc ├── bun.lock ├── components.json ├── lefthook.yml ├── messages ├── en.json └── zh.json ├── next.config.js ├── package.json ├── postcss.config.js ├── prisma ├── migrations │ ├── 20250516075647_ │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── public └── favicon.ico ├── src ├── app │ ├── api │ │ └── trpc │ │ │ └── [trpc] │ │ │ └── route.ts │ ├── layout.tsx │ └── page.tsx ├── components │ ├── bear.tsx │ ├── language │ │ └── toggle.tsx │ ├── post.tsx │ ├── theme │ │ ├── provider.tsx │ │ └── toggle.tsx │ └── ui │ │ ├── button.tsx │ │ ├── input.tsx │ │ └── select.tsx ├── env.js ├── i18n │ ├── config.ts │ └── request.ts ├── lib │ └── utils.ts ├── server │ ├── api │ │ ├── root.ts │ │ ├── routers │ │ │ └── post.ts │ │ └── trpc.ts │ ├── db.ts │ └── locale.ts ├── store │ └── bear.ts ├── styles │ └── globals.css └── trpc │ ├── query-client.ts │ ├── react.tsx │ └── server.ts ├── start-database.sh └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | # Since the ".env" file is gitignored, you can use the ".env.example" file to 2 | # build a new ".env" file when you clone the repo. Keep this file up-to-date 3 | # when you add new variables to `.env`. 4 | 5 | # This file will be committed to version control, so make sure not to have any 6 | # secrets in it. If you are cloning this repo, create a copy of this file named 7 | # ".env" and populate it with your secrets. 8 | 9 | # When adding additional environment variables, the schema in "/src/env.js" 10 | # should be updated accordingly. 11 | 12 | # Prisma 13 | # https://www.prisma.io/docs/reference/database-reference/connection-urls#env 14 | DATABASE_URL="postgresql://postgres:password@localhost:5432/nextjs-starter-template" 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.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 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | db.sqlite 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | next-env.d.ts 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | .pnpm-debug.log* 33 | 34 | # local env files 35 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 36 | .env 37 | .env*.local 38 | 39 | # vercel 40 | .vercel 41 | 42 | # typescript 43 | *.tsbuildinfo 44 | 45 | # idea files 46 | .idea -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["biomejs.biome"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false, 3 | "prettier.enable": false, 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "quickfix.biome": "explicit", 7 | "source.organizeImports.biome": "explicit", 8 | "source.formatDocument.biome": "explicit" 9 | }, 10 | "editor.defaultFormatter": "biomejs.biome", 11 | "[typescriptreact]": { 12 | "editor.defaultFormatter": "biomejs.biome" 13 | }, 14 | "[typescript]": { 15 | "editor.defaultFormatter": "biomejs.biome" 16 | }, 17 | "[javascript]": { 18 | "editor.defaultFormatter": "biomejs.biome" 19 | }, 20 | "[javascriptreact]": { 21 | "editor.defaultFormatter": "biomejs.biome" 22 | }, 23 | "[json]": { 24 | "editor.defaultFormatter": "biomejs.biome" 25 | }, 26 | "[jsonc]": { 27 | "editor.defaultFormatter": "biomejs.biome" 28 | }, 29 | "[cjs]": { 30 | "editor.defaultFormatter": "biomejs.biome" 31 | }, 32 | "[mjs]": { 33 | "editor.defaultFormatter": "biomejs.biome" 34 | }, 35 | "[cts]": { 36 | "editor.defaultFormatter": "biomejs.biome" 37 | }, 38 | "[mts]": { 39 | "editor.defaultFormatter": "biomejs.biome" 40 | }, 41 | "[css]": { 42 | "editor.defaultFormatter": "biomejs.biome" 43 | }, 44 | "cSpell.words": [ 45 | "contentlayer", 46 | "createwise", 47 | "lefthook", 48 | "markmap", 49 | "mindmap", 50 | "shownotes", 51 | "supabase", 52 | "toploader", 53 | "turbopack", 54 | "umami", 55 | "vidstack", 56 | "wavesurfer", 57 | "youtu" 58 | ], 59 | "i18n-ally.localesPaths": ["messages", "src/i18n", "src/components/language"] 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Tech Stack 2 | 3 | - [Next.js](https://nextjs.org) 4 | - [Prisma](https://prisma.io) 5 | - [Tailwind CSS](https://tailwindcss.com) 6 | - [tRPC](https://trpc.io) 7 | 8 | ## Develop 9 | 10 | 1. Start the database 11 | 12 | ```bash 13 | ./start-database.sh 14 | 15 | bun run db:generate 16 | 17 | bun run db:migrate 18 | ``` 19 | 20 | 2. Install dependencies 21 | 22 | ```bash 23 | bun install 24 | ``` 25 | 26 | 3. Run the development server 27 | 28 | ```bash 29 | bun run dev 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "files": { 4 | "ignoreUnknown": false, 5 | "ignore": ["src/components/ui/*.tsx", "node_modules", ".next"] 6 | }, 7 | "vcs": { 8 | "enabled": false, 9 | "clientKind": "git", 10 | "useIgnoreFile": false 11 | }, 12 | "linter": { 13 | "enabled": true, 14 | "rules": { 15 | "recommended": true, 16 | "suspicious": { 17 | "noDebugger": "off", 18 | "noArrayIndexKey": "off" 19 | }, 20 | "style": { 21 | "useTemplate": "off", 22 | "noNonNullAssertion": "off" 23 | }, 24 | "a11y": { 25 | "noSvgWithoutTitle": "off", 26 | "useSemanticElements": "off", 27 | "useKeyWithClickEvents": "off", 28 | "useMediaCaption": "off" 29 | }, 30 | "correctness": { 31 | "noUnusedImports": "error" 32 | }, 33 | "nursery": { 34 | "useSortedClasses": { 35 | "level": "warn", 36 | "fix": "safe", 37 | "options": { 38 | "attributes": ["className"], 39 | "functions": ["clsx", "cva", "cn"] 40 | } 41 | } 42 | } 43 | } 44 | }, 45 | "formatter": { 46 | "enabled": true, 47 | "formatWithErrors": false, 48 | "indentStyle": "space", 49 | "indentWidth": 2, 50 | "lineEnding": "lf", 51 | "lineWidth": 80, 52 | "attributePosition": "auto", 53 | "useEditorconfig": false 54 | }, 55 | "organizeImports": { 56 | "enabled": true 57 | }, 58 | "javascript": { 59 | "formatter": { 60 | "enabled": true, 61 | "quoteStyle": "single", 62 | "jsxQuoteStyle": "single", 63 | "quoteProperties": "asNeeded", 64 | "trailingCommas": "all", 65 | "semicolons": "asNeeded", 66 | "arrowParentheses": "always", 67 | "indentStyle": "space", 68 | "indentWidth": 2, 69 | "lineEnding": "lf", 70 | "lineWidth": 80, 71 | "bracketSameLine": false, 72 | "bracketSpacing": true, 73 | "attributePosition": "multiline" 74 | } 75 | }, 76 | "json": { 77 | "parser": { 78 | "allowComments": true 79 | }, 80 | "formatter": { 81 | "enabled": true, 82 | "indentStyle": "space", 83 | "indentWidth": 2, 84 | "lineEnding": "lf", 85 | "lineWidth": 80 86 | } 87 | }, 88 | "css": { 89 | "parser": { 90 | "cssModules": false 91 | }, 92 | "formatter": { 93 | "enabled": true, 94 | "indentStyle": "space", 95 | "indentWidth": 2, 96 | "lineEnding": "lf", 97 | "lineWidth": 80, 98 | "quoteStyle": "single" 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "nextjs-starter-template", 6 | "dependencies": { 7 | "@prisma/client": "^6.5.0", 8 | "@radix-ui/react-select": "^2.2.4", 9 | "@radix-ui/react-slot": "^1.2.2", 10 | "@t3-oss/env-nextjs": "^0.13.4", 11 | "@tanstack/react-query": "^5.69.0", 12 | "@trpc/client": "^11.0.0", 13 | "@trpc/react-query": "^11.0.0", 14 | "@trpc/server": "^11.0.0", 15 | "class-variance-authority": "^0.7.1", 16 | "clsx": "^2.1.1", 17 | "lefthook": "^1.11.13", 18 | "lucide-react": "^0.511.0", 19 | "next": "^15.2.3", 20 | "next-intl": "^4.1.0", 21 | "next-themes": "^0.4.6", 22 | "react": "^19.0.0", 23 | "react-dom": "^19.0.0", 24 | "server-only": "^0.0.1", 25 | "superjson": "^2.2.1", 26 | "tailwind-merge": "^3.3.0", 27 | "zod": "^3.24.2", 28 | "zustand": "^5.0.4", 29 | }, 30 | "devDependencies": { 31 | "@biomejs/biome": "1.9.4", 32 | "@tailwindcss/postcss": "^4.0.15", 33 | "@types/node": "^22.15.18", 34 | "@types/react": "^19.0.0", 35 | "@types/react-dom": "^19.0.0", 36 | "postcss": "^8.5.3", 37 | "prisma": "^6.5.0", 38 | "tailwindcss": "^4.0.15", 39 | "tw-animate-css": "^1.2.9", 40 | "typescript": "^5.8.2", 41 | }, 42 | }, 43 | }, 44 | "packages": { 45 | "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], 46 | 47 | "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], 48 | 49 | "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], 50 | 51 | "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], 52 | 53 | "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], 54 | 55 | "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], 56 | 57 | "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], 58 | 59 | "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], 60 | 61 | "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], 62 | 63 | "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], 64 | 65 | "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], 66 | 67 | "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], 68 | 69 | "@floating-ui/core": ["@floating-ui/core@1.7.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA=="], 70 | 71 | "@floating-ui/dom": ["@floating-ui/dom@1.7.0", "", { "dependencies": { "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg=="], 72 | 73 | "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="], 74 | 75 | "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], 76 | 77 | "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.4", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.1", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA=="], 78 | 79 | "@formatjs/fast-memoize": ["@formatjs/fast-memoize@2.2.7", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ=="], 80 | 81 | "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@2.11.2", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/icu-skeleton-parser": "1.8.14", "tslib": "^2.8.0" } }, "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA=="], 82 | 83 | "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@1.8.14", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "tslib": "^2.8.0" } }, "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ=="], 84 | 85 | "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.5.10", "", { "dependencies": { "tslib": "2" } }, "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q=="], 86 | 87 | "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.1.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A=="], 88 | 89 | "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.1.0" }, "os": "darwin", "cpu": "x64" }, "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q=="], 90 | 91 | "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.1.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA=="], 92 | 93 | "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.1.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ=="], 94 | 95 | "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.1.0", "", { "os": "linux", "cpu": "arm" }, "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA=="], 96 | 97 | "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.1.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew=="], 98 | 99 | "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.1.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ=="], 100 | 101 | "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.1.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA=="], 102 | 103 | "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.1.0", "", { "os": "linux", "cpu": "x64" }, "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q=="], 104 | 105 | "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.1.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w=="], 106 | 107 | "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.1.0", "", { "os": "linux", "cpu": "x64" }, "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A=="], 108 | 109 | "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.1.0" }, "os": "linux", "cpu": "arm" }, "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA=="], 110 | 111 | "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.1.0" }, "os": "linux", "cpu": "arm64" }, "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ=="], 112 | 113 | "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.1.0" }, "os": "linux", "cpu": "s390x" }, "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA=="], 114 | 115 | "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.1.0" }, "os": "linux", "cpu": "x64" }, "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA=="], 116 | 117 | "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" }, "os": "linux", "cpu": "arm64" }, "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ=="], 118 | 119 | "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.1.0" }, "os": "linux", "cpu": "x64" }, "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg=="], 120 | 121 | "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.1", "", { "dependencies": { "@emnapi/runtime": "^1.4.0" }, "cpu": "none" }, "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg=="], 122 | 123 | "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw=="], 124 | 125 | "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.1", "", { "os": "win32", "cpu": "x64" }, "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw=="], 126 | 127 | "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], 128 | 129 | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], 130 | 131 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 132 | 133 | "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 134 | 135 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 136 | 137 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 138 | 139 | "@next/env": ["@next/env@15.3.3", "", {}, "sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw=="], 140 | 141 | "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg=="], 142 | 143 | "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw=="], 144 | 145 | "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw=="], 146 | 147 | "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA=="], 148 | 149 | "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw=="], 150 | 151 | "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw=="], 152 | 153 | "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ=="], 154 | 155 | "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw=="], 156 | 157 | "@prisma/client": ["@prisma/client@6.9.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-Gg7j1hwy3SgF1KHrh0PZsYvAaykeR0PaxusnLXydehS96voYCGt1U5zVR31NIouYc63hWzidcrir1a7AIyCsNQ=="], 158 | 159 | "@prisma/config": ["@prisma/config@6.9.0", "", { "dependencies": { "jiti": "2.4.2" } }, "sha512-Wcfk8/lN3WRJd5w4jmNQkUwhUw0eksaU/+BlAJwPQKW10k0h0LC9PD/6TQFmqKVbHQL0vG2z266r0S1MPzzhbA=="], 160 | 161 | "@prisma/debug": ["@prisma/debug@6.9.0", "", {}, "sha512-bFeur/qi/Q+Mqk4JdQ3R38upSYPebv5aOyD1RKywVD+rAMLtRkmTFn28ZuTtVOnZHEdtxnNOCH+bPIeSGz1+Fg=="], 162 | 163 | "@prisma/engines": ["@prisma/engines@6.9.0", "", { "dependencies": { "@prisma/debug": "6.9.0", "@prisma/engines-version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e", "@prisma/fetch-engine": "6.9.0", "@prisma/get-platform": "6.9.0" } }, "sha512-im0X0bwDLA0244CDf8fuvnLuCQcBBdAGgr+ByvGfQY9wWl6EA+kRGwVk8ZIpG65rnlOwtaWIr/ZcEU5pNVvq9g=="], 164 | 165 | "@prisma/engines-version": ["@prisma/engines-version@6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e", "", {}, "sha512-Qp9gMoBHgqhKlrvumZWujmuD7q4DV/gooEyPCLtbkc13EZdSz2RsGUJ5mHb3RJgAbk+dm6XenqG7obJEhXcJ6Q=="], 166 | 167 | "@prisma/fetch-engine": ["@prisma/fetch-engine@6.9.0", "", { "dependencies": { "@prisma/debug": "6.9.0", "@prisma/engines-version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e", "@prisma/get-platform": "6.9.0" } }, "sha512-PMKhJdl4fOdeE3J3NkcWZ+tf3W6rx3ht/rLU8w4SXFRcLhd5+3VcqY4Kslpdm8osca4ej3gTfB3+cSk5pGxgFg=="], 168 | 169 | "@prisma/get-platform": ["@prisma/get-platform@6.9.0", "", { "dependencies": { "@prisma/debug": "6.9.0" } }, "sha512-/B4n+5V1LI/1JQcHp+sUpyRT1bBgZVPHbsC4lt4/19Xp4jvNIVcq5KYNtQDk5e/ukTSjo9PZVAxxy9ieFtlpTQ=="], 170 | 171 | "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], 172 | 173 | "@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="], 174 | 175 | "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], 176 | 177 | "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], 178 | 179 | "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], 180 | 181 | "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], 182 | 183 | "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], 184 | 185 | "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="], 186 | 187 | "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="], 188 | 189 | "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], 190 | 191 | "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], 192 | 193 | "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ=="], 194 | 195 | "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], 196 | 197 | "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], 198 | 199 | "@radix-ui/react-select": ["@radix-ui/react-select@2.2.5", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.7", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA=="], 200 | 201 | "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], 202 | 203 | "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], 204 | 205 | "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], 206 | 207 | "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], 208 | 209 | "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], 210 | 211 | "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], 212 | 213 | "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], 214 | 215 | "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], 216 | 217 | "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], 218 | 219 | "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], 220 | 221 | "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], 222 | 223 | "@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="], 224 | 225 | "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], 226 | 227 | "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], 228 | 229 | "@t3-oss/env-core": ["@t3-oss/env-core@0.13.6", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-rH7FgcB1YGbv/tvv7mdJAxnNvRkK/gEqdVYBwO1AVvaWOTNuftqskxkEYyhM2O+lkNPJmTq5YBE7H+Unl7CLjg=="], 230 | 231 | "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.6", "", { "dependencies": { "@t3-oss/env-core": "0.13.6" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-KcA5U8L+Be4OuR5YxmrBzkeo7WsKkEFJDwcAgYFwcBgxxc3oJBdTB7KPQfVrx7wjtX5aDr2jZ0LG55yEXqQi9A=="], 232 | 233 | "@tailwindcss/node": ["@tailwindcss/node@4.1.8", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.8" } }, "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q=="], 234 | 235 | "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.8", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.8", "@tailwindcss/oxide-darwin-arm64": "4.1.8", "@tailwindcss/oxide-darwin-x64": "4.1.8", "@tailwindcss/oxide-freebsd-x64": "4.1.8", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", "@tailwindcss/oxide-linux-x64-musl": "4.1.8", "@tailwindcss/oxide-wasm32-wasi": "4.1.8", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A=="], 236 | 237 | "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg=="], 238 | 239 | "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A=="], 240 | 241 | "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw=="], 242 | 243 | "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg=="], 244 | 245 | "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8", "", { "os": "linux", "cpu": "arm" }, "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ=="], 246 | 247 | "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q=="], 248 | 249 | "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ=="], 250 | 251 | "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g=="], 252 | 253 | "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg=="], 254 | 255 | "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.8", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg=="], 256 | 257 | "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA=="], 258 | 259 | "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.8", "", { "os": "win32", "cpu": "x64" }, "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ=="], 260 | 261 | "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.8", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.8", "@tailwindcss/oxide": "4.1.8", "postcss": "^8.4.41", "tailwindcss": "4.1.8" } }, "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw=="], 262 | 263 | "@tanstack/query-core": ["@tanstack/query-core@5.80.6", "", {}, "sha512-nl7YxT/TAU+VTf+e2zTkObGTyY8YZBMnbgeA1ee66lIVqzKlYursAII6z5t0e6rXgwUMJSV4dshBTNacNpZHbQ=="], 264 | 265 | "@tanstack/react-query": ["@tanstack/react-query@5.80.6", "", { "dependencies": { "@tanstack/query-core": "5.80.6" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-izX+5CnkpON3NQGcEm3/d7LfFQNo9ZpFtX2QsINgCYK9LT2VCIdi8D3bMaMSNhrAJCznRoAkFic76uvLroALBw=="], 266 | 267 | "@trpc/client": ["@trpc/client@11.3.1", "", { "peerDependencies": { "@trpc/server": "11.3.1", "typescript": ">=5.7.2" } }, "sha512-UlLsUN7x8qD07FaGzdz/FG3K+kpaYowZkmVZ8R4Nlx1Yj01Kgr1Y+cGjtBdRpd+0l7uVylwDPmHA1OxYE6zHgA=="], 268 | 269 | "@trpc/react-query": ["@trpc/react-query@11.3.1", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.3.1", "@trpc/server": "11.3.1", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-hVo+W7bRLo6QhhuV369IvCo/Ut0HWkpSprA4N8zSkcXwCf7dZN3BH/neKlTprFPyZWsLNFjRlDhymzwpaYwLAQ=="], 270 | 271 | "@trpc/server": ["@trpc/server@11.3.1", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-5Rjigjqwl9ieXL91ebZ1M4FPFXQJ5qipRyZuBoNzvqkq7Qlig+2F4WwLELyiTSNk2bGzdJTXJMKLhRE9Z56D8w=="], 272 | 273 | "@types/node": ["@types/node@22.15.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA=="], 274 | 275 | "@types/react": ["@types/react@19.1.6", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q=="], 276 | 277 | "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="], 278 | 279 | "aria-hidden": ["aria-hidden@1.2.4", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A=="], 280 | 281 | "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], 282 | 283 | "caniuse-lite": ["caniuse-lite@1.0.30001718", "", {}, "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw=="], 284 | 285 | "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], 286 | 287 | "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], 288 | 289 | "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], 290 | 291 | "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 292 | 293 | "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], 294 | 295 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 296 | 297 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 298 | 299 | "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], 300 | 301 | "copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="], 302 | 303 | "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], 304 | 305 | "decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="], 306 | 307 | "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 308 | 309 | "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], 310 | 311 | "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], 312 | 313 | "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], 314 | 315 | "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 316 | 317 | "intl-messageformat": ["intl-messageformat@10.7.16", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.2", "tslib": "^2.8.0" } }, "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug=="], 318 | 319 | "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], 320 | 321 | "is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="], 322 | 323 | "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], 324 | 325 | "lefthook": ["lefthook@1.11.13", "", { "optionalDependencies": { "lefthook-darwin-arm64": "1.11.13", "lefthook-darwin-x64": "1.11.13", "lefthook-freebsd-arm64": "1.11.13", "lefthook-freebsd-x64": "1.11.13", "lefthook-linux-arm64": "1.11.13", "lefthook-linux-x64": "1.11.13", "lefthook-openbsd-arm64": "1.11.13", "lefthook-openbsd-x64": "1.11.13", "lefthook-windows-arm64": "1.11.13", "lefthook-windows-x64": "1.11.13" }, "bin": { "lefthook": "bin/index.js" } }, "sha512-SDTk3D4nW1XRpR9u9fdYQ/qj1xeZVIwZmIFdJUnyq+w9ZLdCCvIrOmtD8SFiJowSevISjQDC+f9nqyFXUxc0SQ=="], 326 | 327 | "lefthook-darwin-arm64": ["lefthook-darwin-arm64@1.11.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gHwHofXupCtzNLN+8esdWfFTnAEkmBxE/WKA0EwxPPJXdZYa1GUsiG5ipq/CdG/0j8ekYyM9Hzyrrk5BqJ42xw=="], 328 | 329 | "lefthook-darwin-x64": ["lefthook-darwin-x64@1.11.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-zYxkWNUirmTidhskY9J9AwxvdMi3YKH+TqZ3AQ1EOqkOwPBWJQW5PbnzsXDrd3YnrtZScYm/tE/moXJpEPPIpQ=="], 330 | 331 | "lefthook-freebsd-arm64": ["lefthook-freebsd-arm64@1.11.13", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gJzWnllcMcivusmPorEkXPpEciKotlBHn7QxWwYaSjss/U3YdZu+NTjDO30b3qeiVlyq4RAZ4BPKJODXxHHwUA=="], 332 | 333 | "lefthook-freebsd-x64": ["lefthook-freebsd-x64@1.11.13", "", { "os": "freebsd", "cpu": "x64" }, "sha512-689XdchgtDvZQWFFx1szUvm/mqrq/v6laki0odq5FAfcSgUeLu3w+z6UicBS5l55eFJuQTDNKARFqrKJ04e+Vw=="], 334 | 335 | "lefthook-linux-arm64": ["lefthook-linux-arm64@1.11.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-ujCLbaZg5S/Ao8KZAcNSb+Y3gl898ZEM0YKyiZmZo22dFFpm/5gcV46pF3xaqIw5IpH+3YYDTKDU+qTetmARyQ=="], 336 | 337 | "lefthook-linux-x64": ["lefthook-linux-x64@1.11.13", "", { "os": "linux", "cpu": "x64" }, "sha512-O5WdodeBtFOXQlvPcckqp4W/yqVM9DbVQBkvOxwSJlmsxO4sGYK1TqdxH9ihLB85B2kPPssZj9ze36/oizzhVQ=="], 338 | 339 | "lefthook-openbsd-arm64": ["lefthook-openbsd-arm64@1.11.13", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-SyBpciUfvY/lUDbZu7L6MtL/SVG2+yMTckBgb4PdJQhJlisY0IsyOYdlTw2icPPrY7JnwdsFv8UW0EJOB76W4g=="], 340 | 341 | "lefthook-openbsd-x64": ["lefthook-openbsd-x64@1.11.13", "", { "os": "openbsd", "cpu": "x64" }, "sha512-6+/0j6O2dzo9cjTWUKfL2J6hRR7Krna/ssqnW8cWh8QHZKO9WJn34epto9qgjeHwSysou8byI7Mwv5zOGthLCQ=="], 342 | 343 | "lefthook-windows-arm64": ["lefthook-windows-arm64@1.11.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-w5TwZ8bsZ17uOMtYGc5oEb4tCHjNTSeSXRy6H9Yic8E7IsPZtZLkaZGnIIwgXFuhhrcCdc6FuTvKt2tyV7EW2g=="], 344 | 345 | "lefthook-windows-x64": ["lefthook-windows-x64@1.11.13", "", { "os": "win32", "cpu": "x64" }, "sha512-7lvwnIs8CNOXKU4y3i1Pbqna+QegIORkSD2VCuHBNpIJ8H84NpjoG3tKU91IM/aI1a2eUvCk+dw+1rfMRz7Ytg=="], 346 | 347 | "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], 348 | 349 | "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], 350 | 351 | "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], 352 | 353 | "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], 354 | 355 | "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], 356 | 357 | "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], 358 | 359 | "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], 360 | 361 | "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], 362 | 363 | "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], 364 | 365 | "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], 366 | 367 | "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], 368 | 369 | "lucide-react": ["lucide-react@0.511.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w=="], 370 | 371 | "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 372 | 373 | "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 374 | 375 | "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], 376 | 377 | "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], 378 | 379 | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 380 | 381 | "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], 382 | 383 | "next": ["next@15.3.3", "", { "dependencies": { "@next/env": "15.3.3", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.3", "@next/swc-darwin-x64": "15.3.3", "@next/swc-linux-arm64-gnu": "15.3.3", "@next/swc-linux-arm64-musl": "15.3.3", "@next/swc-linux-x64-gnu": "15.3.3", "@next/swc-linux-x64-musl": "15.3.3", "@next/swc-win32-arm64-msvc": "15.3.3", "@next/swc-win32-x64-msvc": "15.3.3", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw=="], 384 | 385 | "next-intl": ["next-intl@4.1.0", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^4.1.0" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-JNJRjc7sdnfUxhZmGcvzDszZ60tQKrygV/VLsgzXhnJDxQPn1cN2rVpc53adA1SvBJwPK2O6Sc6b4gYSILjCzw=="], 386 | 387 | "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], 388 | 389 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 390 | 391 | "postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 392 | 393 | "prisma": ["prisma@6.9.0", "", { "dependencies": { "@prisma/config": "6.9.0", "@prisma/engines": "6.9.0" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-resJAwMyZREC/I40LF6FZ6rZTnlrlrYrb63oW37Gq+U+9xHwbyMSPJjKtM7VZf3gTO86t/Oyz+YeSXr3CmAY1Q=="], 394 | 395 | "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], 396 | 397 | "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], 398 | 399 | "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], 400 | 401 | "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], 402 | 403 | "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], 404 | 405 | "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], 406 | 407 | "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 408 | 409 | "server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="], 410 | 411 | "sharp": ["sharp@0.34.1", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.7.1" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.1", "@img/sharp-darwin-x64": "0.34.1", "@img/sharp-libvips-darwin-arm64": "1.1.0", "@img/sharp-libvips-darwin-x64": "1.1.0", "@img/sharp-libvips-linux-arm": "1.1.0", "@img/sharp-libvips-linux-arm64": "1.1.0", "@img/sharp-libvips-linux-ppc64": "1.1.0", "@img/sharp-libvips-linux-s390x": "1.1.0", "@img/sharp-libvips-linux-x64": "1.1.0", "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", "@img/sharp-libvips-linuxmusl-x64": "1.1.0", "@img/sharp-linux-arm": "0.34.1", "@img/sharp-linux-arm64": "0.34.1", "@img/sharp-linux-s390x": "0.34.1", "@img/sharp-linux-x64": "0.34.1", "@img/sharp-linuxmusl-arm64": "0.34.1", "@img/sharp-linuxmusl-x64": "0.34.1", "@img/sharp-wasm32": "0.34.1", "@img/sharp-win32-ia32": "0.34.1", "@img/sharp-win32-x64": "0.34.1" } }, "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg=="], 412 | 413 | "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], 414 | 415 | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 416 | 417 | "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], 418 | 419 | "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], 420 | 421 | "superjson": ["superjson@2.2.2", "", { "dependencies": { "copy-anything": "^3.0.2" } }, "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q=="], 422 | 423 | "tailwind-merge": ["tailwind-merge@3.3.0", "", {}, "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ=="], 424 | 425 | "tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="], 426 | 427 | "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="], 428 | 429 | "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], 430 | 431 | "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 432 | 433 | "tw-animate-css": ["tw-animate-css@1.3.4", "", {}, "sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg=="], 434 | 435 | "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], 436 | 437 | "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 438 | 439 | "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], 440 | 441 | "use-intl": ["use-intl@4.1.0", "", { "dependencies": { "@formatjs/fast-memoize": "^2.2.0", "@schummar/icu-type-parser": "1.21.5", "intl-messageformat": "^10.5.14" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-mQvDYFvoGn+bm/PWvlQOtluKCknsQ5a9F1Cj0hMfBjMBVTwnOqLPd6srhjvVdEQEQFVyHM1PfyifKqKYb11M9Q=="], 442 | 443 | "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], 444 | 445 | "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], 446 | 447 | "zod": ["zod@3.25.56", "", {}, "sha512-rd6eEF3BTNvQnR2e2wwolfTmUTnp70aUTqr0oaGbHifzC3BKJsoV+Gat8vxUMR1hwOKBs6El+qWehrHbCpW6SQ=="], 448 | 449 | "zustand": ["zustand@5.0.5", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg=="], 450 | 451 | "@formatjs/ecma402-abstract/@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.1", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg=="], 452 | 453 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], 454 | 455 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], 456 | 457 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="], 458 | 459 | "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.10", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ=="], 460 | 461 | "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], 462 | 463 | "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 464 | 465 | "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "~/components", 15 | "utils": "~/lib/utils", 16 | "ui": "~/components/ui", 17 | "lib": "~/lib", 18 | "hooks": "~/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | commands: 3 | check: 4 | glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" 5 | run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} 6 | stage_fixed: true 7 | -------------------------------------------------------------------------------- /messages/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "HomePage": { 3 | "title": "Hello world!" 4 | }, 5 | "Language": { 6 | "en": "English", 7 | "zh": "简体中文" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /messages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "HomePage": { 3 | "title": "你好,世界!" 4 | }, 5 | "Language": { 6 | "en": "English", 7 | "zh": "简体中文" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | import './src/env.js' 6 | import createNextIntlPlugin from 'next-intl/plugin' 7 | 8 | /** @type {import("next").NextConfig} */ 9 | const nextConfig = {} 10 | 11 | const withNextIntl = createNextIntlPlugin() 12 | export default withNextIntl(nextConfig) 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "next dev --turbo", 8 | "build": "next build", 9 | "vercel-build": "bun run db:migrate && next build", 10 | "preview": "next build && next start", 11 | "start": "next start", 12 | "check": "biome check .", 13 | "typecheck": "tsc --noEmit", 14 | "check:unsafe": "biome check --write --unsafe .", 15 | "check:write": "biome check --write .", 16 | "db:generate": "prisma migrate dev", 17 | "db:migrate": "prisma migrate deploy", 18 | "db:push": "prisma db push", 19 | "db:studio": "prisma studio", 20 | "postinstall": "prisma generate" 21 | }, 22 | "dependencies": { 23 | "@prisma/client": "^6.9.0", 24 | "@radix-ui/react-select": "^2.2.5", 25 | "@radix-ui/react-slot": "^1.2.3", 26 | "@t3-oss/env-nextjs": "^0.13.6", 27 | "@tanstack/react-query": "^5.80.6", 28 | "@trpc/client": "^11.3.1", 29 | "@trpc/react-query": "^11.3.1", 30 | "@trpc/server": "^11.3.1", 31 | "class-variance-authority": "^0.7.1", 32 | "clsx": "^2.1.1", 33 | "lefthook": "^1.11.13", 34 | "lucide-react": "^0.511.0", 35 | "next": "^15.3.3", 36 | "next-intl": "^4.1.0", 37 | "next-themes": "^0.4.6", 38 | "react": "^19.1.0", 39 | "react-dom": "^19.1.0", 40 | "server-only": "^0.0.1", 41 | "superjson": "^2.2.2", 42 | "tailwind-merge": "^3.3.0", 43 | "zod": "^3.25.56", 44 | "zustand": "^5.0.5" 45 | }, 46 | "devDependencies": { 47 | "@biomejs/biome": "1.9.4", 48 | "@tailwindcss/postcss": "^4.1.8", 49 | "@types/node": "^22.15.30", 50 | "@types/react": "^19.1.6", 51 | "@types/react-dom": "^19.1.6", 52 | "postcss": "^8.5.4", 53 | "prisma": "^6.9.0", 54 | "tailwindcss": "^4.1.8", 55 | "tw-animate-css": "^1.3.4", 56 | "typescript": "^5.8.3" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250516075647_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Post" ( 3 | "id" SERIAL NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 6 | "updatedAt" TIMESTAMP(3) NOT NULL, 7 | 8 | CONSTRAINT "Post_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE INDEX "Post_name_idx" ON "Post"("name"); 13 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" 4 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "postgresql" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | model Post { 14 | id Int @id @default(autoincrement()) 15 | name String 16 | createdAt DateTime @default(now()) 17 | updatedAt DateTime @updatedAt 18 | 19 | @@index([name]) 20 | } 21 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun0225SUN/nextjs-starter/9fa7a19aa9c5c51177491427ee627a2e61335399/public/favicon.ico -------------------------------------------------------------------------------- /src/app/api/trpc/[trpc]/route.ts: -------------------------------------------------------------------------------- 1 | import { fetchRequestHandler } from '@trpc/server/adapters/fetch' 2 | import type { NextRequest } from 'next/server' 3 | 4 | import { env } from '~/env' 5 | import { appRouter } from '~/server/api/root' 6 | import { createTRPCContext } from '~/server/api/trpc' 7 | 8 | /** 9 | * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when 10 | * handling a HTTP request (e.g. when you make requests from Client Components). 11 | */ 12 | const createContext = async (req: NextRequest) => { 13 | return createTRPCContext({ 14 | headers: req.headers, 15 | }) 16 | } 17 | 18 | const handler = (req: NextRequest) => 19 | fetchRequestHandler({ 20 | endpoint: '/api/trpc', 21 | req, 22 | router: appRouter, 23 | createContext: () => createContext(req), 24 | onError: 25 | env.NODE_ENV === 'development' 26 | ? ({ path, error }) => { 27 | console.error( 28 | `❌ tRPC failed on ${path ?? ''}: ${error.message}`, 29 | ) 30 | } 31 | : undefined, 32 | }) 33 | 34 | export { handler as GET, handler as POST } 35 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '~/styles/globals.css' 2 | 3 | import type { Metadata } from 'next' 4 | import { Geist } from 'next/font/google' 5 | 6 | import { NextIntlClientProvider } from 'next-intl' 7 | import { getLocale } from 'next-intl/server' 8 | import { ThemeProvider } from '~/components/theme/provider' 9 | import { TRPCReactProvider } from '~/trpc/react' 10 | 11 | export const metadata: Metadata = { 12 | title: 'Create T3 App', 13 | description: 'Generated by create-t3-app', 14 | icons: [{ rel: 'icon', url: '/favicon.ico' }], 15 | } 16 | 17 | const geist = Geist({ 18 | subsets: ['latin'], 19 | variable: '--font-geist-sans', 20 | }) 21 | 22 | export default async function RootLayout({ 23 | children, 24 | }: Readonly<{ children: React.ReactNode }>) { 25 | const locale = await getLocale() 26 | 27 | return ( 28 | 33 | 34 | 35 | 36 | 42 | {children} 43 | 44 | 45 | 46 | 47 | 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { getTranslations } from 'next-intl/server' 2 | import { Bear } from '~/components/bear' 3 | import { LanguageToggle } from '~/components/language/toggle' 4 | import { LatestPost } from '~/components/post' 5 | import { ThemeToggle } from '~/components/theme/toggle' 6 | import { HydrateClient, api } from '~/trpc/server' 7 | 8 | export default async function Home() { 9 | const hello = await api.post.hello({ text: 'from tRPC' }) 10 | 11 | const t = await getTranslations('HomePage') 12 | 13 | void api.post.getLatest.prefetch() 14 | 15 | return ( 16 | 17 |
18 |
19 |

{t('title')}

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |

29 | {hello ? hello.greeting : 'Loading tRPC query...'} 30 |

31 |
32 | 33 | 34 |
35 |
36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/components/bear.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Button } from '~/components/ui/button' 4 | import { useBearStore } from '~/store/bear' 5 | 6 | export function Bear() { 7 | const { bears, addABear, removeABear } = useBearStore() 8 | return ( 9 | <> 10 | {bears} 11 |
12 | 18 | 24 |
25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/components/language/toggle.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useLocale, useTranslations } from 'next-intl' 4 | import { useTransition } from 'react' 5 | import { 6 | Select, 7 | SelectContent, 8 | SelectItem, 9 | SelectTrigger, 10 | SelectValue, 11 | } from '~/components/ui/select' 12 | import type { Locale } from '~/i18n/config' 13 | import { setUserLocale } from '~/server/locale' 14 | 15 | export function LanguageToggle() { 16 | const t = useTranslations('Language') 17 | const locale = useLocale() 18 | const [, startTransition] = useTransition() 19 | 20 | const handleLocaleChange = (value: string) => { 21 | const locale = value as Locale 22 | startTransition(() => { 23 | setUserLocale(locale) 24 | }) 25 | } 26 | 27 | return ( 28 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /src/components/post.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useState } from 'react' 4 | import { Button } from '~/components/ui/button' 5 | import { Input } from '~/components/ui/input' 6 | import { api } from '~/trpc/react' 7 | 8 | export function LatestPost() { 9 | const [latestPost] = api.post.getLatest.useSuspenseQuery() 10 | 11 | const utils = api.useUtils() 12 | const [name, setName] = useState('') 13 | const createPost = api.post.create.useMutation({ 14 | onSuccess: async () => { 15 | await utils.post.invalidate() 16 | setName('') 17 | }, 18 | }) 19 | 20 | return ( 21 |
22 |
{ 24 | e.preventDefault() 25 | createPost.mutate({ name }) 26 | }} 27 | className='flex flex-col gap-4' 28 | > 29 | {latestPost ? ( 30 |

Your most recent post: {latestPost.name}

31 | ) : ( 32 |

You have no posts yet.

33 | )} 34 | setName(e.target.value)} 39 | required 40 | /> 41 | 47 |
48 |
49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /src/components/theme/provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ThemeProvider as NextThemesProvider } from 'next-themes' 4 | import type * as React from 'react' 5 | 6 | export function ThemeProvider({ 7 | children, 8 | ...props 9 | }: React.ComponentProps) { 10 | return {children} 11 | } 12 | -------------------------------------------------------------------------------- /src/components/theme/toggle.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Moon, Sun } from 'lucide-react' 4 | import { useTheme } from 'next-themes' 5 | import { cn } from '~/lib/utils' 6 | 7 | interface ThemeToggleProps { 8 | className?: string 9 | size?: number 10 | strokeWidth?: number 11 | } 12 | 13 | export function ThemeToggle({ 14 | size = 20, 15 | strokeWidth = 2, 16 | className, 17 | }: ThemeToggleProps) { 18 | const { setTheme, resolvedTheme } = useTheme() 19 | 20 | const toggleTheme = () => { 21 | const newTheme = resolvedTheme === 'light' ? 'dark' : 'light' 22 | 23 | if (!document.startViewTransition) { 24 | setTheme(newTheme) 25 | } else { 26 | document.startViewTransition(() => setTheme(newTheme)) 27 | } 28 | } 29 | 30 | return ( 31 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "~/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", 16 | outline: 17 | "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", 20 | ghost: 21 | "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", 22 | link: "text-primary underline-offset-4 hover:underline", 23 | }, 24 | size: { 25 | default: "h-9 px-4 py-2 has-[>svg]:px-3", 26 | sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", 27 | lg: "h-10 rounded-md px-6 has-[>svg]:px-4", 28 | icon: "size-9", 29 | }, 30 | }, 31 | defaultVariants: { 32 | variant: "default", 33 | size: "default", 34 | }, 35 | } 36 | ) 37 | 38 | function Button({ 39 | className, 40 | variant, 41 | size, 42 | asChild = false, 43 | ...props 44 | }: React.ComponentProps<"button"> & 45 | VariantProps & { 46 | asChild?: boolean 47 | }) { 48 | const Comp = asChild ? Slot : "button" 49 | 50 | return ( 51 | 56 | ) 57 | } 58 | 59 | export { Button, buttonVariants } 60 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "~/lib/utils" 4 | 5 | function Input({ className, type, ...props }: React.ComponentProps<"input">) { 6 | return ( 7 | 18 | ) 19 | } 20 | 21 | export { Input } 22 | -------------------------------------------------------------------------------- /src/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SelectPrimitive from "@radix-ui/react-select" 5 | import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" 6 | 7 | import { cn } from "~/lib/utils" 8 | 9 | function Select({ 10 | ...props 11 | }: React.ComponentProps) { 12 | return 13 | } 14 | 15 | function SelectGroup({ 16 | ...props 17 | }: React.ComponentProps) { 18 | return 19 | } 20 | 21 | function SelectValue({ 22 | ...props 23 | }: React.ComponentProps) { 24 | return 25 | } 26 | 27 | function SelectTrigger({ 28 | className, 29 | size = "default", 30 | children, 31 | ...props 32 | }: React.ComponentProps & { 33 | size?: "sm" | "default" 34 | }) { 35 | return ( 36 | 45 | {children} 46 | 47 | 48 | 49 | 50 | ) 51 | } 52 | 53 | function SelectContent({ 54 | className, 55 | children, 56 | position = "popper", 57 | ...props 58 | }: React.ComponentProps) { 59 | return ( 60 | 61 | 72 | 73 | 80 | {children} 81 | 82 | 83 | 84 | 85 | ) 86 | } 87 | 88 | function SelectLabel({ 89 | className, 90 | ...props 91 | }: React.ComponentProps) { 92 | return ( 93 | 98 | ) 99 | } 100 | 101 | function SelectItem({ 102 | className, 103 | children, 104 | ...props 105 | }: React.ComponentProps) { 106 | return ( 107 | 115 | 116 | 117 | 118 | 119 | 120 | {children} 121 | 122 | ) 123 | } 124 | 125 | function SelectSeparator({ 126 | className, 127 | ...props 128 | }: React.ComponentProps) { 129 | return ( 130 | 135 | ) 136 | } 137 | 138 | function SelectScrollUpButton({ 139 | className, 140 | ...props 141 | }: React.ComponentProps) { 142 | return ( 143 | 151 | 152 | 153 | ) 154 | } 155 | 156 | function SelectScrollDownButton({ 157 | className, 158 | ...props 159 | }: React.ComponentProps) { 160 | return ( 161 | 169 | 170 | 171 | ) 172 | } 173 | 174 | export { 175 | Select, 176 | SelectContent, 177 | SelectGroup, 178 | SelectItem, 179 | SelectLabel, 180 | SelectScrollDownButton, 181 | SelectScrollUpButton, 182 | SelectSeparator, 183 | SelectTrigger, 184 | SelectValue, 185 | } 186 | -------------------------------------------------------------------------------- /src/env.js: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | export const env = createEnv({ 5 | /** 6 | * Specify your server-side environment variables schema here. This way you can ensure the app 7 | * isn't built with invalid env vars. 8 | */ 9 | server: { 10 | DATABASE_URL: z.string().url(), 11 | NODE_ENV: z 12 | .enum(['development', 'test', 'production']) 13 | .default('development'), 14 | }, 15 | 16 | /** 17 | * Specify your client-side environment variables schema here. This way you can ensure the app 18 | * isn't built with invalid env vars. To expose them to the client, prefix them with 19 | * `NEXT_PUBLIC_`. 20 | */ 21 | client: { 22 | // NEXT_PUBLIC_CLIENTVAR: z.string(), 23 | }, 24 | 25 | /** 26 | * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. 27 | * middlewares) or client-side so we need to destruct manually. 28 | */ 29 | runtimeEnv: { 30 | DATABASE_URL: process.env.DATABASE_URL, 31 | NODE_ENV: process.env.NODE_ENV, 32 | // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, 33 | }, 34 | /** 35 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially 36 | * useful for Docker builds. 37 | */ 38 | skipValidation: !!process.env.SKIP_ENV_VALIDATION, 39 | /** 40 | * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and 41 | * `SOME_VAR=''` will throw an error. 42 | */ 43 | emptyStringAsUndefined: true, 44 | }) 45 | -------------------------------------------------------------------------------- /src/i18n/config.ts: -------------------------------------------------------------------------------- 1 | export type Locale = 'en' | 'zh' 2 | 3 | export const defaultLocale: Locale = 'en' 4 | -------------------------------------------------------------------------------- /src/i18n/request.ts: -------------------------------------------------------------------------------- 1 | import { getRequestConfig } from 'next-intl/server' 2 | import { getUserLocale } from '~/server/locale' 3 | 4 | export default getRequestConfig(async () => { 5 | const locale = await getUserLocale() 6 | 7 | return { 8 | locale, 9 | messages: (await import(`../../messages/${locale}.json`)).default, 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /src/server/api/root.ts: -------------------------------------------------------------------------------- 1 | import { postRouter } from '~/server/api/routers/post' 2 | import { createCallerFactory, createTRPCRouter } from '~/server/api/trpc' 3 | 4 | /** 5 | * This is the primary router for your server. 6 | * 7 | * All routers added in /api/routers should be manually added here. 8 | */ 9 | export const appRouter = createTRPCRouter({ 10 | post: postRouter, 11 | }) 12 | 13 | // export type definition of API 14 | export type AppRouter = typeof appRouter 15 | 16 | /** 17 | * Create a server-side caller for the tRPC API. 18 | * @example 19 | * const trpc = createCaller(createContext); 20 | * const res = await trpc.post.all(); 21 | * ^? Post[] 22 | */ 23 | export const createCaller = createCallerFactory(appRouter) 24 | -------------------------------------------------------------------------------- /src/server/api/routers/post.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import { createTRPCRouter, publicProcedure } from '~/server/api/trpc' 4 | 5 | export const postRouter = createTRPCRouter({ 6 | hello: publicProcedure 7 | .input(z.object({ text: z.string() })) 8 | .query(({ input }) => { 9 | return { 10 | greeting: `Hello ${input.text}`, 11 | } 12 | }), 13 | 14 | create: publicProcedure 15 | .input(z.object({ name: z.string().min(1) })) 16 | .mutation(async ({ ctx, input }) => { 17 | return ctx.db.post.create({ 18 | data: { 19 | name: input.name, 20 | }, 21 | }) 22 | }), 23 | 24 | getLatest: publicProcedure.query(async ({ ctx }) => { 25 | const post = await ctx.db.post.findFirst({ 26 | orderBy: { createdAt: 'desc' }, 27 | }) 28 | 29 | return post ?? null 30 | }), 31 | }) 32 | -------------------------------------------------------------------------------- /src/server/api/trpc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: 3 | * 1. You want to modify request context (see Part 1). 4 | * 2. You want to create a new middleware or type of procedure (see Part 3). 5 | * 6 | * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will 7 | * need to use are documented accordingly near the end. 8 | */ 9 | import { initTRPC } from '@trpc/server' 10 | import superjson from 'superjson' 11 | import { ZodError } from 'zod' 12 | 13 | import { db } from '~/server/db' 14 | 15 | /** 16 | * 1. CONTEXT 17 | * 18 | * This section defines the "contexts" that are available in the backend API. 19 | * 20 | * These allow you to access things when processing a request, like the database, the session, etc. 21 | * 22 | * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each 23 | * wrap this and provides the required context. 24 | * 25 | * @see https://trpc.io/docs/server/context 26 | */ 27 | export const createTRPCContext = async (opts: { headers: Headers }) => { 28 | return { 29 | db, 30 | ...opts, 31 | } 32 | } 33 | 34 | /** 35 | * 2. INITIALIZATION 36 | * 37 | * This is where the tRPC API is initialized, connecting the context and transformer. We also parse 38 | * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation 39 | * errors on the backend. 40 | */ 41 | const t = initTRPC.context().create({ 42 | transformer: superjson, 43 | errorFormatter({ shape, error }) { 44 | return { 45 | ...shape, 46 | data: { 47 | ...shape.data, 48 | zodError: 49 | error.cause instanceof ZodError ? error.cause.flatten() : null, 50 | }, 51 | } 52 | }, 53 | }) 54 | 55 | /** 56 | * Create a server-side caller. 57 | * 58 | * @see https://trpc.io/docs/server/server-side-calls 59 | */ 60 | export const createCallerFactory = t.createCallerFactory 61 | 62 | /** 63 | * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) 64 | * 65 | * These are the pieces you use to build your tRPC API. You should import these a lot in the 66 | * "/src/server/api/routers" directory. 67 | */ 68 | 69 | /** 70 | * This is how you create new routers and sub-routers in your tRPC API. 71 | * 72 | * @see https://trpc.io/docs/router 73 | */ 74 | export const createTRPCRouter = t.router 75 | 76 | /** 77 | * Middleware for timing procedure execution and adding an artificial delay in development. 78 | * 79 | * You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating 80 | * network latency that would occur in production but not in local development. 81 | */ 82 | const timingMiddleware = t.middleware(async ({ next, path }) => { 83 | const start = Date.now() 84 | 85 | if (t._config.isDev) { 86 | // artificial delay in dev 87 | const waitMs = Math.floor(Math.random() * 400) + 100 88 | await new Promise((resolve) => setTimeout(resolve, waitMs)) 89 | } 90 | 91 | const result = await next() 92 | 93 | const end = Date.now() 94 | console.log(`[TRPC] ${path} took ${end - start}ms to execute`) 95 | 96 | return result 97 | }) 98 | 99 | /** 100 | * Public (unauthenticated) procedure 101 | * 102 | * This is the base piece you use to build new queries and mutations on your tRPC API. It does not 103 | * guarantee that a user querying is authorized, but you can still access user session data if they 104 | * are logged in. 105 | */ 106 | export const publicProcedure = t.procedure.use(timingMiddleware) 107 | -------------------------------------------------------------------------------- /src/server/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client' 2 | 3 | import { env } from '~/env' 4 | 5 | const createPrismaClient = () => 6 | new PrismaClient({ 7 | log: 8 | env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], 9 | }) 10 | 11 | const globalForPrisma = globalThis as unknown as { 12 | prisma: ReturnType | undefined 13 | } 14 | 15 | export const db = globalForPrisma.prisma ?? createPrismaClient() 16 | 17 | if (env.NODE_ENV !== 'production') globalForPrisma.prisma = db 18 | -------------------------------------------------------------------------------- /src/server/locale.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { cookies } from 'next/headers' 4 | import { type Locale, defaultLocale } from '~/i18n/config' 5 | 6 | // In this example the locale is read from a cookie. You could alternatively 7 | // also read it from a database, backend service, or any other source. 8 | const COOKIE_NAME = 'NEXT_LOCALE' 9 | 10 | export async function getUserLocale() { 11 | return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale 12 | } 13 | 14 | export async function setUserLocale(locale: Locale) { 15 | ;(await cookies()).set(COOKIE_NAME, locale) 16 | } 17 | -------------------------------------------------------------------------------- /src/store/bear.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand' 2 | import { persist } from 'zustand/middleware' 3 | 4 | interface BearStore { 5 | bears: number 6 | addABear: () => void 7 | removeABear: () => void 8 | } 9 | 10 | export const useBearStore = create()( 11 | persist( 12 | (set, get) => ({ 13 | bears: 0, 14 | addABear: () => set({ bears: get().bears + 1 }), 15 | removeABear: () => set({ bears: get().bears - 1 }), 16 | }), 17 | { 18 | name: 'bears', 19 | }, 20 | ), 21 | ) 22 | -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @import 'tw-animate-css'; 3 | 4 | @custom-variant dark (&:is(.dark *)); 5 | 6 | @theme { 7 | --font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif, 8 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; 9 | } 10 | 11 | @theme inline { 12 | --radius-sm: calc(var(--radius) - 4px); 13 | --radius-md: calc(var(--radius) - 2px); 14 | --radius-lg: var(--radius); 15 | --radius-xl: calc(var(--radius) + 4px); 16 | --color-background: var(--background); 17 | --color-foreground: var(--foreground); 18 | --color-card: var(--card); 19 | --color-card-foreground: var(--card-foreground); 20 | --color-popover: var(--popover); 21 | --color-popover-foreground: var(--popover-foreground); 22 | --color-primary: var(--primary); 23 | --color-primary-foreground: var(--primary-foreground); 24 | --color-secondary: var(--secondary); 25 | --color-secondary-foreground: var(--secondary-foreground); 26 | --color-muted: var(--muted); 27 | --color-muted-foreground: var(--muted-foreground); 28 | --color-accent: var(--accent); 29 | --color-accent-foreground: var(--accent-foreground); 30 | --color-destructive: var(--destructive); 31 | --color-border: var(--border); 32 | --color-input: var(--input); 33 | --color-ring: var(--ring); 34 | --color-chart-1: var(--chart-1); 35 | --color-chart-2: var(--chart-2); 36 | --color-chart-3: var(--chart-3); 37 | --color-chart-4: var(--chart-4); 38 | --color-chart-5: var(--chart-5); 39 | --color-sidebar: var(--sidebar); 40 | --color-sidebar-foreground: var(--sidebar-foreground); 41 | --color-sidebar-primary: var(--sidebar-primary); 42 | --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); 43 | --color-sidebar-accent: var(--sidebar-accent); 44 | --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); 45 | --color-sidebar-border: var(--sidebar-border); 46 | --color-sidebar-ring: var(--sidebar-ring); 47 | } 48 | 49 | :root { 50 | --radius: 0.625rem; 51 | --background: oklch(1 0 0); 52 | --foreground: oklch(0.145 0 0); 53 | --card: oklch(1 0 0); 54 | --card-foreground: oklch(0.145 0 0); 55 | --popover: oklch(1 0 0); 56 | --popover-foreground: oklch(0.145 0 0); 57 | --primary: oklch(0.205 0 0); 58 | --primary-foreground: oklch(0.985 0 0); 59 | --secondary: oklch(0.97 0 0); 60 | --secondary-foreground: oklch(0.205 0 0); 61 | --muted: oklch(0.97 0 0); 62 | --muted-foreground: oklch(0.556 0 0); 63 | --accent: oklch(0.97 0 0); 64 | --accent-foreground: oklch(0.205 0 0); 65 | --destructive: oklch(0.577 0.245 27.325); 66 | --border: oklch(0.922 0 0); 67 | --input: oklch(0.922 0 0); 68 | --ring: oklch(0.708 0 0); 69 | --chart-1: oklch(0.646 0.222 41.116); 70 | --chart-2: oklch(0.6 0.118 184.704); 71 | --chart-3: oklch(0.398 0.07 227.392); 72 | --chart-4: oklch(0.828 0.189 84.429); 73 | --chart-5: oklch(0.769 0.188 70.08); 74 | --sidebar: oklch(0.985 0 0); 75 | --sidebar-foreground: oklch(0.145 0 0); 76 | --sidebar-primary: oklch(0.205 0 0); 77 | --sidebar-primary-foreground: oklch(0.985 0 0); 78 | --sidebar-accent: oklch(0.97 0 0); 79 | --sidebar-accent-foreground: oklch(0.205 0 0); 80 | --sidebar-border: oklch(0.922 0 0); 81 | --sidebar-ring: oklch(0.708 0 0); 82 | } 83 | 84 | .dark { 85 | --background: oklch(0.145 0 0); 86 | --foreground: oklch(0.985 0 0); 87 | --card: oklch(0.205 0 0); 88 | --card-foreground: oklch(0.985 0 0); 89 | --popover: oklch(0.205 0 0); 90 | --popover-foreground: oklch(0.985 0 0); 91 | --primary: oklch(0.922 0 0); 92 | --primary-foreground: oklch(0.205 0 0); 93 | --secondary: oklch(0.269 0 0); 94 | --secondary-foreground: oklch(0.985 0 0); 95 | --muted: oklch(0.269 0 0); 96 | --muted-foreground: oklch(0.708 0 0); 97 | --accent: oklch(0.269 0 0); 98 | --accent-foreground: oklch(0.985 0 0); 99 | --destructive: oklch(0.704 0.191 22.216); 100 | --border: oklch(1 0 0 / 10%); 101 | --input: oklch(1 0 0 / 15%); 102 | --ring: oklch(0.556 0 0); 103 | --chart-1: oklch(0.488 0.243 264.376); 104 | --chart-2: oklch(0.696 0.17 162.48); 105 | --chart-3: oklch(0.769 0.188 70.08); 106 | --chart-4: oklch(0.627 0.265 303.9); 107 | --chart-5: oklch(0.645 0.246 16.439); 108 | --sidebar: oklch(0.205 0 0); 109 | --sidebar-foreground: oklch(0.985 0 0); 110 | --sidebar-primary: oklch(0.488 0.243 264.376); 111 | --sidebar-primary-foreground: oklch(0.985 0 0); 112 | --sidebar-accent: oklch(0.269 0 0); 113 | --sidebar-accent-foreground: oklch(0.985 0 0); 114 | --sidebar-border: oklch(1 0 0 / 10%); 115 | --sidebar-ring: oklch(0.556 0 0); 116 | } 117 | 118 | @layer base { 119 | * { 120 | @apply border-border outline-ring/50; 121 | } 122 | body { 123 | @apply bg-background text-foreground; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/trpc/query-client.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query' 2 | import SuperJSON from 'superjson' 3 | 4 | export const createQueryClient = () => 5 | new QueryClient({ 6 | defaultOptions: { 7 | queries: { 8 | // With SSR, we usually want to set some default staleTime 9 | // above 0 to avoid refetching immediately on the client 10 | staleTime: 30 * 1000, 11 | }, 12 | dehydrate: { 13 | serializeData: SuperJSON.serialize, 14 | shouldDehydrateQuery: (query) => 15 | defaultShouldDehydrateQuery(query) || 16 | query.state.status === 'pending', 17 | }, 18 | hydrate: { 19 | deserializeData: SuperJSON.deserialize, 20 | }, 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /src/trpc/react.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { type QueryClient, QueryClientProvider } from '@tanstack/react-query' 4 | import { httpBatchStreamLink, loggerLink } from '@trpc/client' 5 | import { createTRPCReact } from '@trpc/react-query' 6 | import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server' 7 | import { useState } from 'react' 8 | import SuperJSON from 'superjson' 9 | 10 | import type { AppRouter } from '~/server/api/root' 11 | import { createQueryClient } from './query-client' 12 | 13 | let clientQueryClientSingleton: QueryClient | undefined = undefined 14 | const getQueryClient = () => { 15 | if (typeof window === 'undefined') { 16 | // Server: always make a new query client 17 | return createQueryClient() 18 | } 19 | // Browser: use singleton pattern to keep the same query client 20 | clientQueryClientSingleton ??= createQueryClient() 21 | 22 | return clientQueryClientSingleton 23 | } 24 | 25 | export const api = createTRPCReact() 26 | 27 | /** 28 | * Inference helper for inputs. 29 | * 30 | * @example type HelloInput = RouterInputs['example']['hello'] 31 | */ 32 | export type RouterInputs = inferRouterInputs 33 | 34 | /** 35 | * Inference helper for outputs. 36 | * 37 | * @example type HelloOutput = RouterOutputs['example']['hello'] 38 | */ 39 | export type RouterOutputs = inferRouterOutputs 40 | 41 | export function TRPCReactProvider(props: { children: React.ReactNode }) { 42 | const queryClient = getQueryClient() 43 | 44 | const [trpcClient] = useState(() => 45 | api.createClient({ 46 | links: [ 47 | loggerLink({ 48 | enabled: (op) => 49 | process.env.NODE_ENV === 'development' || 50 | (op.direction === 'down' && op.result instanceof Error), 51 | }), 52 | httpBatchStreamLink({ 53 | transformer: SuperJSON, 54 | url: `${getBaseUrl()}/api/trpc`, 55 | headers: () => { 56 | const headers = new Headers() 57 | headers.set('x-trpc-source', 'nextjs-react') 58 | return headers 59 | }, 60 | }), 61 | ], 62 | }), 63 | ) 64 | 65 | return ( 66 | 67 | 71 | {props.children} 72 | 73 | 74 | ) 75 | } 76 | 77 | function getBaseUrl() { 78 | if (typeof window !== 'undefined') return window.location.origin 79 | if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}` 80 | return `http://localhost:${process.env.PORT ?? 3000}` 81 | } 82 | -------------------------------------------------------------------------------- /src/trpc/server.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import { createHydrationHelpers } from '@trpc/react-query/rsc' 4 | import { headers } from 'next/headers' 5 | import { cache } from 'react' 6 | 7 | import { type AppRouter, createCaller } from '~/server/api/root' 8 | import { createTRPCContext } from '~/server/api/trpc' 9 | import { createQueryClient } from './query-client' 10 | 11 | /** 12 | * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when 13 | * handling a tRPC call from a React Server Component. 14 | */ 15 | const createContext = cache(async () => { 16 | const heads = new Headers(await headers()) 17 | heads.set('x-trpc-source', 'rsc') 18 | 19 | return createTRPCContext({ 20 | headers: heads, 21 | }) 22 | }) 23 | 24 | const getQueryClient = cache(createQueryClient) 25 | const caller = createCaller(createContext) 26 | 27 | export const { trpc: api, HydrateClient } = createHydrationHelpers( 28 | caller, 29 | getQueryClient, 30 | ) 31 | -------------------------------------------------------------------------------- /start-database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Use this script to start a docker container for a local development database 3 | 4 | # TO RUN ON WINDOWS: 5 | # 1. Install WSL (Windows Subsystem for Linux) - https://learn.microsoft.com/en-us/windows/wsl/install 6 | # 2. Install Docker Desktop or Podman Deskop 7 | # - Docker Desktop for Windows - https://docs.docker.com/docker-for-windows/install/ 8 | # - Podman Desktop - https://podman.io/getting-started/installation 9 | # 3. Open WSL - `wsl` 10 | # 4. Run this script - `./start-database.sh` 11 | 12 | # On Linux and macOS you can run this script directly - `./start-database.sh` 13 | 14 | # import env variables from .env 15 | set -a 16 | source .env 17 | 18 | DB_PASSWORD=$(echo "$DATABASE_URL" | awk -F':' '{print $3}' | awk -F'@' '{print $1}') 19 | DB_PORT=$(echo "$DATABASE_URL" | awk -F':' '{print $4}' | awk -F'\/' '{print $1}') 20 | DB_NAME=$(echo "$DATABASE_URL" | awk -F'/' '{print $4}') 21 | DB_CONTAINER_NAME="$DB_NAME-postgres" 22 | 23 | if ! [ -x "$(command -v docker)" ] && ! [ -x "$(command -v podman)" ]; then 24 | echo -e "Docker or Podman is not installed. Please install docker or podman and try again.\nDocker install guide: https://docs.docker.com/engine/install/\nPodman install guide: https://podman.io/getting-started/installation" 25 | exit 1 26 | fi 27 | 28 | # determine which docker command to use 29 | if [ -x "$(command -v docker)" ]; then 30 | DOCKER_CMD="docker" 31 | elif [ -x "$(command -v podman)" ]; then 32 | DOCKER_CMD="podman" 33 | fi 34 | 35 | if ! $DOCKER_CMD info > /dev/null 2>&1; then 36 | echo "$DOCKER_CMD daemon is not running. Please start $DOCKER_CMD and try again." 37 | exit 1 38 | fi 39 | 40 | if command -v nc >/dev/null 2>&1; then 41 | if nc -z localhost "$DB_PORT" 2>/dev/null; then 42 | echo "Port $DB_PORT is already in use." 43 | exit 1 44 | fi 45 | else 46 | echo "Warning: Unable to check if port $DB_PORT is already in use (netcat not installed)" 47 | read -p "Do you want to continue anyway? [y/N]: " -r REPLY 48 | if ! [[ $REPLY =~ ^[Yy]$ ]]; then 49 | echo "Aborting." 50 | exit 1 51 | fi 52 | fi 53 | 54 | if [ "$($DOCKER_CMD ps -q -f name=$DB_CONTAINER_NAME)" ]; then 55 | echo "Database container '$DB_CONTAINER_NAME' already running" 56 | exit 0 57 | fi 58 | 59 | if [ "$($DOCKER_CMD ps -q -a -f name=$DB_CONTAINER_NAME)" ]; then 60 | $DOCKER_CMD start "$DB_CONTAINER_NAME" 61 | echo "Existing database container '$DB_CONTAINER_NAME' started" 62 | exit 0 63 | fi 64 | 65 | if [ "$DB_PASSWORD" = "password" ]; then 66 | echo "You are using the default database password" 67 | read -p "Should we generate a random password for you? [y/N]: " -r REPLY 68 | if ! [[ $REPLY =~ ^[Yy]$ ]]; then 69 | echo "Please change the default password in the .env file and try again" 70 | exit 1 71 | fi 72 | # Generate a random URL-safe password 73 | DB_PASSWORD=$(openssl rand -base64 12 | tr '+/' '-_') 74 | sed -i '' "s#:password@#:$DB_PASSWORD@#" .env 75 | fi 76 | 77 | $DOCKER_CMD run -d \ 78 | --name $DB_CONTAINER_NAME \ 79 | -e POSTGRES_USER="postgres" \ 80 | -e POSTGRES_PASSWORD="$DB_PASSWORD" \ 81 | -e POSTGRES_DB="$DB_NAME" \ 82 | -p "$DB_PORT":5432 \ 83 | docker.io/postgres && echo "Database container '$DB_CONTAINER_NAME' was successfully created" 84 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Base Options: */ 4 | "esModuleInterop": true, 5 | "skipLibCheck": true, 6 | "target": "es2022", 7 | "allowJs": true, 8 | "resolveJsonModule": true, 9 | "moduleDetection": "force", 10 | "isolatedModules": true, 11 | "verbatimModuleSyntax": true, 12 | 13 | /* Strictness */ 14 | "strict": true, 15 | "noUncheckedIndexedAccess": true, 16 | "checkJs": true, 17 | 18 | /* Bundled projects */ 19 | "lib": ["dom", "dom.iterable", "ES2022"], 20 | "noEmit": true, 21 | "module": "ESNext", 22 | "moduleResolution": "Bundler", 23 | "jsx": "preserve", 24 | "plugins": [{ "name": "next" }], 25 | "incremental": true, 26 | 27 | /* Path Aliases */ 28 | "baseUrl": ".", 29 | "paths": { 30 | "~/*": ["./src/*"] 31 | } 32 | }, 33 | "include": [ 34 | "next-env.d.ts", 35 | "**/*.ts", 36 | "**/*.tsx", 37 | "**/*.cjs", 38 | "**/*.js", 39 | ".next/types/**/*.ts" 40 | ], 41 | "exclude": ["node_modules"] 42 | } 43 | --------------------------------------------------------------------------------