diff --git a/.changeset/curly-points-attend.md b/.changeset/curly-points-attend.md new file mode 100644 index 0000000000..9dbabf3993 --- /dev/null +++ b/.changeset/curly-points-attend.md @@ -0,0 +1,5 @@ +--- +"create-t3-app": patch +--- + +chore: update drizzle-orm diff --git a/.changeset/dirty-birds-own.md b/.changeset/dirty-birds-own.md new file mode 100644 index 0000000000..c14d69253e --- /dev/null +++ b/.changeset/dirty-birds-own.md @@ -0,0 +1,5 @@ +--- +"create-t3-app": patch +--- + +fix: correct casing of “PlanetScale” diff --git a/.changeset/loud-pugs-bathe.md b/.changeset/loud-pugs-bathe.md new file mode 100644 index 0000000000..9560be24ae --- /dev/null +++ b/.changeset/loud-pugs-bathe.md @@ -0,0 +1,5 @@ +--- +"create-t3-app": patch +--- + +fix: fix password substitution in start-database scripts diff --git a/.changeset/nervous-oranges-deliver.md b/.changeset/nervous-oranges-deliver.md new file mode 100644 index 0000000000..ec4fb8c317 --- /dev/null +++ b/.changeset/nervous-oranges-deliver.md @@ -0,0 +1,5 @@ +--- +"create-t3-app": patch +--- + +fix: generated comments in env file diff --git a/.changeset/sixty-rats-drop.md b/.changeset/sixty-rats-drop.md new file mode 100644 index 0000000000..d4b3f44896 --- /dev/null +++ b/.changeset/sixty-rats-drop.md @@ -0,0 +1,5 @@ +--- +"create-t3-app": patch +--- + +fix: use singleton pattern for client-side QueryClient to support `useSuspenseQuery` when there is no parent `<Suspense>`-element diff --git a/.changeset/smart-pumpkins-compare.md b/.changeset/smart-pumpkins-compare.md new file mode 100644 index 0000000000..17d1761a43 --- /dev/null +++ b/.changeset/smart-pumpkins-compare.md @@ -0,0 +1,5 @@ +--- +"create-t3-app": patch +--- + +add missing `pg` dev dependency when using postgres drizzle option, required for `drizzle-studio` diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 86eca04c05..88c6cccaff 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -15,6 +15,10 @@ env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + jobs: build-t3-app: runs-on: ubuntu-latest diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml index caa1106435..eb8d9bb93f 100644 --- a/.github/workflows/translations.yml +++ b/.github/workflows/translations.yml @@ -43,6 +43,7 @@ jobs: pl: ["matibox", "Infiplaya", "PiotrekPKP"], pt: ["minsk-dev", "Sn0wye", "victoriaquasar", "MattFerreira18", "gilhrpenner"], ru: ["AmadeusTwi", "ronanru", "JohnBakhmat"], + uk: ["pqoqubbw"], "zh-hans": ["fernandoxu", "escwxyz"], }; diff --git a/cli/README.md b/cli/README.md index 1402e751ce..c9544de68a 100644 --- a/cli/README.md +++ b/cli/README.md @@ -51,6 +51,7 @@ The _"T3 Stack"_ is a web development stack made by [Theo](https://twitter.com/t - [Tailwind CSS](https://tailwindcss.com) - [TypeScript](https://typescriptlang.org) - [Prisma](https://prisma.io) +- [Drizzle](https://orm.drizzle.team) - [NextAuth.js](https://next-auth.js.org) ### So... what is `create-t3-app`? A template? diff --git a/cli/package.json b/cli/package.json index e917c98d42..1f9ce8afcc 100644 --- a/cli/package.json +++ b/cli/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@auth/drizzle-adapter": "^0.3.16", "@auth/prisma-adapter": "^1.0.16", - "@planetscale/database": "^1.14.0", + "@planetscale/database": "^1.16.0", "@prisma/adapter-planetscale": "^5.6.0", "@prisma/client": "^5.6.0", "@t3-oss/env-nextjs": "^0.7.1", @@ -76,8 +76,8 @@ "@types/fs-extra": "^11.0.1", "@types/gradient-string": "^1.1.2", "@types/node": "^18.17.0", - "drizzle-kit": "^0.20.13", - "drizzle-orm": "^0.29.3", + "drizzle-kit": "^0.20.14", + "drizzle-orm": "^0.29.4", "next": "^14.1.0", "next-auth": "^4.24.5", "prettier": "^3.1.0", diff --git a/cli/src/cli/index.ts b/cli/src/cli/index.ts index 1cbba39824..402128a7aa 100644 --- a/cli/src/cli/index.ts +++ b/cli/src/cli/index.ts @@ -287,7 +287,7 @@ export const runCli = async (): Promise<CliResults> => { { value: "sqlite", label: "SQLite" }, { value: "mysql", label: "MySQL" }, { value: "postgres", label: "PostgreSQL" }, - { value: "planetscale", label: "Planetscale" }, + { value: "planetscale", label: "PlanetScale" }, ], initialValue: "sqlite", }); diff --git a/cli/src/installers/dependencyVersionMap.ts b/cli/src/installers/dependencyVersionMap.ts index ab18ed3a20..ae3436e51f 100644 --- a/cli/src/installers/dependencyVersionMap.ts +++ b/cli/src/installers/dependencyVersionMap.ts @@ -14,11 +14,12 @@ export const dependencyVersionMap = { "@prisma/adapter-planetscale": "^5.6.0", // Drizzle - "drizzle-orm": "^0.29.3", - "drizzle-kit": "^0.20.13", + "drizzle-orm": "^0.29.4", + "drizzle-kit": "^0.20.14", mysql2: "^3.6.1", - "@planetscale/database": "^1.14.0", + "@planetscale/database": "^1.16.0", postgres: "^3.4.3", + pg: "^8.11.3", "@types/better-sqlite3": "^7.6.6", "better-sqlite3": "^9.0.0", diff --git a/cli/src/installers/drizzle.ts b/cli/src/installers/drizzle.ts index 65f59c23f5..79a2e5193c 100644 --- a/cli/src/installers/drizzle.ts +++ b/cli/src/installers/drizzle.ts @@ -16,6 +16,7 @@ export const drizzleInstaller: Installer = ({ const devPackages: AvailableDependencies[] = ["drizzle-kit"]; if (databaseProvider === "planetscale") devPackages.push("mysql2"); if (databaseProvider === "sqlite") devPackages.push("@types/better-sqlite3"); + if (databaseProvider === "postgres") devPackages.push("pg"); addPackageDependency({ projectDir, diff --git a/cli/src/installers/envVars.ts b/cli/src/installers/envVars.ts index 38198d9ee4..d85afc73eb 100644 --- a/cli/src/installers/envVars.ts +++ b/cli/src/installers/envVars.ts @@ -78,11 +78,11 @@ const getEnvContent = ( if (usingPrisma || usingDrizzle) { if (databaseProvider === "planetscale") { if (usingDrizzle) { - content += `Get the Database URL from the "prisma" dropdown selector in PlanetScale. + content += `# Get the Database URL from the "prisma" dropdown selector in PlanetScale. # Change the query params at the end of the URL to "?ssl={"rejectUnauthorized":true}" DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?ssl={"rejectUnauthorized":true}'`; } else { - content = `Get the Database URL from the "prisma" dropdown selector in PlanetScale. + content = `# Get the Database URL from the "prisma" dropdown selector in PlanetScale. DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?sslaccept=strict'`; } } else if (databaseProvider === "mysql") { diff --git a/cli/template/base/README.md b/cli/template/base/README.md index fba19edacb..67943c7fb4 100644 --- a/cli/template/base/README.md +++ b/cli/template/base/README.md @@ -11,6 +11,7 @@ If you are not familiar with the different technologies used in this project, pl - [Next.js](https://nextjs.org) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) +- [Drizzle](https://orm.drizzle.team) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) diff --git a/cli/template/extras/src/server/db/index-drizzle/with-planetscale.ts b/cli/template/extras/src/server/db/index-drizzle/with-planetscale.ts index 0b766215d0..4613a4c17e 100644 --- a/cli/template/extras/src/server/db/index-drizzle/with-planetscale.ts +++ b/cli/template/extras/src/server/db/index-drizzle/with-planetscale.ts @@ -4,9 +4,4 @@ import { drizzle } from "drizzle-orm/planetscale-serverless"; import { env } from "~/env"; import * as schema from "./schema"; -export const db = drizzle( - new Client({ - url: env.DATABASE_URL, - }).connection(), - { schema } -); +export const db = drizzle(new Client({ url: env.DATABASE_URL }), { schema }); diff --git a/cli/template/extras/src/trpc/react.tsx b/cli/template/extras/src/trpc/react.tsx index 06c119c3bf..1a228566d2 100644 --- a/cli/template/extras/src/trpc/react.tsx +++ b/cli/template/extras/src/trpc/react.tsx @@ -8,10 +8,22 @@ import SuperJSON from "superjson"; import { type AppRouter } from "~/server/api/root"; +const createQueryClient = () => new QueryClient(); + +let clientQueryClientSingleton: QueryClient | undefined = undefined; +const getQueryClient = () => { + if (typeof window === "undefined") { + // Server: always make a new query client + return createQueryClient(); + } + // Browser: use singleton pattern to keep the same query client + return (clientQueryClientSingleton ??= createQueryClient()); +}; + export const api = createTRPCReact<AppRouter>(); export function TRPCReactProvider(props: { children: React.ReactNode }) { - const [queryClient] = useState(() => new QueryClient()); + const queryClient = getQueryClient(); const [trpcClient] = useState(() => api.createClient({ diff --git a/cli/template/extras/start-database/mysql.sh b/cli/template/extras/start-database/mysql.sh index 5319e9286e..c650e09d91 100755 --- a/cli/template/extras/start-database/mysql.sh +++ b/cli/template/extras/start-database/mysql.sh @@ -7,7 +7,7 @@ # 3. Open WSL - `wsl` # 4. Run this script - `./start-database.sh` -# On Lunux and macOS you can run this script directly - `./start-database.sh` +# On Linux and macOS you can run this script directly - `./start-database.sh` DB_CONTAINER_NAME="project1-mysql" @@ -36,7 +36,7 @@ if [ "$DB_PASSWORD" == "password" ]; then exit 1 fi DB_PASSWORD=$(openssl rand -base64 12) - sed -i -e "s/:password@/:$DB_PASSWORD@/" .env + sed -i -e "s#:password@#:$DB_PASSWORD@#" .env fi docker run --name $DB_CONTAINER_NAME -e MYSQL_ROOT_PASSWORD=$DB_PASSWORD -e MYSQL_DATABASE=project1 -d -p 3306:3306 docker.io/mysql diff --git a/cli/template/extras/start-database/postgres.sh b/cli/template/extras/start-database/postgres.sh index f873d4df62..dfaf2d54c3 100755 --- a/cli/template/extras/start-database/postgres.sh +++ b/cli/template/extras/start-database/postgres.sh @@ -7,7 +7,7 @@ # 3. Open WSL - `wsl` # 4. Run this script - `./start-database.sh` -# On Lunux and macOS you can run this script directly - `./start-database.sh` +# On Linux and macOS you can run this script directly - `./start-database.sh` DB_CONTAINER_NAME="project1-postgres" @@ -36,7 +36,7 @@ if [ "$DB_PASSWORD" = "password" ]; then exit 1 fi DB_PASSWORD=$(openssl rand -base64 12) - sed -i -e "s/:password@/:$DB_PASSWORD@/" .env + sed -i -e "s#:password@#:$DB_PASSWORD@#" .env fi docker run --name $DB_CONTAINER_NAME -e POSTGRES_PASSWORD=$DB_PASSWORD -e POSTGRES_DB=project1 -d -p 5432:5432 docker.io/postgres diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6dc865c7c..f3644cc9dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,11 +102,11 @@ importers: specifier: ^1.0.16 version: 1.0.16(@prisma/client@5.6.0) '@planetscale/database': - specifier: ^1.14.0 - version: 1.14.0 + specifier: ^1.16.0 + version: 1.16.0 '@prisma/adapter-planetscale': specifier: ^5.6.0 - version: 5.6.0(@planetscale/database@1.14.0) + version: 5.6.0(@planetscale/database@1.16.0) '@prisma/client': specifier: ^5.6.0 version: 5.6.0(prisma@5.6.0) @@ -138,11 +138,11 @@ importers: specifier: ^18.17.0 version: 18.17.0 drizzle-kit: - specifier: ^0.20.13 - version: 0.20.13 + specifier: ^0.20.14 + version: 0.20.14 drizzle-orm: - specifier: ^0.29.3 - version: 0.29.3(@planetscale/database@1.14.0)(react@18.2.0) + specifier: ^0.29.4 + version: 0.29.4(@planetscale/database@1.16.0)(react@18.2.0) next: specifier: ^14.1.0 version: 14.1.0(@babel/core@7.22.9)(react-dom@18.2.0)(react@18.2.0) @@ -190,7 +190,7 @@ importers: version: 4.1.0(prettier@3.1.0) '@mdx-js/loader': specifier: ^2.3.0 - version: 2.3.0(webpack@5.90.1) + version: 2.3.0(webpack@5.88.2) '@next/mdx': specifier: ^13.4.1 version: 13.4.1(@mdx-js/loader@2.3.0) @@ -317,7 +317,7 @@ importers: version: 0.16.8(astro@2.5.5)(sharp@0.32.6) '@astrojs/mdx': specifier: ^0.19.1 - version: 0.19.1(astro@2.5.5)(rollup@4.11.0) + version: 0.19.1(astro@2.5.5)(rollup@3.26.3) '@astrojs/sitemap': specifier: ^1.3.1 version: 1.3.1 @@ -677,14 +677,14 @@ packages: transitivePeerDependencies: - supports-color - /@astrojs/mdx@0.19.1(astro@2.5.5)(rollup@4.11.0): + /@astrojs/mdx@0.19.1(astro@2.5.5)(rollup@3.26.3): resolution: {integrity: sha512-9GNNZbGT+lGvbRkQK/NaEJcnjj1T94/ne0KwPjJgNCBQrJuskX5IW1hKiE5bRSOFvkAOrBGneYKg0GXYArBOQQ==} engines: {node: '>=16.12.0'} dependencies: '@astrojs/markdown-remark': 2.2.1(astro@2.5.5) '@astrojs/prism': 2.1.2 '@mdx-js/mdx': 2.3.0 - '@mdx-js/rollup': 2.3.0(rollup@4.11.0) + '@mdx-js/rollup': 2.3.0(rollup@3.26.3) acorn: 8.8.2 es-module-lexer: 1.3.0 estree-util-visit: 1.2.1 @@ -802,8 +802,8 @@ packages: '@panva/hkdf': 1.1.1 '@types/cookie': 0.6.0 cookie: 0.6.0 - jose: 5.2.0 - oauth4webapi: 2.7.0 + jose: 5.2.2 + oauth4webapi: 2.10.3 preact: 10.11.3 preact-render-to-string: 5.2.3(preact@10.11.3) dev: true @@ -2208,24 +2208,10 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 '@jridgewell/trace-mapping': 0.3.17 - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.22 - dev: false - /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - dev: false - /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} @@ -2233,30 +2219,19 @@ packages: /@jridgewell/source-map@0.3.5: resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.22 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 dev: false /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: false - /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 - /@jridgewell/trace-mapping@0.3.22: - resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: false - /@ljharb/has-package-exports-patterns@0.0.2: resolution: {integrity: sha512-4/RWEeXDO6bocPONheFe6gX/oQdP/bEpv0oL4HqjPP5DCenBSt0mHgahppY49N0CpsaqffdwPq+TlX9CYOq2Dw==} @@ -2346,14 +2321,14 @@ packages: - supports-color dev: false - /@mdx-js/loader@2.3.0(webpack@5.90.1): + /@mdx-js/loader@2.3.0(webpack@5.88.2): resolution: {integrity: sha512-IqsscXh7Q3Rzb+f5DXYk0HU71PK+WuFsEhf+mSV3fOhpLcEpgsHvTQ2h0T6TlZ5gHOaBeFjkXwB52by7ypMyNg==} peerDependencies: webpack: '>=4' dependencies: '@mdx-js/mdx': 2.3.0 source-map: 0.7.4 - webpack: 5.90.1 + webpack: 5.88.2 transitivePeerDependencies: - supports-color dev: false @@ -2382,14 +2357,14 @@ packages: - supports-color dev: false - /@mdx-js/rollup@2.3.0(rollup@4.11.0): + /@mdx-js/rollup@2.3.0(rollup@3.26.3): resolution: {integrity: sha512-wLvRfJS/M4UmdqTd+WoaySEE7q4BIejYf1xAHXYvtT1du/1Tl/z2450Gg2+Hu7fh05KwRRiehiTP9Yc/Dtn0fA==} peerDependencies: rollup: '>=2' dependencies: '@mdx-js/mdx': 2.3.0 - '@rollup/pluginutils': 5.0.2(rollup@4.11.0) - rollup: 4.11.0 + '@rollup/pluginutils': 5.0.2(rollup@3.26.3) + rollup: 3.26.3 source-map: 0.7.4 vfile: 5.3.7 transitivePeerDependencies: @@ -2416,7 +2391,7 @@ packages: '@mdx-js/react': optional: true dependencies: - '@mdx-js/loader': 2.3.0(webpack@5.90.1) + '@mdx-js/loader': 2.3.0(webpack@5.88.2) source-map: 0.7.4 dev: false @@ -2549,6 +2524,10 @@ packages: '@octokit/openapi-types': 18.0.0 dev: false + /@panva/hkdf@1.0.4: + resolution: {integrity: sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ==} + dev: true + /@panva/hkdf@1.1.1: resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} dev: true @@ -2564,17 +2543,17 @@ packages: tiny-glob: 0.2.9 tslib: 2.5.0 - /@planetscale/database@1.14.0: - resolution: {integrity: sha512-7BB+iH6EH4YJuDeVeEbxpo60BFz3wIXJNXCmBGl40eC4HStRMGZ1COxKfiuPcINWwwrJhAhI9AEI97AOZYT7JQ==} + /@planetscale/database@1.16.0: + resolution: {integrity: sha512-HNUrTqrd8aTRZYMDcsoZ62s36sIWkMMmKZBOehoCWR2WrfNPKq+Q1yQef5okl3pSVlldFnu2h/dbHjOsDTHXug==} engines: {node: '>=16'} dev: true - /@prisma/adapter-planetscale@5.6.0(@planetscale/database@1.14.0): + /@prisma/adapter-planetscale@5.6.0(@planetscale/database@1.16.0): resolution: {integrity: sha512-VhBEXKcRDbkozzo3W0mXd4lIHxvpR6YJkZRCHhPTyAx7gba7kpu/lJ83y/P1+YXRiyN2Qmf91pDVYr1qcpfCwQ==} peerDependencies: '@planetscale/database': ^1.11.0 dependencies: - '@planetscale/database': 1.14.0 + '@planetscale/database': 1.16.0 '@prisma/driver-adapter-utils': 5.6.0 transitivePeerDependencies: - supports-color @@ -3114,7 +3093,7 @@ packages: picomatch: 2.3.1 dev: false - /@rollup/pluginutils@5.0.2(rollup@4.11.0): + /@rollup/pluginutils@5.0.2(rollup@3.26.3): resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -3126,112 +3105,8 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.11.0 - dev: false - - /@rollup/rollup-android-arm-eabi@4.11.0: - resolution: {integrity: sha512-BV+u2QSfK3i1o6FucqJh5IK9cjAU6icjFFhvknzFgu472jzl0bBojfDAkJLBEsHFMo+YZg6rthBvBBt8z12IBQ==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-android-arm64@4.11.0: - resolution: {integrity: sha512-0ij3iw7sT5jbcdXofWO2NqDNjSVVsf6itcAkV2I6Xsq4+6wjW1A8rViVB67TfBEan7PV2kbLzT8rhOVWLI2YXw==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-darwin-arm64@4.11.0: - resolution: {integrity: sha512-yPLs6RbbBMupArf6qv1UDk6dzZvlH66z6NLYEwqTU0VHtss1wkI4UYeeMS7TVj5QRVvaNAWYKP0TD/MOeZ76Zg==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-darwin-x64@4.11.0: - resolution: {integrity: sha512-OvqIgwaGAwnASzXaZEeoJY3RltOFg+WUbdkdfoluh2iqatd090UeOG3A/h0wNZmE93dDew9tAtXgm3/+U/B6bw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-linux-arm-gnueabihf@4.11.0: - resolution: {integrity: sha512-X17s4hZK3QbRmdAuLd2EE+qwwxL8JxyVupEqAkxKPa/IgX49ZO+vf0ka69gIKsaYeo6c1CuwY3k8trfDtZ9dFg==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-linux-arm64-gnu@4.11.0: - resolution: {integrity: sha512-673Lu9EJwxVB9NfYeA4AdNu0FOHz7g9t6N1DmT7bZPn1u6bTF+oZjj+fuxUcrfxWXE0r2jxl5QYMa9cUOj9NFg==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-linux-arm64-musl@4.11.0: - resolution: {integrity: sha512-yFW2msTAQNpPJaMmh2NpRalr1KXI7ZUjlN6dY/FhWlOclMrZezm5GIhy3cP4Ts2rIAC+IPLAjNibjp1BsxCVGg==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-linux-riscv64-gnu@4.11.0: - resolution: {integrity: sha512-kKT9XIuhbvYgiA3cPAGntvrBgzhWkGpBMzuk1V12Xuoqg7CI41chye4HU0vLJnGf9MiZzfNh4I7StPeOzOWJfA==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-linux-x64-gnu@4.11.0: - resolution: {integrity: sha512-6q4ESWlyTO+erp1PSCmASac+ixaDv11dBk1fqyIuvIUc/CmRAX2Zk+2qK1FGo5q7kyDcjHCFVwgGFCGIZGVwCA==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-linux-x64-musl@4.11.0: - resolution: {integrity: sha512-vIAQUmXeMLmaDN78HSE4Kh6xqof2e3TJUKr+LPqXWU4NYNON0MDN9h2+t4KHrPAQNmU3w1GxBQ/n01PaWFwa5w==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-win32-arm64-msvc@4.11.0: - resolution: {integrity: sha512-LVXo9dDTGPr0nezMdqa1hK4JeoMZ02nstUxGYY/sMIDtTYlli1ZxTXBYAz3vzuuvKO4X6NBETciIh7N9+abT1g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-win32-ia32-msvc@4.11.0: - resolution: {integrity: sha512-xZVt6K70Gr3I7nUhug2dN6VRR1ibot3rXqXS3wo+8JP64t7djc3lBFyqO4GiVrhNaAIhUCJtwQ/20dr0h0thmQ==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@rollup/rollup-win32-x64-msvc@4.11.0: - resolution: {integrity: sha512-f3I7h9oTg79UitEco9/2bzwdciYkWr8pITs3meSDSlr1TdvQ7IxkQaaYN2YqZXX5uZhiYL+VuYDmHwNzhx+HOg==} - cpu: [x64] - os: [win32] - requiresBuild: true + rollup: 3.26.3 dev: false - optional: true /@rushstack/eslint-patch@1.3.3: resolution: {integrity: sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==} @@ -3402,11 +3277,11 @@ packages: dependencies: '@types/ms': 0.7.31 - /@types/eslint-scope@3.7.7: - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + /@types/eslint-scope@3.7.4: + resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: '@types/eslint': 8.37.0 - '@types/estree': 1.0.5 + '@types/estree': 1.0.0 dev: false /@types/eslint@8.37.0: @@ -3424,10 +3299,6 @@ packages: /@types/estree@1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: false - /@types/fs-extra@11.0.1: resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} dependencies: @@ -3455,10 +3326,6 @@ packages: /@types/json-schema@7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: false - /@types/json5@0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -3894,12 +3761,12 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: false - /acorn-import-assertions@1.9.0(acorn@8.11.3): + /acorn-import-assertions@1.9.0(acorn@8.8.2): resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} peerDependencies: acorn: ^8 dependencies: - acorn: 8.11.3 + acorn: 8.8.2 dev: false /acorn-jsx@5.3.2(acorn@8.8.2): @@ -3909,12 +3776,6 @@ packages: dependencies: acorn: 8.8.2 - /acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: false - /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} @@ -4238,7 +4099,7 @@ packages: strip-ansi: 7.0.1 supports-esm: 1.0.0 tsconfig-resolver: 3.0.1 - typescript: 5.3.3 + typescript: 5.0.4 unist-util-visit: 4.1.2 vfile: 5.3.7 vite: 4.4.6(@types/node@18.17.0) @@ -4412,20 +4273,20 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001579 + caniuse-lite: 1.0.30001589 electron-to-chromium: 1.4.459 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) - /browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + /browserslist@4.22.2: + resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001587 - electron-to-chromium: 1.4.670 + caniuse-lite: 1.0.30001589 + electron-to-chromium: 1.4.616 node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) + update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: false /buffer-from@1.1.2: @@ -4533,12 +4394,8 @@ packages: resolution: {integrity: sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==} dev: true - /caniuse-lite@1.0.30001579: - resolution: {integrity: sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==} - - /caniuse-lite@1.0.30001587: - resolution: {integrity: sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==} - dev: false + /caniuse-lite@1.0.30001589: + resolution: {integrity: sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==} /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -4591,7 +4448,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.3 + fsevents: 2.3.2 /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -5068,8 +4925,8 @@ packages: wordwrap: 1.0.0 dev: true - /drizzle-kit@0.20.13: - resolution: {integrity: sha512-j9oZSQXNWG+KBJm0Sg3S/zJpncHGKnpqNfFuM4NUxUMGTcihDHhP9SW6Jncqwb5vsP1Xm0a8JLm3PZUIspC/oA==} + /drizzle-kit@0.20.14: + resolution: {integrity: sha512-0fHv3YIEaUcSVPSGyaaBfOi9bmpajjhbJNdPsRMIUvYdLVxBu9eGjH8mRc3Qk7HVmEidFc/lhG1YyJhoXrn5yA==} hasBin: true dependencies: '@drizzle-team/studio': 0.0.39 @@ -5090,8 +4947,8 @@ packages: - supports-color dev: true - /drizzle-orm@0.29.3(@planetscale/database@1.14.0)(react@18.2.0): - resolution: {integrity: sha512-uSE027csliGSGYD0pqtM+SAQATMREb3eSM/U8s6r+Y0RFwTKwftnwwSkqx3oS65UBgqDOM0gMTl5UGNpt6lW0A==} + /drizzle-orm@0.29.4(@planetscale/database@1.16.0)(react@18.2.0): + resolution: {integrity: sha512-ZnSM8TAxFhzH7p1s3+w3pRE/eKaOeNkH9SKitm717pubDVVcV2I0BCDBPGKV+pe02+wMfw37ntlTcCyo2rA3IA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=3' @@ -5161,7 +5018,7 @@ packages: sqlite3: optional: true dependencies: - '@planetscale/database': 1.14.0 + '@planetscale/database': 1.16.0 react: 18.2.0 dev: true @@ -5179,8 +5036,8 @@ packages: /electron-to-chromium@1.4.459: resolution: {integrity: sha512-XXRS5NFv8nCrBL74Rm3qhJjA2VCsRFx0OjHKBMPI0otij56aun8UWiKTDABmd5/7GTR021pA4wivs+Ri6XCElg==} - /electron-to-chromium@1.4.670: - resolution: {integrity: sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A==} + /electron-to-chromium@1.4.616: + resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} dev: false /embla-carousel-autoplay@7.1.0(embla-carousel@7.1.0): @@ -5360,10 +5217,6 @@ packages: /es-module-lexer@1.3.0: resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} - /es-module-lexer@1.4.1: - resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} - dev: false - /es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} engines: {node: '>= 0.4'} @@ -5553,11 +5406,6 @@ packages: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: false - /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -6307,8 +6155,8 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true @@ -7216,12 +7064,12 @@ packages: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} dev: false - /jose@4.15.4: - resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} + /jose@4.13.1: + resolution: {integrity: sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==} dev: true - /jose@5.2.0: - resolution: {integrity: sha512-oW3PCnvyrcm1HMvGTzqjxxfnEs9EoFOFWi2HsEGhlFVOXxTE3K9GKWVMFoFw06yPUqwpvEWic1BmtUZBI/tIjw==} + /jose@5.2.2: + resolution: {integrity: sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==} dev: true /joycon@3.1.1: @@ -8186,14 +8034,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.21.0 - '@panva/hkdf': 1.1.1 + '@panva/hkdf': 1.0.4 cookie: 0.5.0 - jose: 4.15.4 + jose: 4.13.1 next: 14.1.0(@babel/core@7.22.9)(react-dom@18.2.0)(react@18.2.0) oauth: 0.9.15 - openid-client: 5.6.4 - preact: 10.11.3 - preact-render-to-string: 5.2.3(preact@10.11.3) + openid-client: 5.4.0 + preact: 10.13.1 + preact-render-to-string: 5.2.6(preact@10.13.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) uuid: 8.3.2 @@ -8221,7 +8069,7 @@ packages: '@next/env': 14.1.0 '@swc/helpers': 0.5.2 busboy: 1.6.0 - caniuse-lite: 1.0.30001579 + caniuse-lite: 1.0.30001589 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.2.0 @@ -8332,8 +8180,8 @@ packages: set-blocking: 2.0.0 dev: false - /oauth4webapi@2.7.0: - resolution: {integrity: sha512-XxmYWOSL9jO4K/ufyZL0YeqE1M+3ZNJ4gdo8C3bpuBemibFBLAlAXiVia2HCiErTzX+q+CFDlhf4o7P0HVsZ0w==} + /oauth4webapi@2.10.3: + resolution: {integrity: sha512-9FkXEXfzVKzH63GUOZz1zMr3wBaICSzk6DLXx+CGdrQ10ItNk2ePWzYYc1fdmKq1ayGFb2aX97sRCoZ2s0mkDw==} dev: true /oauth@0.9.15: @@ -8434,8 +8282,8 @@ packages: define-properties: 1.2.0 es-abstract: 1.22.3 - /oidc-token-hash@5.0.3: - resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + /oidc-token-hash@5.0.1: + resolution: {integrity: sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==} engines: {node: ^10.13.0 || >=12.0.0} dev: true @@ -8464,13 +8312,13 @@ packages: is-docker: 2.2.1 is-wsl: 2.2.0 - /openid-client@5.6.4: - resolution: {integrity: sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==} + /openid-client@5.4.0: + resolution: {integrity: sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==} dependencies: - jose: 4.15.4 + jose: 4.13.1 lru-cache: 6.0.0 object-hash: 2.2.0 - oidc-token-hash: 5.0.3 + oidc-token-hash: 5.0.1 dev: true /optionator@0.9.1: @@ -8811,10 +8659,23 @@ packages: pretty-format: 3.8.0 dev: true + /preact-render-to-string@5.2.6(preact@10.13.1): + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.13.1 + pretty-format: 3.8.0 + dev: true + /preact@10.11.3: resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} dev: true + /preact@10.13.1: + resolution: {integrity: sha512-KyoXVDU5OqTpG9LXlB3+y639JAGzl8JSBXLn1J9HTSB3gbKcuInga7bZnXLlxmK94ntTs1EFeZp0lrja2AuBYQ==} + dev: true + /prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} engines: {node: '>=10'} @@ -8863,7 +8724,7 @@ packages: engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'} dependencies: '@astrojs/compiler': 1.6.1 - prettier: 3.2.4 + prettier: 3.1.0 sass-formatter: 0.7.6 dev: true @@ -8943,12 +8804,6 @@ packages: engines: {node: '>=14'} hasBin: true - /prettier@3.2.4: - resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} - engines: {node: '>=14'} - hasBin: true - dev: true - /pretty-format@3.8.0: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} dev: true @@ -9473,7 +9328,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.3 + fsevents: 2.3.2 dev: true /rollup@3.26.3: @@ -9481,30 +9336,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.3 - - /rollup@4.11.0: - resolution: {integrity: sha512-2xIbaXDXjf3u2tajvA5xROpib7eegJ9Y/uPlSFhXLNpK9ampCczXAhLEb5yLzJyG3LAdI1NWtNjDXiLyniNdjQ==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - dependencies: - '@types/estree': 1.0.5 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.11.0 - '@rollup/rollup-android-arm64': 4.11.0 - '@rollup/rollup-darwin-arm64': 4.11.0 - '@rollup/rollup-darwin-x64': 4.11.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.11.0 - '@rollup/rollup-linux-arm64-gnu': 4.11.0 - '@rollup/rollup-linux-arm64-musl': 4.11.0 - '@rollup/rollup-linux-riscv64-gnu': 4.11.0 - '@rollup/rollup-linux-x64-gnu': 4.11.0 - '@rollup/rollup-linux-x64-musl': 4.11.0 - '@rollup/rollup-win32-arm64-msvc': 4.11.0 - '@rollup/rollup-win32-ia32-msvc': 4.11.0 - '@rollup/rollup-win32-x64-msvc': 4.11.0 - fsevents: 2.3.3 - dev: false + fsevents: 2.3.2 /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -9576,7 +9408,7 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.15 + '@types/json-schema': 7.0.11 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: false @@ -9623,8 +9455,8 @@ packages: dependencies: lru-cache: 6.0.0 - /serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + /serialize-javascript@6.0.1: + resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} dependencies: randombytes: 2.1.0 dev: false @@ -10200,8 +10032,8 @@ packages: engines: {node: '>=8'} dev: false - /terser-webpack-plugin@5.3.10(webpack@5.90.1): - resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + /terser-webpack-plugin@5.3.9(webpack@5.88.2): + resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -10216,21 +10048,21 @@ packages: uglify-js: optional: true dependencies: - '@jridgewell/trace-mapping': 0.3.22 + '@jridgewell/trace-mapping': 0.3.17 jest-worker: 27.5.1 schema-utils: 3.3.0 - serialize-javascript: 6.0.2 - terser: 5.27.0 - webpack: 5.90.1 + serialize-javascript: 6.0.1 + terser: 5.19.2 + webpack: 5.88.2 dev: false - /terser@5.27.0: - resolution: {integrity: sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==} + /terser@5.19.2: + resolution: {integrity: sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==} engines: {node: '>=10'} hasBin: true dependencies: '@jridgewell/source-map': 0.3.5 - acorn: 8.11.3 + acorn: 8.8.2 commander: 2.20.3 source-map-support: 0.5.21 dev: false @@ -10564,11 +10396,6 @@ packages: engines: {node: '>=12.20'} hasBin: true - /typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} - engines: {node: '>=14.17'} - hasBin: true - /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -10699,14 +10526,14 @@ packages: escalade: 3.1.1 picocolors: 1.0.0 - /update-browserslist-db@1.0.13(browserslist@4.23.0): + /update-browserslist-db@1.0.13(browserslist@4.22.2): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.23.0 - escalade: 3.1.2 + browserslist: 4.22.2 + escalade: 3.1.1 picocolors: 1.0.0 dev: false @@ -10850,7 +10677,7 @@ packages: postcss: 8.4.31 rollup: 3.26.3 optionalDependencies: - fsevents: 2.3.3 + fsevents: 2.3.2 /vitefu@0.2.4(vite@4.4.6): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} @@ -10951,8 +10778,8 @@ packages: engines: {node: '>=10.13.0'} dev: false - /webpack@5.90.1: - resolution: {integrity: sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==} + /webpack@5.88.2: + resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -10961,17 +10788,17 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/eslint-scope': 3.7.4 + '@types/estree': 1.0.0 '@webassemblyjs/ast': 1.11.6 '@webassemblyjs/wasm-edit': 1.11.6 '@webassemblyjs/wasm-parser': 1.11.6 - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) - browserslist: 4.23.0 + acorn: 8.8.2 + acorn-import-assertions: 1.9.0(acorn@8.8.2) + browserslist: 4.22.2 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 - es-module-lexer: 1.4.1 + es-module-lexer: 1.3.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -10982,7 +10809,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.90.1) + terser-webpack-plugin: 5.3.9(webpack@5.88.2) watchpack: 2.4.0 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -11228,3 +11055,7 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/www/src/components/navigation/githubIcon.astro b/www/src/components/navigation/githubIcon.astro index 520ede5905..f5e9388be3 100644 --- a/www/src/components/navigation/githubIcon.astro +++ b/www/src/components/navigation/githubIcon.astro @@ -2,11 +2,17 @@ import { getIsRtlFromUrl, getLanguageFromURL } from "../../languages"; import { fetchGithub } from "../../utils/fetchGithub"; -const githubStars = +let githubStars = 42069; + +try { (await fetchGithub("https://api.github.com/repos/t3-oss/create-t3-app", { throwIfNoAuth: false, fetchType: "repo", }).then((data) => data?.stargazers_count)) ?? 42069; +} catch (e) { + console.error("unable to fetch from github", e); +} + const ONE_HOUR = 1 * 60 * 60; Astro.response.headers.set("Cache-Control", `public, max-age=${ONE_HOUR}`); diff --git a/www/src/config.ts b/www/src/config.ts index aaadc0c59b..3ea1042cfc 100644 --- a/www/src/config.ts +++ b/www/src/config.ts @@ -36,6 +36,7 @@ export const KNOWN_LANGUAGES = { ru: "Русский", no: "Norsk", pl: "Polski", + uk: "Українська", "zh-hans": "简体中文", } as const; export type KnownLanguageCode = keyof typeof KNOWN_LANGUAGES; @@ -202,6 +203,7 @@ export const SIDEBAR: Sidebar = { { text: "Struktura Projektu", link: "pl/folder-structure" }, { text: "FAQ", link: "pl/faq" }, { text: "Kolekcja T3", link: "pl/t3-collection" }, + { text: "Przykłady", link: "pl/examples" }, { text: "Inne Rekomendacje", link: "pl/other-recs" }, ], Usage: [ @@ -223,6 +225,37 @@ export const SIDEBAR: Sidebar = { { text: "Docker", link: "pl/deployment/docker" }, ], }, + uk: { + "Create T3 App": [ + { text: "Вступ", link: "uk/introduction" }, + { text: "Чому CT3A?", link: "uk/why" }, + { text: "Встановлення", link: "uk/installation" }, + { text: "Структура папок", link: "uk/folder-structure" }, + { text: "FAQ", link: "uk/faq" }, + { text: "T3 Колекція", link: "uk/t3-collection" }, + { text: "Приклади", link: "uk/examples" }, + { text: "Інші рекомендації", link: "uk/other-recs" }, + ], + Usage: [ + { text: "Перші кроки", link: "uk/usage/first-steps" }, + { text: "Next.js", link: "uk/usage/next-js" }, + { text: "TypeScript", link: "uk/usage/typescript" }, + { text: "tRPC", link: "uk/usage/trpc" }, + { text: "Drizzle", link: "uk/usage/drizzle" }, + { text: "Prisma", link: "uk/usage/prisma" }, + { text: "NextAuth.js", link: "uk/usage/next-auth" }, + { + text: "Змінні середовища", + link: "uk/usage/env-variables", + }, + { text: "Tailwind CSS", link: "uk/usage/tailwind" }, + ], + Deployment: [ + { text: "Vercel", link: "uk/deployment/vercel" }, + { text: "Netlify", link: "uk/deployment/netlify" }, + { text: "Docker", link: "uk/deployment/docker" }, + ], + }, fr: { "Create T3 App": [ { text: "Introduction", link: "fr/introduction" }, @@ -396,6 +429,11 @@ export const SIDEBAR_HEADER_MAP: Record< Usage: "Korzystanie Z Narzędzia", Deployment: "Deployment", }, + uk: { + "Create T3 App": "Create T3 App", + Usage: "Використання", + Deployment: "Деплоймент", + }, ar: { "Create T3 App": "Create T3 App", Usage: "كيفية الإستخدام؟", diff --git a/www/src/pages/ar/usage/prisma.md b/www/src/pages/ar/usage/prisma.md index 9ac0585239..40ed2b59f9 100644 --- a/www/src/pages/ar/usage/prisma.md +++ b/www/src/pages/ar/usage/prisma.md @@ -77,4 +77,4 @@ main() | Prisma Docs | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | NextAuth.JS Prisma Adapter | https://next-auth.js.org/adapters/prisma | -| Planetscale Connection Guide | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| PlanetScale Connection Guide | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/en/deployment/docker.md b/www/src/pages/en/deployment/docker.md index f99faf93b6..5bc045475d 100644 --- a/www/src/pages/en/deployment/docker.md +++ b/www/src/pages/en/deployment/docker.md @@ -63,7 +63,7 @@ README.md ##### DEPENDENCIES FROM --platform=linux/amd64 node:20-alpine AS deps -RUN apk add --no-cache libc6-compat openssl1.1-compat +RUN apk add --no-cache libc6-compat openssl WORKDIR /app # Install Prisma Client - remove if not using Prisma @@ -148,7 +148,7 @@ You can also use Docker Compose to build the image and run the container. <details> <summary> - Follow steps 1-4 above, click here, and include contents in <code>docker-compose.yml</code>: + Follow steps 1-3 above, click here, and include contents in <code>docker-compose.yml</code>: </summary> <div class="content"> @@ -170,10 +170,10 @@ services: - DATABASE_URL=database_url_goes_here ``` -Run this using the `docker compose up` command: +Build and run this using the `docker compose up --build` command: ```bash -docker compose up +docker compose up --build ``` Open [localhost:3000](http://localhost:3000/) to see your running application. diff --git a/www/src/pages/en/usage/prisma.md b/www/src/pages/en/usage/prisma.md index 12029d2f68..fc82701ca0 100644 --- a/www/src/pages/en/usage/prisma.md +++ b/www/src/pages/en/usage/prisma.md @@ -75,4 +75,4 @@ Then, just run `pnpm db-seed` (or `npm`/`yarn`) to seed your database. | Prisma GitHub | https://github.com/prisma/prisma | | Prisma Migrate Playground | https://playground.prisma.io/guides | | NextAuth.JS Prisma Adapter | https://next-auth.js.org/adapters/prisma | -| Planetscale Connection Guide | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| PlanetScale Connection Guide | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/en/usage/trpc.md b/www/src/pages/en/usage/trpc.md index d65f3333a7..6285346d32 100644 --- a/www/src/pages/en/usage/trpc.md +++ b/www/src/pages/en/usage/trpc.md @@ -147,7 +147,7 @@ You'll notice we use `superjson` as [data transformer](https://trpc.io/docs/v10/ ### 📄 `server/api/routers/*.ts` -This is where you define the routes and procedures of your API. By convention, you [create separate routers](https://trpc.io/docs/v10/router) for related procedures. +This is where you define the routes and procedures of your API. By convention, you [create separate routers](https://trpc.io/docs/v10/server/routers) for related procedures. ### 📄 `server/api/root.ts` diff --git a/www/src/pages/fr/usage/prisma.md b/www/src/pages/fr/usage/prisma.md index 6f474d31a3..fdb500b6fb 100644 --- a/www/src/pages/fr/usage/prisma.md +++ b/www/src/pages/fr/usage/prisma.md @@ -74,4 +74,4 @@ Ensuite, exécutez simplement `pnpm db-seed` (ou `npm`/`yarn`) pour amorcer votr | Documentation Prisma | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | Adaptateur Prisma pour NextAuth.JS | https://next-auth.js.org/adapters/prisma | -| Guide de connexion à Planetscale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| Guide de connexion à PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/ja/usage/prisma.md b/www/src/pages/ja/usage/prisma.md index 32670f9ca2..fa65ebd352 100644 --- a/www/src/pages/ja/usage/prisma.md +++ b/www/src/pages/ja/usage/prisma.md @@ -75,4 +75,4 @@ main() | Prisma GitHub | https://github.com/prisma/prisma | | Prisma マイグレーションプレイグラウンド | https://playground.prisma.io/guides | | NextAuth.JS Prisma アダプタ | https://next-auth.js.org/adapters/prisma | -| Planetscale 接続ガイド | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| PlanetScale 接続ガイド | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/no/usage/prisma.md b/www/src/pages/no/usage/prisma.md index dcd8c0e26d..a80735bd12 100644 --- a/www/src/pages/no/usage/prisma.md +++ b/www/src/pages/no/usage/prisma.md @@ -75,4 +75,4 @@ Deretter kan du kjøre `pnpm db-seed` (eller `npm`/`yarn`) for å fylle inn data | Prisma GitHub | https://github.com/prisma/prisma | | Prisma Migrate Playground | https://playground.prisma.io/guides | | NextAuth.JS Prisma Adapter | https://next-auth.js.org/adapters/prisma | -| Planetscale Tilkoblingsveiledning | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| PlanetScale Tilkoblingsveiledning | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/pl/examples.mdx b/www/src/pages/pl/examples.mdx new file mode 100644 index 0000000000..aeddff3ef7 --- /dev/null +++ b/www/src/pages/pl/examples.mdx @@ -0,0 +1,22 @@ +--- +title: Przykłady +description: Przykłady różnych aplikacji na żywo +layout: ../../layouts/docs.astro +lang: pl +isMdx: true +--- + +import Callout from "../../components/docs/callout.tsx"; +import Form from "../../components/docs/exampleOptionForm.astro"; + +Możesz wypróbować różne kombinacje technologii, które oferuje create-t3-app. + +<Callout type="info"> + Nie można wybrać `prisma` i `drizzle` jednocześnie. +</Callout> + +<Form /> + +<Callout type="warning"> + Niektóre funkcje mogą nie działać, chyba że utworzysz plik env +</Callout> \ No newline at end of file diff --git a/www/src/pages/pl/usage/first-steps.md b/www/src/pages/pl/usage/first-steps.md index b6989849f2..f153968a6a 100644 --- a/www/src/pages/pl/usage/first-steps.md +++ b/www/src/pages/pl/usage/first-steps.md @@ -9,8 +9,14 @@ Skorzytałeś właśnie z szablonu aplikacji T3 i jesteś gotowy, aby zacząć z ## Baza Danych +### Prisma + Jeżeli twoja aplikacja zawiera Prismę, koniecznie uruchom `npx prisma db push` z głównego folderu projektu. Komenda ta zsynchronizuje twój schemat Prismy z bazą danych i wygeneruje typy TypeScripta dla "Prisma Client" bazując na tym schemacie. Uwaga: po wygenerowaniu typów Prismy prawdopodobnie będziesz musiał [zrestartować serwer TypeScripta](https://tinytip.co/tips/vscode-restart-ts/), aby był on w stanie je wykryć. +### Drizzle + +Jeżeli twoja aplikacja zawiera Drizzle, sprawdź plik `.env` po instrukcje jak stworzyć swój `DATABASE_URL`. Po dodaniu `DATABASE_URL` do pliku `.env` uruchom `pnpm db:push` ( lub odpowiednik dla innych menedżerów pakietów) - dokonana zostanie synchronizacja schematów twojej bazy danych. + ## Uwierzytelnianie Jeżeli twoja aplikacja zawiera NextAuth.js, posiadasz przygotowany już przez nas `DiscordProvider`. Jest to jeden z najprostszych providerów oferowanych przez NextAuth.js, w dalszym ciągu wymaga on jednak trochę setupu po twojej części. diff --git a/www/src/pages/pl/usage/prisma.md b/www/src/pages/pl/usage/prisma.md index 06c992281a..3366947806 100644 --- a/www/src/pages/pl/usage/prisma.md +++ b/www/src/pages/pl/usage/prisma.md @@ -75,4 +75,4 @@ Następnie uruchom po prostu `pnpm db-seed` (lub `npm`/`yarn`) aby wykonać seed | GitHub Prismy | https://github.com/prisma/prisma | | Prisma Migrate Playground | https://playground.prisma.io/guides | | Adapter NextAuth.JS dla Prismy | https://next-auth.js.org/adapters/prisma | -| Poradnik Połączenia z Planetscale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| Poradnik Połączenia z PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/pt/usage/prisma.md b/www/src/pages/pt/usage/prisma.md index 00fac9e1eb..7d28aad9f9 100644 --- a/www/src/pages/pt/usage/prisma.md +++ b/www/src/pages/pt/usage/prisma.md @@ -74,4 +74,4 @@ Em seguida, basta executar `pnpm db-seed` (ou `npm`/`yarn`) para propagar seu ba | Documentação do Prisma | https://www.prisma.io/docs/ | | GitHub do Prisma | https://github.com/prisma/prisma | | Adaptador de Prisma para NextAuth.js | https://next-auth.js.org/adapters/prisma | -| Guia de Conexão com Planetscale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| Guia de Conexão com PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/uk/deployment/docker.md b/www/src/pages/uk/deployment/docker.md new file mode 100644 index 0000000000..13a0d94ea2 --- /dev/null +++ b/www/src/pages/uk/deployment/docker.md @@ -0,0 +1,214 @@ +--- +title: Docker +description: Деплоймент в Docker +layout: ../../../layouts/docs.astro +lang: uk +--- + +Ви можете контейнеризувати цей стек і розгорнути його як один контейнер за допомогою Docker або як частину групи контейнерів за допомогою docker-compose. Дивіться [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) для прикладу репозиторію на основі цієї документації. + +## Конфігурація проекту Docker + +Будь ласка, зверніть увагу, що Next.js потребує різних процесів для білда (доступні у фронтенді, з префіксом `NEXT_PUBLIC`) та змінних середовища, доступних тільки на сервері. У цьому прикладі ми використовуємо дві змінні, зверніть увагу на їх позиції в `Dockerfile`, аргументи командного рядка та `docker-compose.yml`: + +- `DATABASE_URL` (використовується сервером) +- `NEXT_PUBLIC_CLIENTVAR` (використовується клієнтом) + +### 1. Конфігурація Next + +У вашому [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), додайте конфігурацію ` output` зі значенням `standalone` для [зменшення розміру образу за допомогою автоматичного використання трасувань виводу](https://nextjs.org/docs/advanced-features/output-file-tracing): + +```diff +export default defineNextConfig({ + reactStrictMode: true, + swcMinify: true, ++ output: "standalone", +}); +``` + +### 2. Створіть dockerignore file + +<details> + <summary> + Натисніть тут і вставте вміст у <code>.dockerignore</code>: + </summary> +<div class="content"> + +``` +.env +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +.git +``` + +</div> + +</details> + +### 3. Створіть Dockerfile + +> Через те, що ми не виймаємо змінні середовища сервера в наш контейнер, [перевірка схеми середовища](/uk/usage/env-variables) не пройде. Щоб цього уникнути, ми повинні додати прапор `SKIP_ENV_VALIDATION=1` до команди білда, щоб схеми оточення не перевірялися під час білда. + +<details> + <summary> + Натисніть тут і вставте вміст у <code>Dockerfile</code>: + </summary> +<div class="content"> + +```docker +##### DEPENDENCIES + +FROM --platform=linux/amd64 node:16-apline3.17 AS deps +RUN apk add --no-cache libc6-compat openssl1.1-compat +WORKDIR /app + +# Install Prisma Client - remove if not using Prisma + +COPY prisma ./ + +# Install dependencies based on the preferred package manager + +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ + +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ + else echo "Lockfile not found." && exit 1; \ + fi + +##### BUILDER + +FROM --platform=linux/amd64 node:16-apline3.17 AS builder +ARG DATABASE_URL +ARG NEXT_PUBLIC_CLIENTVAR +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN \ + if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ + elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ + elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +##### RUNNER + +FROM --platform=linux/amd64 node:16-apline3.17 AS runner +WORKDIR /app + +ENV NODE_ENV production + +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/public ./public +COPY --from=builder /app/package.json ./package.json + +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs +EXPOSE 3000 +ENV PORT 3000 + +CMD ["node", "server.js"] + +``` + +> **_Нотатки_** +> +> - _Емуляція `--platform=linux/amd64` може не бути необхідною після переходу на Node 18._ +> - _Подивіться [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) щоб зрозуміти, чому `libc6-compat` може бути необхідним._ +> - _Використання образів, заснованих на Alpine 3.17 [може призвести до проблем з Prisma](https://github.com/t3-oss/create-t3-app/issues/975). Встановлення `engineType = "binary"` вирішує проблему з Alpine 3.17, [але має пов'язані з цим витрати продуктивності](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ +> - _Next.js збирає [анонімні дані про телеметрію загального використання](https://nextjs.org/telemetry). Розкоментуйте перший екземпляр `ENV NEXT_TELEMETRY_DISABLED 1`, щоб вимкнути телеметрію під час білда. Розкоментуйте другий екземпляр, щоб вимкнути телеметрію під час виконання._ + +</div> +</details> + +## Зберіть та запустіть образ локально + +Зберіть і запустіть цей образ локально за допомогою наступних команд: + +```bash +docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . +docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker +``` + +Відкрийте [localhost:3000](http://localhost:3000/) щоб побачити запущений додаток. + +## Docker Compose + +Ви також можете використовувати Docker Compose для збирання образу та запуску контейнера. + +<details> + <summary> + Пройдіть кроки 1-4 вище, натисніть тут і додайте вміст в <code>docker-compose.yml</code>: + </summary> +<div class="content"> + +```yaml +version: "3.9" +services: + app: + platform: "linux/amd64" + build: + context: . + dockerfile: Dockerfile + args: + NEXT_PUBLIC_CLIENTVAR: "clientvar" + working_dir: /app + ports: + - "3000:3000" + image: t3-app + environment: + - DATABASE_URL=database_url_goes_here +``` + +Запустіть за допомогою команди `docker compose up`: + +```bash +docker compose up +``` + +Відкрийте [localhost:3000](http://localhost:3000/) щоб побачити запущений додаток. + +</div> +</details> + +## Деплоймент на Railway + +Ви можете використовувати PaaS як автоматичний [деплоймент Dockerfile](https://docs.railway.app/deploy/dockerfiles) від [Railway's](https://railway.app) для деплою вашої програми. Якщо у вас [встановлений Railway CLI](https://docs.railway.app/develop/cli#install), ви можете задеплоїти свою програму за допомогою наступних команд: + +```bash +railway login +railway init +railway link +railway up +railway open +``` + +Перейдіть до "Variables" і увімкніть ваш `DATABASE_URL`. Потім перейдіть до "Settings" і виберіть "Generate Domain." Щоб побачити працюючий приклад на Railway, перейдіть до [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). + +## Корисні ресурси + +| Ресурс | Посилання | +| ---------------------------------------- | -------------------------------------------------------------------- | +| Приклад для Dockerfile | https://docs.docker.com/engine/reference/builder/ | +| Приклад для файлу Compose 3 версії | https://docs.docker.com/compose/compose-file/compose-file-v3/ | +| Приклад Docker CLI | https://docs.docker.com/engine/reference/commandline/docker/ | +| Приклад Docker Compose CLI | https://docs.docker.com/compose/reference/ | +| Розгортання Next.js із Docker Image | https://nextjs.org/docs/deployment#docker-image | +| Next.js в Docker'е | https://benmarte.com/blog/nextjs-in-docker/ | +| Приклад Next.js з Docker | https://github.com/vercel/next.js/tree/canary/examples/with-docker | +| Створення Docker образу Next.js програми | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | diff --git a/www/src/pages/uk/deployment/index.astro b/www/src/pages/uk/deployment/index.astro new file mode 100644 index 0000000000..eb6609cd92 --- /dev/null +++ b/www/src/pages/uk/deployment/index.astro @@ -0,0 +1,24 @@ +--- +import IndexPage from "../../../components/docs/indexPage.astro"; +import { SIDEBAR, type Frontmatter } from "../../../config"; +import { getLanguageFromURL } from "../../../languages"; +import Layout from "../../../layouts/docs.astro"; + +const frontmatter: Frontmatter = { + title: "Деплоймент", + layout: "docs", + description: "Навчіться деплоїти вашу програму T3 в продакшен.", +}; + +const lang = getLanguageFromURL(Astro.url.pathname); +const sidebarEntries = SIDEBAR[lang]["Deployment"]!; +const files = await Astro.glob("./*.{md,mdx,astro}"); +--- + +<Layout frontmatter={frontmatter} headings={[]}> + <IndexPage + frontmatter={frontmatter} + sidebarEntries={sidebarEntries} + files={files} + /> +</Layout> diff --git a/www/src/pages/uk/deployment/netlify.md b/www/src/pages/uk/deployment/netlify.md new file mode 100644 index 0000000000..d17576b21d --- /dev/null +++ b/www/src/pages/uk/deployment/netlify.md @@ -0,0 +1,85 @@ +--- +title: Netlify +description: Деплоймент в Netlify +layout: ../../../layouts/docs.astro +--- + +Netlify - це альтернативний провайдер деплою, схожий на Vercel. Ось [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) приклад репозиторію на основі цієї документації. + +## Навіщо деплоїти на Netlify + +Вважають, що Vercel має кращу підтримку Next.js тому що Vercel розробляє Next.js. Вони зацікавлені в тому, щоб платформа була налаштована для оптимальної продуктивності та DX з Next.js. Найчастіше це так і у відхиленні від стандартного шляху не буде сенсу. + +Також існує спільна думка про те, що багато функцій Next.js підтримуються тільки на Vercel. Хоча це правда, що нові функції Next.js будуть тестуватись і підтримуватись на Vercel в момент випуску за замовчуванням, також слід враховувати, що інші провайдери, такі як Netlify, [швидко реалізують та випускають підтримку](https://www.netlify.com/blog/deploy-nextjs-13/) для [стабільних функцій Next.js](https://docs.netlify.com/integrations/frameworks/next-js/overview/). + +Всі провайдери деплойменту мають переваги і недоліки, оскільки жоден хост не може мати кращу підтримку для всіх випадків використання. Наприклад, Netlify розробив свій власний [користувальницький Next.js runtime](https://github.com/netlify/next-runtime) для Netlify Edge Functions (які працюють на Deno Deploy) та [підтримують унікальні проміжні програми для доступу та зміни HTTP-відповідей](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). + +> _Зверніть увагу: Щоб відстежувати статус нестабільних функцій Next 13, див. [Використання каталогу `app` Next 13 на Netlify](https://github.com/netlify/next-runtime/discussions/1724)._ + +## Конфігурація проекту + +Існує кілька способів налаштування інструкцій білда, включаючи пряме використання Netlify CLI або Netlify Dashboard. Хоча це не обов'язково, рекомендується створити та включити файл [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Це гарантує, що форкнуті та клоновані версії проекту будуть легше повторно задеплоєні. + +```toml +[build] + command = "next build" + publish = ".next" +``` + +## Використання Netlify Dashboard + +1. Запуште свій код до репозиторію GitHub і зареєструйтесь на [Netlify](https://app.netlify.com/signup). Після того, як ви створили обліковий запис, натисніть **Add new site** і потім **Import an existing project**. + +![Новий проект у Netlify](/images/netlify-01-new-project.webp) + +2. Підключіть свій провайдер Git. + +![Імпортуйте репозиторій](/images/netlify-02-connect-to-git-provider.webp) + +3. Виберіть репозиторій вашого проекту. + +![Виберіть репозиторій вашого проекту](/images/netlify-03-pick-a-repository-from-github.webp) + +4. Netlify виявить, якщо у вас є файл `netlify.toml` і автоматично налаштує команду білда та директорію публікацій. + +![Налаштування збірки Nextjs](/images/netlify-04-configure-build-settings.webp) + +5. Натисніть **Show advanced**, а потім **New variable**, щоб додати свої змінні середовища. + +![Додати змінні середовища](/images/netlify-05-env-vars.webp) + +6. Натисніть **Deploy site**, зачекайте, поки білд завершиться, і перегляньте свій новий сайт. + +## Використання Netlify CLI + +Для того, щоб задеплоїти проект із командного рядка, ви повинні спочатку запушити свій проект до репозиторію GitHub і [встановити Netlify CLI](https://docs.netlify.com/cli/get-started/). Ви можете встановити `netlify-cli` як залежність проекту або встановити його глобально на вашому комп'ютері за допомогою наступної команди: + +```bash +npm i -g netlify-cli +``` + +Для того, щоб протестувати свій проект локально, запустіть команду [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) та відкрийте [`localhost :8888`](http://localhost:8888/) для перегляду вашої локально запущеної програми Netlify: + +```bash +ntl dev +``` + +Запустіть команду [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment), щоб налаштувати ваш проект: + +```bash +ntl init +``` + +Імпортуйте змінні середовища вашого проекту з вашого файлу `.env` за допомогою [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): + +```bash +ntl env:import .env +``` + +Задеплойте ваш проект за допомогою [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Вам потрібно буде передати прапор `--build`, щоб запустити команду білда перед деплойментом, та прапор `--prod`, щоб задеплоїти на основному URL вашого сайту: + +```bash +ntl deploy --prod --build +``` + +Щоб переглянути приклад на Netlify, перейдіть на [ct3a.netlify.app](https://ct3a.netlify.app/). diff --git a/www/src/pages/uk/deployment/vercel.md b/www/src/pages/uk/deployment/vercel.md new file mode 100644 index 0000000000..2a60ccf160 --- /dev/null +++ b/www/src/pages/uk/deployment/vercel.md @@ -0,0 +1,63 @@ +--- +title: Vercel +description: Деплоймент на Vercel +layout: ../../../layouts/docs.astro +lang: uk +--- + +Ми рекомендуємо деплоїти вашу програму на [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Він дозволяє дуже легко деплоїти Next.js програми. + +## Конфігурація проекту + +Швидше за все Vercel налаштує вашу команду збірки та каталог публікації автоматично. Однак ви також можете уточнити цю інформацію разом з іншими конфігураціями, створивши файл [vercel.json](https://vercel.com/docs/project-configuration) і включивши в нього наступні команди. **У більшості проектів це робити необов'язково.** + +```json +{ + "buildCommand": "npm run build", + "outputDirectory": "dist", + "devCommand": "npm run dev", + "installCommand": "npm install" +} +``` + +## Використання Vercel Dashboard + +1. Після відправки вашого коду до репозиторію GitHub зареєструйтесь на [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) з GitHub і натисніть **Add New Project**. + +![Новий проект на Vercel](/images/vercel-new-project.webp) + +2. Імпортуйте репозиторій GitHub із вашим проектом. + +![Імпортуйте репозиторій](/images/vercel-import-project.webp) + +3. Додайте ваші змінні середовища. + +![Додайте ваші змінні середовища](/images/vercel-env-vars.webp) + +4. Натисніть **Deploy**. Тепер щоразу, коли ви відправляєте зміни до вашого репозиторію, Vercel автоматично передеплоїть вашу програму! + +## Використання Vercel CLI + +Для того, щоб задеплоїти програму з командного рядка, вам спочатку потрібно [встановити Vercel CLI глобально](https://vercel.com/docs/cli#installing-vercel-cli). + +```bash +npm i -g vercel +``` + +Запустіть команду [`vercel`](https://vercel.com/docs/cli/deploying-from-cli), щоб задеплоїти ваш проект. + +```bash +vercel +``` + +Додайте `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` для змінних середовища, таких як рядок підключення до бази даних. Використовуйте `--yes`, якщо хочете пропустити питання деплою та дати відповідь за умовчанням для кожного. + +```bash +vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes +``` + +Після першого деплою ця команда задеплоїть зміни у гілку попереднього перегляду. Вам потрібно буде включити `-prod`, щоб відправити зміни безпосередньо на сайт у продакшені для майбутніх деплоїв. + +```bash +vercel --prod +``` diff --git a/www/src/pages/uk/examples.mdx b/www/src/pages/uk/examples.mdx new file mode 100644 index 0000000000..5753de84b4 --- /dev/null +++ b/www/src/pages/uk/examples.mdx @@ -0,0 +1,22 @@ +--- +title: Приклади +description: Приклади різних існуючих додатків +layout: ../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Callout from "../../components/docs/callout.tsx"; +import Form from "../../components/docs/exampleOptionForm.astro"; + +Ви можете випробувати різні комбінації технологій, які пропонує create-t3-app. + +<Callout type="info"> + Ви не можете вибрати `prisma` та `drizzle` одночасно. +</Callout> + +<Form /> + +<Callout type="warning"> + Деякі функції можуть не працювати, якщо ви не створите файл env +</Callout> diff --git a/www/src/pages/uk/faq.mdx b/www/src/pages/uk/faq.mdx new file mode 100644 index 0000000000..a5b5fadba2 --- /dev/null +++ b/www/src/pages/uk/faq.mdx @@ -0,0 +1,74 @@ +--- +title: FAQ +description: Найпоширеніші запитання про Create T3 App +layout: ../../layouts/docs.astro +lang: uk +--- + +import Callout from "../../components/docs/callout.tsx"; + +Це деякі часті запитання про `create-t3-app`. + +## Що далі? Як я можу створити додаток? + +Ми намагаємося зробити цей проект якомога простішим, тому ви можете почати з того, що ми налаштували для вас, а потім додавати додаткові вам речі, коли вони стануть вам необхідними. + +Якщо ви не знайомі з різними технологіями, що використовуються в цьому проєкті, зверніться до відповідної документації. Якщо щось усе ще не зрозуміло, приєднуйтесь до нашого [Discord](https://t3.gg/discord) і попросіть допомоги. + +- [Next.js](https://nextjs.org/) +- [NextAuth.js](https://next-auth.js.org) +- [Prisma](https://prisma.io) +- [Tailwind CSS](https://tailwindcss.com) +- [tRPC](https://trpc.io) + +## Як мені тримати свій додаток оновленим? + +Додаток Create T3 – це інструмент для будування, а не фреймворк. Це означає, що як тільки ви ініціалізуєте програму, вона стане вашою. Немає жодного інструменту CLI після інсталяції, який би допомагав вам залишатися в курсі подій. Якщо ви хочете відстежувати будь-які вдосконалення, які ми вносимо в шаблон, ви можете [увімкнути сповіщення про випуски](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository) у нашому репозиторії. Зважаючи це, насправді не обов'язково вносити кожну зміну, яку ми вносимо в шаблон у вашій програмі. + +## Які навчальні ресурси зараз доступні? + +Не дивлячись на те, що перераховані нижче ресурси є одними з найкращих для T3 Stack, спільнота (і [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) рекомендує просто почати використовувати стек і вчитися в процесі розробки з його допомогою. + +Якщо ви розглядаєте Create T3 App, швидше за все, ви вже використовували деякі частини стека. То чому б не зануритися в проєкт з головою і не вивчити решту технологій у процесі розробки? + +Ми розуміємо, що цей шлях не підходить для кожного. Тому, якщо ви відчуваєте, що ви спробували рекомендований шлях і цього недостатньо, і вам все ще потрібні ресурси, або ви просто не впевнені у своїх силах та/або почуваєтесь перевантаженим цим стеком, ознайомтеся з цими приголомшливими навчальними матеріалами щодо Create T3 App: + +### Статті + +Деякі з них можуть бути застарілими. + +- [A first look at Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) +- [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) +- [Integrating Stripe into your T3 App](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) + +### Відео + +- [T3 Stack Tutorial - FROM 0 TO PROD FOR $0 (Next.js, tRPC, TypeScript, Tailwind, Prisma & More)](https://www.youtube.com/watch?v=YkOSUVzOAA4) **(рекомендовано)** +- [Jack Herrington - Build a Note Taking app with the T3 Stack](https://www.youtube.com/watch?v=J1gzN1SAhyM) +- [Build a Twitter Clone with the T3 Stack - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) +- [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) +- [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) +- [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) +- [An overview of the Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) + +## Чому в проєкті є файли `.js`? + +Згідно з [T3-Аксіомою #3](/uk/introduction#%D1%82%D0%B8%D0%BF%D0%BE%D0%B1%D0%B5%D0%B7%D0%BF%D0%B5%D0%BA%D0%B0-%D0%BD%D0%B5-%D1%94-%D0%BE%D0%BF%D1%86%D1%96%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D1%8E), ми вважаємо типобезпеку об'єктом першого класу. На жаль, не всі фреймворки та плагіни підтримують TypeScript, тому деякі файли конфігурації мають бути файлами `.js`. + +Ми намагаємося підкреслити, що ці файли є файлами JavaScript не без причини, явно оголошуючи тип кожного файлу (`cjs` або `mjs`), залежно від того, що підтримується бібліотекою, якою він використовується. Крім того, всі файли `js` у цьому проєкті все ще перевіряються на типи за допомогою коментаря `@ts-check` вгорі. + +## У мене не виходить додати i18n до мого додатка. Чи є якісь рекомендації? + +Ми вирішили не включати i18n за замовчуванням у `create-t3-app`, тому що це дуже суб'єктивна тема, і є багато способів її реалізації. + +У той же час, якщо ви зіткнулися з проблемами під час реалізації та хочете побачити проєкт-зразок, у нас є [посилання на репозиторій](https://github.com/juliusmarminge/t3-i18n), яке показує, як ви можете додати i18n в T3 App за допомогою [next-i18next](https://github.com/i18next/next-i18next). + +## Чому ми використовуємо `/pages` а не `/app` з Next.js 13? + +Згідно з [T3-Аксіомою #2](/uk/introduction#%D0%BE%D0%BD%D0%BE%D0%B2%D0%BB%D1%8E%D0%B9%D1%82%D0%B5%D1%81%D1%8C-%D0%B7-%D0%B2%D1%96%D0%B4%D0%BF%D0%BE%D0%B2%D1%96%D0%B4%D0%B0%D0%BB%D1%8C%D0%BD%D1%96%D1%81%D1%82%D1%8E), ми любимо новинки, але цінуємо стабільність, вам буде складно перенести весь маршрутизатор, [не найкраще місце для експериментів](https://youtu.be/mnwUbtieOuI?t=1662). Хоча `/app` [являє собою погляд у майбутнє](https://youtu.be/rnsC-12PVlM?t=818), він ще не готовий до використання в продакшені; API перебуває в бета-версії, і очікується, що він матиме зворотно несумісні зміни. + +<Callout type="info"> + Список підтримуваних, запланованих і тих, що перебувають у розробці, функцій у + каталозі `/app` можна знайти в [бета-документації + Next.js](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). +</Callout> diff --git a/www/src/pages/uk/folder-structure.mdx b/www/src/pages/uk/folder-structure.mdx new file mode 100644 index 0000000000..8982bf2550 --- /dev/null +++ b/www/src/pages/uk/folder-structure.mdx @@ -0,0 +1,225 @@ +--- +title: Файлова структура +description: Файлова структура нового T3 додатка +layout: ../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Diagram from "../../components/docs/folderStructureDiagram.astro"; +import Form from "../../components/docs/folderStructureForm.astro"; + +Будь ласка, виберіть пакети, щоб побачити файлову структуру нового додатка з обраними пакетами. Нижче ви знайдете опис кожного елемента. + +<Form /> + +<Diagram /> + +<div data-components="prisma"> + +### `prisma` + +Папка `prisma` містить файл `schema.prisma`, який використовується для налаштування з'єднання з базою даних і схеми бази даних. Також це місце для зберігання файлів міграції та/або сценаріїв заповнення, якщо вони використовуються. Дивіться [Використання Prisma](/uk/usage/prisma) для отримання додаткової інформації. + +</div> +<div> + +### `public` + +Папка `public` містить статичні файли, які обслуговуються веб-сервером. Файл `favicon.ico` - це приклад статичного файлу. + +</div> +<div> + +### `src/env` + +Використовується для перевірки змінних оточення і визначення типів - дивіться [Змінні оточення](/uk/usage/env-variables). + +</div> +<div> + +### `src/pages` + +Папка `pages` містить усі сторінки додатка Next.js. Файл `index.tsx` у кореневій папці `/pages` є домашньою сторінкою додатка. Файл `_app.tsx` використовується для обертання додатка провайдерами. Дивіться [документацію Next.js](https://nextjs.org/docs/basic-features/pages) для отримання додаткової інформації. + +</div> +<div data-components="nextauth trpc"> + +#### `src/pages/api` + +Папка `api` містить усі маршрути API додатка Next.js. Файл `examples.ts` (з Prisma) містить приклад маршруту, який використовує функцію [Next.js API route](https://nextjs.org/docs/api-routes/introduction) разом із Prisma. Файл `restricted.ts` (з Next-Auth) містить приклад маршруту, який використовує функцію [Next.js API route](https://nextjs.org/docs/api-routes/introduction) і захищений за допомогою [NextAuth.js](https://next-auth.js.org/). + +</div> +<div data-components="nextauth"> + +#### `src/pages/api/auth/[...nextauth].ts` + +Файл `[...nextauth].ts` - це slug-маршрут NextAuth.js для аутентифікації. Він використовується для обробки запитів аутентифікації. Дивіться [Використання NextAuth.js](/uk/usage/next-auth) для отримання додаткової інформації про NextAuth.js і [документацію Next.js про динамічні маршрути](https://nextjs.org/docs/routing/dynamic-routes) для отримання інформації про маршрути catch-all/slug. + +</div> +<div data-components="trpc"> + +#### `src/pages/api/trpc/[trpc].ts` + +Файл `[trpc].ts` - це точка входу tRPC API. Він використовується для обробки запитів tRPC. Дивіться [Використання tRPC](/uk/usage/trpc#-pagesapitrpctrpcts) для отримання додаткової інформації про цей файл і [документацію Next.js про динамічні маршрути](https://nextjs.org/docs/routing/dynamic-routes) для отримання інформації про маршрути catch-all/slug. + +</div> +<div data-components="trpc prisma nextauth"> + +### `src/server` + +Папка `server` використовується для чіткого поділу серверного коду від клієнтського коду. + +</div> +<div data-components="nextauth+trpc"> + +#### `src/server/auth.ts` + +Містить утиліти для автентифікації, такі як отримання сесії користувача на стороні сервера. Дивіться [Використання NextAuth.js](/uk/usage/next-auth#usage-with-trpc) для отримання додаткової інформації. + +</div> +<div data-components="prisma"> + +#### `src/server/db.ts` + +Файл `db.ts` використовується для створення клієнта Prisma на глобальному рівні. Дивіться [Використання Prisma](/uk/usage/prisma#prisma-client) і [кращі практики по використанню Prisma з Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) для отримання додаткової інформації. + +</div> +<div data-components="trpc"> + +### `src/server/api` + +Папка `api` містить серверний код tRPC. + +</div> +<div data-components="trpc"> + +#### `src/server/api/routers` + +Папка `routers` містить усі ваші під-маршрутизатори tRPC. + +</div> +<div data-components="trpc"> + +#### `src/server/api/routers/example.ts` + +Файл `example.ts` - це приклад маршрутизатора tRPC, який використовує допоміжну функцію `publicProcedure` для демонстрації того, як створити публічний маршрут tRPC. + +Базуючись на обраних вами пакетах, цей маршрутизатор містить більше або менше маршрутів для кращого демонстрування використання ваших потреб. + +</div> +<div data-components="trpc"> + +#### `src/server/api/trpc.ts` + +Файл `trpc.ts` - це основний файл конфігурації для вашого tRPC-бекенду. У ньому ми: + +1. Визначаємо контекст, який використовується в запитах tRPC. Дивіться [Використання tRPC](/uk/usage/trpc#-serverapitrpcts) для отримання додаткової інформації. +2. Експортуємо допоміжні функції процедур. Дивіться [Використання tRPC](/uk/usage/trpc#-serverapitrpcts) для отримання додаткової інформації. + +</div> + +<div data-components="trpc"> + +#### `src/server/api/root.ts` + +Файл `root.ts` використовується для мерджу маршрутизаторів tRPC і експорту їх як єдиного маршрутизатора, а також визначення типу маршрутизатора. Дивіться [Використання tRPC](/uk/usage/trpc#-serverapirootts) для отримання додаткової інформації. + +</div> +<div> + +### `src/styles` + +Папка `styles` містить глобальні стилі додатка. + +</div> +<div data-components="nextauth"> + +### `src/types` + +Папка `types` використовується для зберігання повторно використовуваних типів або оголошень типів. + +</div> +<div data-components="nextauth"> + +#### `src/types/next-auth.d.ts` + +Файл `next-auth.d.ts` використовується для розширення типу сесії за замовчуванням NextAuth на включення ідентифікатора користувача. Дивіться [Використання NextAuth.js](/uk/usage/next-auth#включення-userid-в-сеанс) для отримання додаткової інформації. + +</div> +<div data-components="trpc"> + +### `src/utils` + +Папка `utils` використовується для зберігання повторно використовуваних функцій утиліт. + +</div> +<div data-components="trpc"> + +#### `src/utils/api.ts` + +Файл `api.ts` є точкою входу для tRPC на стороні клієнта. Дивіться [Використання tRPC](/uk/usage/trpc#-utilsapits) для отримання додаткової інформації. + +</div> +<div> + +### `.env` + +Файл `.env` використовується для зберігання змінних середовища. Дивіться [Змінні середовища](/uk/usage/env-variables) для отримання додаткової інформації. Цей файл **не** повинен бути доданий в історію git. + +</div> +<div> + +### `.env.example` + +Файл `.env.example` показує приклад змінних середовища на основі обраних бібліотек. Цей файл має бути доданий в історію git. + +</div> +<div> + +### `.eslintrc.cjs` + +Файл `.eslintrc.cjs` використовується для налаштування ESLint. Дивіться [документацію ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) для отримання додаткової інформації. + +</div> +<div> + +### `next-env.d.ts` + +Файл `next-env.d.ts` гарантує, що типи Next.js будуть виявлені компілятором TypeScript. **Ви не повинні видаляти його або редагувати, оскільки він може змінитися в будь-який час.** Дивіться [документацію Next.js](https://nextjs.org/docs/basic-features/typescript#existing-projects) для отримання додаткової інформації. + +</div> +<div> + +### `next.config.mjs` + +Файл `next.config.mjs` використовується для налаштування Next.js. Дивіться [Документацію Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction) для отримання додаткової інформації. Примітка: Розширення .mjs використовується для дозволу імпорту ESM. + +</div> +<div data-components="tailwind"> + +### `postcss.config.cjs` + +Файл `postcss.config.cjs` використовується для використання Tailwind PostCSS. Дивіться [документацію Taiwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss) для отримання додаткової інформації. + +</div> +<div data-components="tailwind"> + +### `prettier.config.mjs` + +Файл `prettier.config.mjs` використовується для налаштування Prettier для ввімкнення prettier-plugin-tailwindcss для форматування класів Tailwind CSS. Дивіться [пост блогу Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) для отримання додаткової інформації. + +</div> +<div> + +### `tsconfig.json` + +Файл `tsconfig.json` використовується для налаштування TypeScript. Деякі значення за замовчуванням, такі як `strict mode`, були увімкнені для забезпечення кращого використання TypeScript для create-t3-app та його бібліотек. Дивіться [документацію TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) або [Використання TypeScript](usage/typescript) для отримання додаткової інформації. + +</div> +<div> +### `drizzle.config.ts` + +Файл `drizzle.config.ts` використовується для налаштування drizzle kit. Дивіться [документацію](https://orm.drizzle.team/kit-docs/config-reference) для отримання додаткової інформації. + +</div> diff --git a/www/src/pages/uk/installation.mdx b/www/src/pages/uk/installation.mdx new file mode 100644 index 0000000000..918d556be2 --- /dev/null +++ b/www/src/pages/uk/installation.mdx @@ -0,0 +1,72 @@ +--- +title: Встановлення +description: Інструкції зі встановлення Create T3 App +layout: ../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Callout from "../../components/docs/callout.tsx"; + +Для створення додатка за допомогою `create-t3-app`, запустіть одну з наступних трьох команд і дайте відповідь на запитання командного рядка: + +### npm + +```bash +npm create t3-app@latest +``` + +### yarn + +```bash +yarn create t3-app +``` + +### pnpm + +```bash +pnpm create t3-app@latest +``` + +### bun + +```bash +bun create t3-app@latest +``` + +Після того, як додаток буде створено, ознайомтеся з [першими кроками](/uk/usage/first-steps), щоб почати роботу над вашим новим додатком. + +## Додаткові параметри + +| Опції/Флаги | Опис | +| ----------------- | ------------------------------------------------------------------ | +| `[dir]` | Додання аргументу з директорії з ім'ям проекту | +| `--noGit` | Повідомлення CLI не ініціалізувати новий git репозиторій у проекті | +| `-y`, `--default` | Створення нового t3-додатку з дефолтними параметрами | +| `--noInstall` | Генерація проекту без встановлення залежностей | + +## Експериментальне використання + +Для нашого CI ми маємо деякі експериментальні прапори, які дають змогу створювати будь-який додаток без будь-яких запитів. Якщо це стосується вашого випадку, ви можете використовувати ці прапори. Зверніть увагу, що ці прапори є експериментальними і можуть змінитися в майбутньому без зміни версії. + +| Опції/Флаги | Опис | +| ------------ | ----------------------------------------------- | +| `--CI` | Повідомлення CLI, що ви перебуваєте в режимі CI | +| `--trpc` | Додавання tRPC у проєкт | +| `--prisma` | Додавання Prisma у проєкт | +| `--nextAuth` | Додавання NextAuth у проєкт | +| `--tailwind` | Додавання Tailwind CSS у проєкт | + +<Callout type="warning"> + Якщо ви не вказуєте прапор `CI`, то інші прапори не мають ефекту. +</Callout> + +Вам не потрібно явно відмовлятися від пакетів, які вам не потрібні. Однак, якщо ви віддаєте перевагу бути точним, ви можете передати `false`, наприклад `--nextAuth false`. + +### Приклад + +Наступний приклад створить додаток T3 з tRPC і Tailwind CSS. + +```bash +pnpm dlx create-t3-app@latest --CI --trpc --tailwind +``` diff --git a/www/src/pages/uk/introduction.md b/www/src/pages/uk/introduction.md new file mode 100644 index 0000000000..ef7284ac02 --- /dev/null +++ b/www/src/pages/uk/introduction.md @@ -0,0 +1,42 @@ +--- +title: Вступ +description: Вступ в T3 стек +layout: ../../layouts/docs.astro +lang: uk +--- + +<div class="embed"> +<iframe width="560" height="315" src="https://www.youtube.com/embed/YkOSUVzOAA4" title="The best stack for your next project" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> +</div> + +## T3 Stack + +_"T3 Stack"_ - це стек веб-розробки, створений [Theo](https://twitter.com/t3dotgg), з упором на простоту, модульність і full-stack типобезпека. + +Основні частини - [**Next.js**](https://nextjs.org/) і [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) майже завжди включені. Якщо ви робите щось, що нагадує бекенд, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), і [**NextAuth.js**](https://next-auth.js.org/) - чудові доповнення. + +Ви могли помітити, що тут... багато частин. Це зроблено наміренно. Змінюйте частини залежно від ваших потреб - цей стек є модульним за своєю суттю :) + +## І... що таке create-t3-app? Шаблон? + +Щось типу? `create-t3-app` - це CLI, створений досвідченими розробниками T3 стека, щоб спростити налаштування модульного додатка T3 стека. Це означає, що кожна частина є необов'язковою, і "шаблон" генерується на основі ваших конкретних потреб. + +Після безлічі проєктів і багатьох років із цією технологією у нас є безліч думок та інсайтів. Ми постаралися зафіксувати їх у цьому CLI. + +Це **НЕ** всеосяжний шаблон. Ми **очікуємо**, що ви будете використовувати свої власні бібліотеки, які вирішують потреби **ВАШОГО** додатка. Хоча ми не хочемо призначати рішення для більш конкретних проблем, таких як управління станом і деплоймент, ми [маємо деякі рекомендації, перераховані тут](/uk/other-recs). + +## Аксіоми T3 + +Будемо чесними - це _суб'єктивний проєкт_. Ми поділилися кількома основними переконаннями навколо створення і розглядаємо їх як основу для наших рішень. + +### Вирішуйте проблеми + +Легко потрапити в пастку "додавання всього" - ми явно не хочемо цього робити. Усе, що додається в `create-t3-app`, повинно вирішувати конкретну проблему, яка існує в основних технологіях, включених до нього. Це означає, що ми не додамо бібліотеки менеджменту стану (`zustand`, `redux`), але ми додамо NextAuth.js та інтегруємо Prisma і tRPC для вас. + +### Оновлюйтесь з відповідальністю + +Ми любимо технологічні новинки. Швидкість і, будемо чесними, приколів від нових фіговін, дійсно круті. Ми вважаємо важливим використовувати їх відповідально, використовуючи більш ризиковані технології в менш ризикованих частинах. Це означає, що ми не будемо ⛔️ робити ставку на ризиковану нову технологію бази даних (SQL - це здорово!). Але ми із задоволенням ✅ зробимо ставку на tRPC, оскільки це просто функції, які легко замінити. + +### Типобезпека не є опціональною + +Зазначена мета Create T3 App - це надати найшвидший спосіб розпочати новий повнофункціональний, **типобезпечний** веб-додаток. Ми серйозно ставимося до типобезпеки в цих частинах, оскільки вона підвищує нашу продуктивність і допомагає нам створювати менше багів. Будь-яке рішення, яке знижує типобезпеку Create T3 App, це рішення, яке повинно бути прийнято в іншому проекті. diff --git a/www/src/pages/uk/other-recs.md b/www/src/pages/uk/other-recs.md new file mode 100644 index 0000000000..83cc0fb19f --- /dev/null +++ b/www/src/pages/uk/other-recs.md @@ -0,0 +1,163 @@ +--- +title: Додаткові рекомендації +description: Бібліотеки та сервіси, які ми рекомендуємо для багатьох проектів +layout: ../../layouts/docs.astro +lang: uk +--- + +Ми розуміємо, що бібліотеки, включені до `create-t3-app`, не вирішують кожну проблему. Хоча ми рекомендуємо розпочати свій проект з того, що ми надаємо, настане час, коли вам потрібно буде встановити інші пакети. Тільки ви можете знати, чого потребує ваш проект, але ось деякі рішення, які ми часто рекомендуємо. + +Це рекомендації окремих учасників Create T3 App і не повинні розглядатись як "офіційні" рекомендації команди Create T3 App або T3-OSS. _**Будь ласка, проведіть власне дослідження, особливо перед тим, як прив'язуватись до платних сервісів**_. + +## Управління станом + +_**Примітка редактора**_: Бібліотеки керування станом можуть бути відмінними, але часто не потрібні. tRPC's хуки React Query повинні впоратися із вашим серверним станом. Для клієнтського стану почніть з `useState` React, і зверніться до одного з цих варіантів, коли вам буде потрібно більше. + +### Zustand + +**Для того, щоб більше ніколи не використовувати Redux** + +"Сучасний простий Redux", який ви не знали, що вам потрібен. [Poimandres](https://github.com/pmndrs) завжди можна довіряти. Ви можете створювати все, від відеодзвінків до ігор та серверів із цією маленькою бібліотекою. + +- [Головна Zustand](https://zustand-demo.pmnd.rs/) +- [Zustand GitHub](https://github.com/pmndrs/zustand) + +### Jotai + +**Для того, щоб більше ніколи не використовувати Context** + +Jotai важко перевершити в підході Atomic. Також від [Poimandres](https://github.com/pmndrs), Jotai дозволяє визначати сінгелтони, які здаються глобальними useState. Відмінний варіант для станів, які не вимагають стейт машини прямо зараз. + +- [Головна Jotai](https://jotai.org/) +- [Jotai GitHub](https://github.com/pmndrs/jotai) + +## Бібліотеки компонентів + +Більшість додатків потребують однакову низку компонентів: перемикачі, меню, модальні вікна і т.д. Ці бібліотеки надають відмінні, доступні компоненти, які ви можете використовувати та налаштовувати на свій розсуд. + +### Бібліотеки компонентів без стилів + +Також відомі як headless бібліотеки, вони надають відмінні, нестилізовані та доступні компоненти, які ви можете налаштувати на свій розсуд. Ось кілька рекомендацій. + +- [Radix UI](https://www.radix-ui.com/) надає потужний набір зручних і доступних примітивів, які ви можете стилізувати за допомогою звичайного CSS або Tailwind CSS. + +- [Headless UI](https://headlessui.com/) від команди Tailwind CSS також надає нестилізовані, доступні компоненти, які бездоганно інтегруються з Tailwind CSS. + +- [React Aria](https://react-spectrum.adobe.com/react-aria/) надає доступні примітивні налаштування UI для вашої системи дизайну. Їхній компонент вибору дати - це найвищий рівень. + +### Бібліотеки компонентів зі стилями + +**Коли вам просто потрібно, щоб ваш додаток виглядав непогано** + +Іноді ви створюєте проект, де вам просто потрібно, щоб інтерфейс користувача виглядав від початку. Для адміністративних панелей управління та інших подібних проектів будь-яка з цих бібліотек компонентів упорається з цим завданням. + +- [Chakra UI](https://chakra-ui.com) +- [Mantine](https://mantine.dev) +- [@shadcn/ui](https://ui.shadcn.com/) + +### Class Variance Authority + +**Для створення бібліотек інтерфейсу користувача** + +Декларативно створюйте бібліотеку інтерфейсу користувача з різними кольоровими, розмірними і т.д. варіантами. Коли ваш проект досягає масштабу, де вам потрібен стандартизований набір компонентів інтерфейсу користувача з кількома варіантами з використанням Tailwind CSS, CVA - відмінний інструмент. + +- [Class Variance Authority GitHub](https://github.com/joe-bell/cva) + +## Анімації + +Наші рекомендації для створення анімацій у вашому додатку. + +### AutoAnimate + +**Для анімацій з одним рядком коду** + +Більшість бібліотек анімації намагаються задовольнити кожен можливий випадок використання і стають громіздкими. AutoAnimate – це інструмент без конфігурації, який дасть вам значне покращення UX без додаткових зусиль розробника. + +- [Головна AutoAnimate](https://auto-animate.formkit.com/) +- [AutoAnimate GitHub](https://github.com/formkit/auto-animate) +- [Компонент для AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) + +### Framer Motion + +**Для складних анімацій із декларативним кодом** + +Framer Motion надає простий, декларативний синтаксис і дозволяє писати менше коду для створення всього: від складних анімацій і навіть до жестів. + +- [Головна Framer Motion](https://framer.com/motion) +- [Документація Framer Motion](https://www.framer.com/docs/) + +## Деплоймент, інфраструктура, бази даних і CI + +### Vercel + +**Для хостинга вашого додатка** + +Vercel позбавив нас болю при деплойменті веб-додатків, зробивши його простою інтеграцією з GitHub, яку можна встановити і забути. Ми без проблем масштабувалися до сотень тисяч користувачів. Він працює на AWS, але має набагато кращий інтерфейс :) + +- [Головна Vercel](https://vercel.com/) +- [Гайд із деплою T3 додатка на Vercel](/uk/deployment/vercel) + +### PlanetScale + +**Для баз даних без занепокоєння** + +PlanetScale - найкраща serverless платформа для баз даних, яку ми використовували. Неймовірна масштабованість, чудовий DX і фантастична ціна. Якщо ви використовуєте SQL (і, сподіваюся, Prisma), це важко перевершити. + +- [Головна PlanetScale](https://planetscale.com/) + +### Railway + +**Для розміщення вашої інфраструктури** + +"Сучасний Heroku". Найпростіший спосіб запустити реальний сервер. Якщо вам не вистачає функціоналу Vercel і PlanetScale, то Railway, ймовірно, вам підійде. Вкажіть його на репозиторій GitHub і готово. + +- [Головна Railway](https://railway.app/) + +### Upstash + +**Для serverless Redis** + +Ми любимо Prisma і PlanetScale, але деякі проєкти вимагають більш продуктивного рішення. Upstash дає вам змогу отримати in-memory продуктивність Redis у вашому безсерверному проєкті, не переймаючись самостійним управлінням інфраструктурою та масштабуванням. + +- [Головна Upstash](https://upstash.com/) + +### Pusher + +**Для безсерверних WebSocket-ів** + +Якщо WebSocket є основним фокусом вашого проекту, ви можете розглянути більш традиційний бекенд, такий як [Fastify](https://www.fastify.io/) (який [також працює з tRPC!](https://trpc.io/docs/v10/fastify)). Але для швидкого додавання WebSocket у T3 App, Pusher - чудовий вибір. + +- [Головна Pusher](https://pusher.com/) + +### Soketi + +Soketi - це самодостатня, проста і швидка альтернатива Pusher. Він повністю сумісний з Pusher SDK, який ви можете використовувати для підключення до сервера. Soketi serverless також перебуває в бета-версії. + +- [Головна Soketi](https://soketi.app) +- [Soketi GitHub](https://github.com/soketi/soketi) + +## Аналітика + +Користувацькі дані дуже цінні, коли ви створюєте додаток. Ось кілька рекомендованих провайдерів аналітики. + +### Plausible + +Потрібна аналітика? Plausible - один із найшвидших способів її отримати. Супер мінімалістичний. У нього навіть є [простий плагін для Next.js](https://plausible.io/docs/proxy/guides/nextjs). + +- [Головна Plausible](https://plausible.io/) + +### Umami + +Umami - це самодостатня, проста, швидка і конфіденційна альтернатива Google Analytics. Ви можете легко задеплоїти його на Vercel, Railway і т.д. з PlanetScale як базою даних. + +- [Головна Umami](https://umami.is/) +- [Umami GitHub](https://github.com/umami-software/umami) +- [Umami Cloud](https://cloud.umami.is/) + +## Інше + +### Next Bundle Analyzer + +Буває складно визначити, що буде включено до збірки вихідних даних для вашого додатка. Next Bundle Analyzer - це простий спосіб візуалізувати й аналізувати JavaScript-бандли, які генеруються. + +- [@next/bundle-analyzer на npm](https://www.npmjs.com/package/@next/bundle-analyzer) diff --git a/www/src/pages/uk/t3-collection.mdx b/www/src/pages/uk/t3-collection.mdx new file mode 100644 index 0000000000..45ab9dc7de --- /dev/null +++ b/www/src/pages/uk/t3-collection.mdx @@ -0,0 +1,33 @@ +--- +title: T3 колекція +description: Круті open source проекти та компанії, що використовують T3 стек +layout: ../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Callout from "../../components/docs/callout.tsx"; +import CompanyList from "../../components/docs/companyList.tsx"; +import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; + +Зробили проект, що використовує T3 стек і хочете поділитися ним? Додайте його до списку! + +## Open Source програми зроблені з використанням T3 стека + +<OpenSourceAppList + descriptionIntl="Опис" + repoIntl="Посилання" + linkIntl="Посилання" +/> + +## Компанії використовують T3 стек + +Нам би дуже хотілося дізнатися про компанії, які використовують T3 стек для своїх додатків. Ваша компанія використовує стек T3 і ви хочете поділитися цим? Додайте її до списку! + +<CompanyList companyIntl="Опис" linkIntl="Посилання" /> + +<Callout type="tip"> + Є крутий проект, що використовує T3 стек? Зробіть [pull + request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) + та додайте його до списку! +</Callout> diff --git a/www/src/pages/uk/usage/drizzle.mdx b/www/src/pages/uk/usage/drizzle.mdx new file mode 100644 index 0000000000..7d27784ea6 --- /dev/null +++ b/www/src/pages/uk/usage/drizzle.mdx @@ -0,0 +1,14 @@ +--- +title: Drizzle +description: Використання Drizzle +layout: ../../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Callout from "../../../components/docs/callout.tsx"; + +<Callout type="info"> + Опція `drizzle` є новим додатком и жодні документи ще не були написані. Ми + будемо раді вашим внескам! +</Callout> diff --git a/www/src/pages/uk/usage/env-variables.mdx b/www/src/pages/uk/usage/env-variables.mdx new file mode 100644 index 0000000000..398ffaec7b --- /dev/null +++ b/www/src/pages/uk/usage/env-variables.mdx @@ -0,0 +1,149 @@ +--- +title: Змінні середовища +description: Початок роботи з Create T3 App +layout: ../../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Callout from "../../../components/docs/callout.tsx"; + +Create T3 App використовує власний пакет [@t3-oss/env-nextjs](https://env.t3.gg) разом із [Zod](https://github.com/colinhacks/zod) для валідації змінних середовища під час виконання _та_ під час збирання, надаючи просту логіку у файлі `src/env.js`: + +## env.js + +_TLDR; Якщо ви хочете додати нову змінну середовища, вам слід додати валідатор до `src/env.js`, а потім пару ключ-значення в `.env`._ + +```ts:env.js +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + NODE_ENV: z.enum(["development", "test", "production"]), + }, + client: { + // NEXT_PUBLIC_CLIENTVAR: z.string(), + }, + runtimeEnv: { + NODE_ENV: process.env.NODE_ENV, + }, +}); +``` + +T3 Env використовує `createEnv`, який відповідає за створення схеми і буде включати головну логіку валідації для клієнтських і серверних змінних середовища. + +<Callout type="info"> + Для отримання додаткової інформації про те, як `createEnv` працює зсередини, + подивіться документацію [T3 Env](https://env.t3.gg/docs/introduction) (EN) +</Callout> + +## Використання змінних середовища + +Коли ви хочете використовувати змінні середовища, ви можете імпортувати їх із `env.js` і використовувати їх так, як ви зазвичай використовували б. Якщо ви імпортуєте цей файл на стороні клієнта і спробуєте отримати серверну змінну, ви отримаєте помилку виконання: + +```ts:pages/api/hello.ts +import { env } from "../../env.js"; + +// `env` is fully typesafe and provides autocompletion +const dbUrl = env.DATABASE_URL; +``` + +```ts:pages/index.tsx +import { env } from "../env.js"; + +// ❌ This will throw a runtime error +const dbUrl = env.DATABASE_URL; + +// ✅ This is fine +const wsKey = env.NEXT_PUBLIC_WS_KEY; +``` + +## .env.example + +Через те, що файл `.env` за замовчуванням не додається до системи контролю версій, ми також додали файл `.env.example`, в якому ви можете за бажанням зберегти копію вашого файлу `.env` з видаленими secrets. Це необов'язково, але ми рекомендуємо тримати приклад в актуальному стані, щоб зробити процес налаштування середовища для нових учасників проекту якомога простішим. + +Деякі фреймворки та інструменти збірки, такі як Next.js, пропонують зберігати secrets у файлі `.env.local` і коммітити файли `.env` у ваш проект. Це не рекомендується, оскільки це може полегшити випадковий комміт secrets у ваш проект. Натомість ми рекомендуємо зберігати secrets в `.env`, тримати ваш файл `.env` в `.gitignore` і коммітити лише файли `.env.example` у ваш проект. + +## Додавання змінних середовища + +Для того, щоб переконатися, що ваша збірка ніколи не завершиться без змінних середовища, які проєкт вимагає, вам потрібно додати нові змінні середовища в **двох** місцях: + +📄 `.env`: Введіть змінну середовища, як зазвичай робите у файлі `.env`, тобто `KEY=VALUE` + +📄 `env.js`: Додайте відповідну логіку валідації для змінних середовища, визначивши для кожної з них Zod схему всередині `createEnv`, наприклад `KEY: z.string()`. Крім цього, переконайтеся в тому, що ви деструктурували їх в опції `runtimeEnv`, наприклад `KEY: process.env.KEY`. + +<Callout type="info"> + Навіщо потрібно деструктурувати змінні середовища всередині `runtimeEnv`? Це + пов'язано з тим, як Next.js збирає змінні середовища в деяких рантаймах. + Деструктуруючи їх вручну, ми гарантуємо, що ці змінні не будуть прибрані з + фінальної збірки. +</Callout> + +Опціонально, ви також можете оновлювати файл `.env.example`: + +📄 `.env.example`: Введіть вашу змінну середовища, але переконайтеся, що не включаєте значення, якщо воно є секретним, тобто `KEY=VALUE` або `KEY=`. + +### Приклад + +_Я хочу додати мій Twitter API токен як змінну середовища на стороні сервера_ + +1. Додайте змінну середовища в `.env`: + +``` +TWITTER_API_TOKEN=1234567890 +``` + +2. Додайте змінну середовища в `env.js`: + +```ts +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + TWITTER_API_TOKEN: z.string(), + }, + // ... + runtimeEnv: { + // ... + TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, + }, +}); +``` + +3. _Опціонально:_ Додайте змінну середовища в `.env.example`, але не включайте токен в `runtimeEnv`. + +``` +TWITTER_API_TOKEN= +``` + +## Type Coercion + +Усі змінні які ви додаєте до `.env` будуть импортовані як рядки, навіть якщо їх значення має представляти інший тип. Якщо ви хочете використовувати ваші змінні середовища як інший тип під час рантайму, ви можете використовувати `coerce` з Zod для конвертації рядків в тип, який ви хочете. Це викине помилку, якщо конвертація не вдасться. + +Додайте змінну до вашого `.env`: + +``` +SOME_NUMBER=123 +SOME_BOOLEAN=true +``` + +Потім, провалідуйте їх в `env.js`: + +```ts +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + SOME_NUMBER: z.coerce.number(), + SOME_BOOLEAN: z.coerce.boolean(), + }, + // ... + runtimeEnv: { + SOME_NUMBER: process.env.SOME_NUMBER, + SOME_BOOLEAN: process.env.SOME_BOOLEAN, + }, +}); +``` diff --git a/www/src/pages/uk/usage/first-steps.md b/www/src/pages/uk/usage/first-steps.md new file mode 100644 index 0000000000..93166bd242 --- /dev/null +++ b/www/src/pages/uk/usage/first-steps.md @@ -0,0 +1,54 @@ +--- +title: Перші кроки +description: Початок роботи з вашим новим T3 App +layout: ../../../layouts/docs.astro +lang: uk +--- + +Ви щойно створили новий додаток T3 і готові до роботи. Ось мінімум для запуску вашої програми. + +## База даних + +### MySQL, PostgreSQL + +Якщо ви обираєте MySQL або PostgreSQL як свою базу даних, ваше T3 додаток буде починатись з `start-database.sh` bash-скриптом, який може створити контейнер Docker з базою даних для локальної розробки. Якщо у вас вже є база даних, ви можете видалити цей файл і вказати свої облікові дані для бази даних в `.env`. На macOS ви також можете використовувати [DBngin](https://dbngin.com/), якщо ви не хочете використовувати Docker. + +### Prisma + +Якщо ваш додаток включає Prisma, переконайтеся, що ви запустили `npx prisma db push` з кореневої директорії вашої програми. Ця команда синхронізує схему Prisma з вашою базою даних і генерує типи TypeScript для Prisma Client на основі вашої схеми. Зверніть увагу, що вам потрібно [перезапустити сервер TypeScript](https://tinytip.co/tips/vscode-restart-ts/) після цього, щоб він міг виявити згенеровані типи. + +### Drizzle + +Якщо ваш додаток включає в себе Drizzle, перевірте `.env` файл для інструкцій щодо побудови вашої змінної середовища `DATABASE_URL`. Як тільки ваш файл середовища готовий, запустіть `pnpm db:push` (або еквівалент для інших менеджерів пакетів), щоб відправити вашу схему. + +## Аутентифікація + +Якщо ваш додаток включає NextAuth.js, ми починаємо з `DiscordProvider`. Це один з найпростіших провайдерів, пропонований NextAuth.js, однак він все ще вимагає певного початкового налаштування з вашого боку. + +Звичайно, якщо ви віддаєте перевагу використанню іншого провайдера автентифікації, ви також можете використати один із [багатьох провайдерів](https://next-auth.js.org/providers/), які пропонує NextAuth.js. + +1. Вам потрібен обліковий запис Discord, тому зареєструйтеся, якщо ще не зареєструвалися. +2. Перейдіть на https://discord.com/developers/applications і натисніть "New Application" у правому верхньому куті. Дайте вашому додатку ім'я та погодьтеся з Умовами використання. +3. Коли ви створите додаток, перейдіть до "Settings → OAuth2 → General". +4. Скопіюйте "Client ID" і додайте його у ваш `.env` як `DISCORD_CLIENT_ID`. +5. Натисніть "Reset Secret", скопіюйте новий secret і додайте його у ваш `.env` як `DISCORD_CLIENT_SECRET`. +6. Натисніть "Add Redirect" і введіть `http://localhost:3000/api/auth/callback/discord`. + - Для деплойменту в продакшені дотримуйтесь попередніх кроків для створення іншого додатка Discord, але цього разу замініть `http://localhost:3000` на URL, на який ви деплоїте. +7. Збережіть зміни. +8. Встановіть `NEXTAUTH_SECRET` у `.env`. У розробці будь-який рядок працюватиме, для продакшена див. примітка в `.env` про генерацію безпечного secret. + +Тепер у вас має бути можливість увійти в систему. + +## Сетап едітора + +Наступні розширення рекомендуються для оптимального досвіду розробки. Нижче наведені посилання на підтримку плагінів для редактора. + +- [Prisma Extension](https://www.prisma.io/docs/guides/development-environment/editor-setup) +- [Tailwind CSS IntelliSense Extension](https://tailwindcss.com/docs/editor-setup) +- [Prettier Extension](https://prettier.io/docs/en/editors.html) + +## Наступні кроки + +- Якщо ваш додаток включає tRPC, ознайомтеся з `src/pages/index.tsx` і `src/server/trpc/router/post.ts`, щоб дізнатися, як працюють запити tRPC. +- Подивіться на документацію `create-t3-app`, а також на документацію пакетів, які включає ваш додаток. +- Приєднуйтесь до нашого [Discord](https://t3.gg/discord) і поставте зірку на [GitHub](https://github.com/t3-oss/create-t3-app)! :) diff --git a/www/src/pages/uk/usage/index.astro b/www/src/pages/uk/usage/index.astro new file mode 100644 index 0000000000..cabae5a189 --- /dev/null +++ b/www/src/pages/uk/usage/index.astro @@ -0,0 +1,24 @@ +--- +import IndexPage from "../../../components/docs/indexPage.astro"; +import { SIDEBAR, type Frontmatter } from "../../../config"; +import { getLanguageFromURL } from "../../../languages"; +import Layout from "../../../layouts/docs.astro"; + +const frontmatter: Frontmatter = { + title: "Використання", + layout: "docs", + description: "Навчіться використовувати різні технології з T3 Stack.", +}; + +const lang = getLanguageFromURL(Astro.url.pathname); +const sidebarEntries = SIDEBAR[lang]["Usage"]!; +const files = await Astro.glob("./*.{md,mdx,astro}"); +--- + +<Layout frontmatter={frontmatter} headings={[]}> + <IndexPage + frontmatter={frontmatter} + sidebarEntries={sidebarEntries} + files={files} + /> +</Layout> diff --git a/www/src/pages/uk/usage/next-auth.mdx b/www/src/pages/uk/usage/next-auth.mdx new file mode 100644 index 0000000000..908a82c9d8 --- /dev/null +++ b/www/src/pages/uk/usage/next-auth.mdx @@ -0,0 +1,230 @@ +--- +title: NextAuth.js +description: Використання NextAuth.js +layout: ../../../layouts/docs.astro +lang: uk +isMdx: true +--- + +import Callout from "../../../components/docs/callout.tsx"; + +Коли ви хочете мати систему автентифікації у вашому додатку Next.js, NextAuth.js - чудове рішення, щоб не морочитися з реалізацією складної безпеки самостійно. Він має великий список провайдерів для швидкого додавання аутентифікації OAuth і надає адаптери для багатьох баз даних і ORM. + +## Провайдер контексту + +У точці входу вашого додатка ви побачите, що ваш додаток загорнутий у [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): + +```tsx:pages/_app.tsx +<SessionProvider session={session}>> + <Component {...pageProps} /> +</SessionProvider> +``` + +Цей провайдер контексту дає змогу вашому застосунку отримати доступ до даних сесії з будь-якого місця вашого застосунку, не передаючи їх як пропси: + +```tsx:pages/users/[id].tsx +import { useSession } from "next-auth/react"; + +const User = () => { + const { data: session } = useSession(); + + if (!session) { + // Handle unauthenticated state, e.g. render a SignIn component + return <SignIn />; + } + + return <p>Welcome {session.user.name}!</p>; +}; +``` + +## Отримання сесії на сервері + +Іноді вам може знадобитися запросити сесію на сервері. Щоб зробити це, попередньо отримайте сесію за допомогою функції-помічника `getServerAuthSession`, яку надає `create-t3-app`, і передайте її на клієнт за допомогою `getServerSideProps`: + +```tsx:pages/users/[id].tsx +import { getServerAuthSession } from "../server/auth"; +import { type GetServerSideProps } from "next"; + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + return { + props: { session }, + }; +}; + +const User = () => { + const { data: session } = useSession(); + // NOTE: `session` wont have a loading state since it's already prefetched on the server + + ... +} +``` + +## Включення `user.id` у сесію + +Create T3 App налаштований для використання [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) у конфігурації NextAuth.js для включення ID користувача в об'єкт`session`. + +```ts:pages/api/auth/[...nextauth].ts +callbacks: { + session({ session, user }) { + if (session.user) { + session.user.id = user.id; + } + return session; + }, + }, +``` + +Це пов'язано з файлом оголошення типів, щоб переконатися, що `user.id` типізовано під час доступу до об'єкта `session`. Детальніше про [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) у документації NextAuth.js. + +```ts:types/next-auth.d.ts +import { DefaultSession } from "next-auth"; + +declare module "next-auth" { + інтерфейс Session { + user? { + id: string; + } & DefaultSession["user"]; + } +} +``` + +Такий самий шаблон може бути використаний для додавання будь-яких інших даних в об'єкт `session`, наприклад, поля `role`, але **ним не слід зловживати для зберігання конфіденційних даних** на клієнті. + +## Використання з tRPC + +При використанні NextAuth.js з tRPC ви можете повторно створити використовувані, захищені процедури за допомогою [middleware](https://trpc.io/docs/v10/middlewares). Це дозволяє вам створювати процедури, які можуть бути доступні тільки автентифікованим користувачам. `create-t3-app` налаштовує все це для вас, дозволяючи вам легко отримувати доступ до сесії об’єкта в аутентифікованих процедурах. + +Це робиться в два кроки: + +1. Викликайте сесію із заголовків запиту за допомогою функції [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). Перевага використання `getServerSession` замість звичайного `getSession` полягає в тому, що це серверна функція і вона не викликає непотрібних викликів fetch. `create-t3-app` створює допоміжну функцію, яка абстрагує це конкретне API щоб вам не треба було імпортувати обидва ваших NextAuth.js варіанти так як і `getServerSession` функцію кожного разу коли вам треба отримати доступ до сесії. + +```ts:server/auth.ts +export const getServerAuthSession = async (ctx: { + req: GetServerSidePropsContext["req"]; + res: GetServerSidePropsContext["res"]; +}) => { + return await getServerSession(ctx.req, ctx.res, authOptions); +}; +``` + +Використовуючи цю допоміжну функцію, ми можемо отримати сесію та передати її в контексті tRPC: + +```ts:server/api/trpc.ts +import { getServerAuthSession } from "../auth"; + +export const createContext = async (opts: CreateNextContextOptions) => { + const { req, res } = opts; + const session = await getServerAuthSession({ req, res }); + return await createContextInner({ + session, + }); +}; +``` + +2. Створіть tRPC middleware, яке перевіряє, аутентифіковано чи користувач. Потім ми використовуємо middleware в `protectedProcedure`. Будь-який виклик процедур повинен бути автентифікований, інакше буде сгенерована помилка, яку можна правильно опрацювати на стороні клієнта. + +```ts:server/api/trpc.ts +export const protectedProcedure = t.procedure.use(({ ctx, next }) => { + if (!ctx.session || !ctx.session.user) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + return next({ + ctx: { + // infers the `session` as non-nullable + session: { ...ctx.session, user: ctx.session.user }, + }, + }); +}) +``` + +Об'єкт сесії - це легке, мінімальне представлення користувача і містить лише кілька полів. При використанні `protectedProcedures` у вас є доступ до ідентифікатора користувача, який можна використовувати для отримання більшої кількості даних з бази даних. + +```ts:server/api/routers/user.ts +const userRouter = router({ + me: protectedProcedure.query(async ({ ctx }) => { + const user = await prisma.user.findUnique({ + where: { + id: ctx.session.user.id, + }, + }); + return user; + }), +}); +``` + +## Використання з Prisma + +Щоб змусити NextAuth.js і Prisma працювати разом, необхідна велика кількість [початкових налаштувань](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` виконує все це для вас, і якщо ви виберете одночасно і Prisma, і NextAuth.js, ви отримаєте повністю працюючу систему аутентифікації з усіма попередньо вбудованими, необхідними моделями. Ми надаємо вашому сгенерованому додатку попередньо вбудований провайдер Discord OAuth, який ми вибрали тому, що з ним легше всього почати – просто введіть свої токени в `.env` і ви готові до роботи. Однак ви можете легко додати більше провайдерів, слідуючи [докуменації NextAuth.js](https://next-auth.js.org/providers/). Зверніть увагу, що деякі провайдери вимагають додаткових полів для додавання в певні моделі. Ми рекомендуємо вам прочитати документацію для провайдера, який ви хочете використовувати, щоб переконатися, що у вас є всі необхідні поля. + +### Додавання нових полів у ваші моделі + +Коли ви додаєте нові поля до будь-якої з моделей `User`, `Account`, `Session` або `VerificationToken` (у більшості випадків вам потрібно лише змінити модель `User`), вам потрібно мати на увазі, що [адаптер Prisma](https://next-auth.js.org/adapters/prisma) автоматично створює поля в цих моделях при реєстрації нових користувачів та вході до системи. Тому, додаючи нові поля до цих моделей, ви повинні надати значення за замовчуванням для них, оскільки адаптер не знає про них. + +Якщо, наприклад, ви хочете додати `role` у модель `User`, вам потрібно буде надати значення за замовчуванням для поля `role`. Це робиться шляхом додавання значення `@default` до поля `role` у моделі `User`: + +```diff:prisma/schema.prisma ++ enum Role { ++ USER ++ ADMIN ++ } + + model User { + ... ++ role Role @default(USER) + } +``` + +## Використання з Next.js middleware + +Використання NextAuth.js з Next.js middleware [вимагає використання стратегії сеансу JWT](https://next-auth.js.org/configuration/nextjs#caveats) для автентифікації. Це пов'язано з тим, що middleware може отримати доступ до сесійного cookie тільки в тому випадку, якщо це JWT. За замовчуванням, create-t3-app налаштований на використання **default** стратегії бази даних, у поєднанні з Prisma як адаптера бази даних. + +<Callout type="warning"> + Використання сесій баз даних - рекомендований підхід, й вам варто почитати про + JWT перед тим як переходити до стратегії JWT сесії, щоб уникнути будь-яких + проблем з безпекою. +</Callout> + +Після переходу до стратегії JWT сесії, переконайтеся, що ви оновили `session` колбек в `src/server/auth.ts`. + +Об'єкт `user` буде `undefined`. Замість цього, витягніть ID користувача з об'єкту `token`. + +Наприклад: + +```diff:server/auth.ts + export const authOptions: NextAuthOptions = { ++ session: { ++ strategy: "jwt", ++ }, + callbacks: { +- session: ({ session, user }) => ({ ++ session: ({ session, token }) => ({ + ...session, + user: { + ...session.user, +- id: user.id, ++ id: token.sub, + }, + }), + }, + } +``` + +## Налаштовуємо DiscordProvider за замовчуванням + +1. Перейдіть до розділу Applications у [Discord Developer Portal](https://discord.com/developers/applications) і натисніть "New Application" +2. У меню налаштувань перейдіть до "OAuth2 => General" + +- Скопіюйте Client ID і вставте його в `DISCORD_CLIENT_ID` у `.env`. +- Біля Client Secret натисніть "Reset Secret" і скопіюйте цей рядок у `DISCORD_CLIENT_SECRET` у `.env`. Будьте обережними, оскільки ви більше не зможете побачити цей secret, і скидання його призведе до того, що існуючий протермінується. +- Натисніть "Add Redirect" і вставте `<app url>/api/auth/callback/discord` (приклад для локальної розробки: <code class="break-all">http://localhost:3000/api/auth/callback/discord</code>) +- Збережіть зміни +- Можливо, але не рекомендується, використовувати один і той же додаток Discord для розробки та продакшену. Ви також можете розглянути [Mocking the Provider](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) під час розробки. + +## Корисні ресурси + +| Ресурс | Посилання | +| --------------------------------- | --------------------------------------- | +| Документація NextAuth.js | https://next-auth.js.org/ | +| NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | +| tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | diff --git a/www/src/pages/uk/usage/next-js.md b/www/src/pages/uk/usage/next-js.md new file mode 100644 index 0000000000..358d9adbec --- /dev/null +++ b/www/src/pages/uk/usage/next-js.md @@ -0,0 +1,37 @@ +--- +title: Next.js +description: Використання Next.js +layout: ../../../layouts/docs.astro +lang: uk +--- + +Next.js це бекенд фреймворк для ваших React додатків. + +<div class="embed"> +<iframe width="560" height="315" src="https://www.youtube.com/embed/W4UhNo3HAMw" title="Next.js is a backend framework" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> +</div> + +Подивіться [виступ Theo на Next.js Conf](https://www.youtube.com/watch?v=W4UhNo3HAMw) щоб отримати більш детальне розуміння того, що таке Next.js і як він працює. + +## Чому я повинен це використовувати? + +Ми любимо React. Він зробив розробку UI доступною у тому вигляді, в якому ми ніколи не могли собі уявити. Він також може вести розробників тернистими шляхами. Next.js пропонує злегка суб'єктивний, більш оптимізований підхід до створення додатків з використанням React. Від маршрутизації до визначення API до візуалізації зображень ми довіряємо Next.js, щоб вести розробників до правильних рішень. + +Поєднуючи Next.js з [Vercel](https://vercel.com/), ви можете розробляти та деплоїти веб-програми легше, ніж будь-коли. Їх надзвичайно щедрий безкоштовний тариф та супер інтуїтивний інтерфейс надають рішення в один клік для деплойменту вашого сайту (Ми ❤️ Vercel) + +## Get Static/Server Props + +Ключові особливості Next.js – це можливості отримання даних. Ми наполегливо рекомендуємо прочитати [офіційну документацію](https://nextjs.org/docs/basic-features/data-fetching), щоб зрозуміти, як використовувати кожен метод і чим вони відрізняються. `getServerSideProps` зазвичай не рекомендується, якщо немає вагомої причини, тому що це блокуючий виклик і він уповільнить ваш сайт. [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) - це відмінна альтернатива `getServerSideProps`, коли дані динамічні та можуть бути отримані поступово. + +Якщо ви все одно хочете використовувати цю функцію, перегляньте ці посилання: [Advanced tRPC - Callers, functions, and gSSP](https://www.youtube.com/watch?v=G2ZzmgShHgQ) та [SSG-Helpers](https://trpc.io/docs/v9/ssg-helpers) + +## Корисні ресурси + +| Ресурс | Посилання | +| ------------------------- | ---------------------------------- | +| Next.js Документація | https://nextjs.org/docs | +| Next.js GitHub | https://github.com/vercel/next.js | +| Next.js Блог | https://nextjs.org/blog | +| Next.js Discord | https://nextjs.org/discord | +| Next.js Twitter | https://twitter.com/nextjs | +| Vercel/Next.js на YouTube | https://www.youtube.com/c/VercelHQ | diff --git a/www/src/pages/uk/usage/prisma.md b/www/src/pages/uk/usage/prisma.md new file mode 100644 index 0000000000..b74fd0293d --- /dev/null +++ b/www/src/pages/uk/usage/prisma.md @@ -0,0 +1,78 @@ +--- +title: Prisma +description: Використання Prisma +layout: ../../../layouts/docs.astro +lang: uk +--- + +Prisma це ORM для TypeScript, який дозволяє визначати схему та моделі бази даних у файлі `schema.prisma`, а потім генерувати клієнт, який забезпечує типобезпеку та може використовуватись для взаємодії з базою даних з вашого бекенда. + +## Prisma Client + +Розташований в `/server/db/client.ts`, Prisma Client ініціалізується як глобальна змінна (як рекомендовано [кращими практиками](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) команди Prisma) та експортується для використання у ваших API маршрутах. Ми включаємо Prisma Client у [Context](/uk/usage/trpc#-serverapitrpcts) за замовчуванням і рекомендуємо використовувати його замість окремого імпорту у кожному файлі. + +## Схема + +Ви знайдете файл схеми Prisma в `/prisma/schema.prisma`. Цей файл використовується для визначення схеми та моделей баз даних, а також для генерації Prisma Client. + +### З NextAuth.js + +Коли ви вибираєте NextAuth.js у поєднанні з Prisma, файл схеми генерується і налаштовується для вас з рекомендованими значеннями для моделей `User`, `Session`, `Account` та `VerificationToken`, згідно з [документацією NextAuth.js](https://next-auth.js.org/adapters/prisma). + +## База даних за замовчуванням + +База даних за замовчуванням - це база даних SQLite, яка відмінно підходить для розробки та швидкого створення proof-of-concept, але не рекомендується для використання у продакшені. Ви можете змінити базу даних, використовуючи 'provider' в блоці 'datasource' на 'postgresql' або 'mysql', а потім оновити рядок підключення до змінних середовища, щоб вказати на вашу базу даних. + +## Заповнення (seeding) бази даних + +[Заповнення (seeding) вашої бази даних](https://www.prisma.io/docs/guides/database/seed-database) - це чудовий спосіб швидко заповнити вашу базу даних тестовими даними, щоб допомогти вам розпочати. Щоб налаштувати заповнення, вам потрібно створити файл `seed.ts` у каталозі `/prisma` і потім додати скрипт `seed` у файл `package.json`. Вам також знадобиться певний TypeScript-раннер, який може виконати скрипт заповнення. Ми рекомендуємо [tsx](https://github.com/esbuild-kit/tsx), який є дуже ефективним TypeScript-раннером, який використовує esbuild і не вимагає будь-якої конфігурації ESM, але `ts-node` або інші раннери також працюватимуть. + +```jsonc:package.json +{ + "scripts": { + "db-seed": "NODE_ENV=development prisma db seed" + }, + "prisma": { + "seed": "tsx prisma/seed.ts" + } +} +``` + +```ts:prisma/seed.ts +import { prisma } from "../src/server/db"; + +async function main() { + const id = "cl9ebqhxk00003b600tymydho"; + await prisma.example.upsert({ + where: { + id, + }, + create: { + id, + }, + update: {}, + }); +} + +main() + .then(async () => { + await prisma.$disconnect(); + }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }); +``` + +Потім, просто запустіть `pnpm db-seed` (або `npm`/`yarn`), щоб заповнити вашу базу даних. + +## Корисні ресурси + +| Ресурс | Посилання | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| Документація Prisma | https://www.prisma.io/ | +| Prisma GitHub | https://github.com/prisma/ | +| Prisma Migrate Playground | https://playground.prisma.io/guides | +| NextAuth.JS Адаптер для Prisma | https://next-auth.js.org/adapters/ | +| Гайд з підключення PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | diff --git a/www/src/pages/uk/usage/tailwind.md b/www/src/pages/uk/usage/tailwind.md new file mode 100644 index 0000000000..25bf66fc78 --- /dev/null +++ b/www/src/pages/uk/usage/tailwind.md @@ -0,0 +1,96 @@ +--- +title: Tailwind CSS +description: Використання Tailwind CSS +layout: ../../../layouts/docs.astro +lang: uk +--- + +## Що таке Tailwind CSS? + +Tailwind CSS - це невеликий [utility first](https://tailwindcss.com/docs/utility-first) CSS фреймворк для створення власного дизайну, без перемикання контексту, який потрібний для звичайного CSS. Це чисто CSS фреймворк і він не надає ніяких попередньо зібраних компонентів або логіки, і надає [принципово інший набір переваг](https://www.youtube.com/watch?v=CQuTF-bkOgc) у порівнянні з бібліотекою компонентів, такою як наприклад Material UI. + +Він робить CSS неймовірно легким і швидким для написання, як показано в наступному прикладі: + +Старий CSS: + +1. Напишіть CSS, часто в окремому файлі + +```css +.my-class { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #fff; + border: 1px solid #e2e8f0; + border-radius: 0.25rem; + padding: 1rem; +} +``` + +2. Імпортуйте CSS у ваш компонент + +```jsx +import "./my-class.css"; +``` + +3. Додайте клас у ваш HTML + +```html +<div class="my-class">...</div> +``` + +Еквівалент у Tailwind: + +1. Просто напишіть класи у вашому HTML + +```html +<div + class="flex flex-col items-center justify-center rounded border border-gray-200 bg-white p-4" +> + ... +</div> +``` + +Використовуючи разом із React компонентами, він є надзвичайно потужним для швидкого створення UI. + +Tailwind CSS містить вбудовану красиву систему дизайну, яка поставляється від початку з ретельно вибраною палітрою кольорів, розмірами для стилів, таких як ширина/висота і відступи для єдиного дизайну, а також точками переривання для створення адаптивних макетів. Ця система дизайну може бути налаштована та розширена для створення точного набору стилів, які потрібні вашому проекту. + +<div class="embed"> +<iframe width="560" height="315" src="https://www.youtube.com/embed/T-Zv73yZ_QI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> +</div> + +Tru Narla краще відома як [mewtru](https://twitter.com/trunarla) дала приголомшливу лекцію про [створення системи дизайну з використанням Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). + +## Використання + +Переконайтеся, що у вас є плагіни редактора для Tailwind, щоб поліпшити ваш досвід написання Tailwind. + +### Розширення та плагіни + +- [Розширення для VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) +- [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) +- [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) + +### Форматування + +Класи Tailwind CSS можуть легко стати трохи безладними, тому форматтер для класів є необхідним. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) сортує класи у [рекомендованому порядку](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted), щоб класи відповідали зібраному css-пакету. При виборі Tailwind у CLI ми встановимо та налаштуємо це для вас. + +### Додавання класів в залежності від умов + +Додавання класів в залежності від умов з використанням тернарних операторів може стати дуже безладним і важким для читання. Ці пакети допомагають організувати ваші класи, використовуючи деяку логіку з умовами. + +- [clsx](https://github.com/lukeed/clsx) +- [classnames](https://github.com/JedWatson/classnames) + +## Корисні ресурси + +| Ресурс | Посилання | +| ----------------------------- | -------------------------------------------------------- | +| Документація Tailwind | https://tailwindcss.com/docs/editor-setup/ | +| Шпаргалка по Tailwind | https://nerdcave.com/tailwind-cheat-sheet/ | +| awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | +| Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | +| Сервер Tailwind в Discord | https://tailwindcss.com/discord/ | +| Канал TailwindLabs на Youtube | https://www.youtube.com/tailwindlabs/ | +| Tailwind Playground | https://play.tailwindcss.com/ | diff --git a/www/src/pages/uk/usage/trpc.md b/www/src/pages/uk/usage/trpc.md new file mode 100644 index 0000000000..a241fe3d18 --- /dev/null +++ b/www/src/pages/uk/usage/trpc.md @@ -0,0 +1,382 @@ +--- +title: tRPC +description: Використання tRPC +layout: ../../../layouts/docs.astro +lang: uk +--- + +tRPC дозволяє нам писати типобезпечні API від кінця до кінця (end-to-end) без будь-якої генерації коду чи подовження рантайму. Він використовує чудовий висновок TypeScript для виведення визначень типів вашого API-маршрутизатора і дозволяє викликати ваші API-процедури з інтерфейсу з повною безпекою типів і автоматичним завершенням. Використовуючи tRPC, ваш фронтенд і бекенд почуваються ближче один до одного, ніж будь-коли, забезпечуючи винятковий досвід розробника. + +<blockquote className="w-full relative border-l-4 italic bg-t3-purple-200 dark:text-t3-purple-50 text-zinc-900 dark:bg-t3-purple-300/20 p-2 rounded-md text-sm my-3 border-neutral-500 quote"> + <div className="relative w-fit flex items-center justify-center p-1"> + <p className="mb-4 text-lg"> + <span aria-hidden="true">"</span>I built tRPC to allow people to move faster by removing the need of a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.<span aria-hidden="true">"</span> + </p> + </div> + <cite className="flex items-center justify-end pr-4 pb-2"> + <img + alt="Avatar of @alexdotjs" + className="w-12 rounded-full bg-neutral-500 [margin-inline-end:16px]" + src="https://avatars.githubusercontent.com/u/459267?v=4" + /> + <div className="flex flex-col items-start not-italic"> + <span className=" text-sm font-semibold">Alex - creator of tRPC</span> + <a + href="https://twitter.com/alexdotjs" + target="_blank" + rel="noopener noreferrer" + className="text-sm" + > + @alexdotjs + </a> + </div> + </cite> +</blockquote> + +## Як я можу використовувати tRPC? + +<div class="embed"> +<iframe width="560" height="315" src="https://www.youtube.com/embed/2LYM8gf184U" title="Making typesafe APIs easy with tRPC" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> +</div> + +tRPC контриб'ютор [trashh_dev](https://twitter.com/trashh_dev) [виступив на Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) з розповіддю про tRPC. Ми рекомендуємо вам подивитися, якщо ви ще не зробили це. + +З tRPC ви пишете функції TypeScript на бекенді, а потім викликаєте їх із фронтенду. Проста процедура tRPC може виглядати так: + +```ts:server/api/routers/user.ts +const userRouter = createTRPCRouter({ + getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { + return ctx.prisma.user.findFirst({ + where: { + id: input, + }, + }); + }), +}); +``` + +Це процедура tRPC (еквівалент route handler-а у традиційному бекенді), яка спочатку перевіряє вхідні дані за допомогою Zod (який є тією самою бібліотекою перевірки, яку ми використовуємо для [змінних середовища](./env-variables)) – у цьому випадку ми переконуємось , що вхідні дані є рядком. Якщо вхідні дані не є рядком, він надішле інформативну помилку. + +Після вхідних даних ми застосовуємо функцію, яка може бути або [запитом](https://trpc.io/docs/v10/react-queries), [мутацією](https://trpc.io/docs/v10/react-mutations) або [підпискою](https://trpc.io/docs/v10/subscriptions). У нашому прикладі resolver викликає нашу базу даних за допомогою клієнта [prisma](./prisma) і повертає користувача, у якого `id` збігається з тим, що ми передали. + +Ви визначаєте свої процедури в `routers` які є колекцією пов'язаних процедур із спільним ім'ям. У вас може бути один роутер для користувачів, один для постів і ще один для повідомлень. Ці роутери потім можна об'єднати в єдиний централізований `appRouter`: + +```ts:server/api/root.ts +const appRouter = createTRPCRouter({ + users: userRouter, + posts: postRouter, + messages: messageRouter, +}); + +export type AppRouter = typeof appRouter; +``` + +Зверніть увагу, що нам потрібно експортувати тільки типи наших роутерів, що означає, що ми ніколи не імпортуємо серверний код на клієнта. + +Тепер давайте викличемо процедуру на нашому фронтенді. tRPC надає обгортку для `@tanstack/react-query`, яка дозволяє використовувати всі можливості хуків (hooks), які він надає, але з додатковою перевагою того, що ваші виклики API типізовані та інферовані (inferred). Ми можемо викликати наші процедури з фронтенду так: + +```tsx:pages/users/[id].tsx +import { useRouter } from "next/router"; +import { api } from "../../utils/api"; + +const UserPage = () => { + const { query } = useRouter(); + const userQuery = api.users.getById.useQuery(query.id); + + return ( + <div> + <h1>{userQuery.data?.name}</h1> + </div> + ); +}; +``` + +Ви відразу помітите, наскільки хороші автодоповнення та типобезпека. Як тільки ви почнете писати `api.`, ваші роутери з'являться в автодоповненні, і коли ви оберете роутер, його процедури також з'являться. Ви також отримаєте помилку TypeScript, якщо ваше введення не відповідає валідатору, який ви визначили на бекенді. + +## Обробка помилок + +По дефолту, `create-t3-app` створює [error formatter](https://trpc.io/docs/error-formatting) який дозволяє вам виводити ваші Zod помилки якщо ви отримуєте помилки валідації на бекенді. + +Приклад використання: + +```tsx +function MyComponent() { + const { mutate, error } = api.post.create.useMutation(); + + return ( + <form onSubmit={(e) => { + e.preventDefault(); + const formData = new FormData(e.currentTarget); + mutate({ title: formData.get('title') }); + }}> + <input name="title" /> + {error?.data?.zodError?.fieldErrors.title && ( + {/** `mutate` returned with an error on the `title` */} + <span className="mb-8 text-red-500"> + {error.data.zodError.fieldErrors.title} + </span> + )} + + ... + </form> + ); +} +``` + +## Файли + +tRPC вимагає багато шаблонного коду, який `create-t3-app` налаштовує для вас. Давайте розглянемо файли, що генеруються: + +### 📄 `pages/api/trpc/[trpc].ts` + +Цей файл є точкою входу для вашого API та експортує ваш tRPC роутер. Зазвичай, вам не доведеться чіпати цей файл, але якщо вам потрібно, наприклад, включити CORS middleware або щось подібне, корисно знати, що експортований `createNextApiHandler` - це [Next.js API handler](https://nextjs.org/docs/api-routes/introduction) який приймає [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) та [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) об'єкт. Це означає, що ви можете обернути `createNextApiHandler` у будь-який middleware, який вам потрібен. Дивись нижче для [прикладу](#enabling-cors) додавання CORS. + +### 📄 `server/api/trpc.ts` + +Цей файл поділено на дві частини: створення контексту та ініціалізація tRPC. + +1. Ми визначаємо контекст, який передається вашим tRPC процедурам. Контекст - це дані, до яких у вас є доступ у всіх ваших процедурах tRPC, і це відмінне місце, щоб помістити речі, такі як з'єднання з базою даних, інформація про аутентифікацію і т.д. У create-t3-app ми використовуємо дві функції, щоб увімкнути використання підмножини контексту, коли ми не маємо доступу до об'єкта запиту. + +- `createInnerTRPCContext`: Це те місце, де ви визначаєте контекст, який не залежить від запиту, наприклад, ваше з'єднання з базою даних. Ви можете використовувати цю функцію для [інтеграційного тестування](#sample-integration-test) або [ssg-помічників](https://trpc.io/docs/v10/ssg-helpers), де у вас немає об'єкта запиту. + +- `createTRPCContext`: Тут ви визначаєте контекст, який залежить від запиту: наприклад, сесія користувача. Ви запитуєте сесію за допомогою об'єкта `opts.req`, а потім передаєте сесію у функцію `createInnerTRPCContext` для створення остаточного контексту. + +2. Ми ініціалізуємо tRPC і визначаємо повторно використовані [процедури](https://trpc.io/docs/v10/procedures) та [посередники](https://trpc.io/docs/v10/middlewares). За угодою, ви не повинні експортувати весь об'єкт `t`, а натомість створювати повторно використовувані процедури та посередники та експортувати їх. + +Ви помітите, що ми використовуємо `superjson` як [перетворювач даних](https://trpc.io/docs/v10/data-transformers). Це дозволяє зберігати типи даних, коли вони досягають клієнта, тому якщо ви, наприклад, відправляєте об'єкт `Date`, клієнт поверне `Date`, а не рядок, що є нагодою для більшості API. + +### 📄 `server/api/routers/*.ts` + +Це те місце, де ви визначаєте маршрути та процедури вашого API. Ви [створюєте окремі маршрутизатори](https://trpc.io/docs/v10/router) для пов'язаних процедур. + +### 📄 `server/api/root.ts` + +Тут ми [об'єднуємо](https://trpc.io/docs/v10/merging-routers) всі під-маршрутизатори, визначені в `routers/**` в єдиний додатковий маршрутизатор. + +### 📄 `utils/api.ts` + +Це точка входу для tRPC на фронтенді. Тут ви імпортуєте **визначення типів** маршрутизатора та створюєте клієнт tRPC разом із хуками react-query. Оскільки ми включили `superjson` як перетворювач даних на бекенді, нам також потрібно включити його на фронтенді. Це тому що серіалізовані дані з бэкенда десеріалізуються на фронтенді. + +Тут ви визначите свої tRPC [посилання](https://trpc.io/docs/v10/links), які визначають потік запитів від клієнта до сервера. Ми використовуємо "default" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink), який дозволяє [групувати запити](https://cloud.google.com/compute/docs/api/how-tos/batch), а також [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink), що виводить корисні журнали запитів під час розробки. + +Зрештою, ми експортуємо [допоміжний тип](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type), який ви можете використовувати для виведення типів на фронтенді. + +<div class="embed"> +<iframe width="560" height="315" src="https://www.youtube.com/embed/x4mu-jOiA0Q" title="How tRPC really works" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> +</div> + +Create T3 App контриб’ютор [Christopher Ehrlich](https://twitter.com/ccccjjjjeeee) зробив [відео про потоки даних у tRPC](https://www.youtube.com/watch?v=x4mu-jOiA0Q). Це відео рекомендовано якщо ви використовували tRPC, але все ще відчуваєте, що трохи не розумієте як воно працює. + +## Як я можу викликати свій API ззовні? + +У звичайних API, ви можете викликати ваші кінцеві точки (endpoints) використовуючи будь-який HTTP клієнт, як `curl`, `Postman`, `fetch` або прямо з вашого браузера. З tRPC, це працює трохи інакше. Якщо ви хочете викликати ваші процедури без клієнта tRPC, є два рекомендовані способи зробити це: + +### Розкрийте одну процедуру зовні + +Якщо ви бажаєте розкрити вашу процедуру зовні, вам варто шукати [server side calls](https://trpc.io/docs/v10/server-side-calls). Це дозволить вам створити звичайну кінцеву точку (endpoint) API Next.js, але перевикористовувати частину резолвера вашої процедури tRPC. + +```ts:pages/api/users/[id].ts +import { type NextApiRequest, type NextApiResponse } from "next"; +import { appRouter, createCaller } from "../../../server/api/root"; +import { createTRPCContext } from "../../../server/api/trpc"; + +const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { + // Create context and caller + const ctx = await createTRPCContext({ req, res }); + const caller = createCaller(ctx); + try { + const { id } = req.query; + const user = await caller.user.getById(id); + res.status(200).json(user); + } catch (cause) { + if (cause instanceof TRPCError) { + // An error from tRPC occurred + const httpCode = getHTTPStatusCodeFromError(cause); + return res.status(httpCode).json(cause); + } + // Another error occurred + console.error(cause); + res.status(500).json({ message: "Internal server error" }); + } +}; + +export default userByIdHandler; +``` + +### Розкрийте кожну процедуру зовнішньо + +Якщо ви хочете розкрити будь-яку процедуру ззовні, познайомтеся з плагіном спільноти [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Надаючи додаткові метаданні вашим процедурам, ви можете створити відповідний REST API з вашого маршрутизатора tRPC. + +### Це всього лише HTTP-запити + +tRPC взаємодіє через HTTP, тому також можна викликати ваші процедури tRPC за допомогою "звичайних" HTTP-запитів. Однак синтаксис може бути неприйнятним із-за [протоколу RPC](https://trpc.io/docs/v10/rpc), який використовує tRPC. Якщо вам цікаво, ви можете перевірити, як виглядають запити та відповіді tRPC у вашій network вкладці веб-браузера, але ми рекомендуємо робити це лише в якості навчальної вправи і притримуватися одного з рішень, описаних вище. + +## Порівняння з кінцевою точкою (enpoint) Next.js API + +Давайте порівняємо кінцеву точку Next.js API з процедурою tRPC. Припустимо, ми хочемо отримати об'єкт користувача з нашої бази даних і повернути його на фронтенд. Ми могли б написати кінцеву точку Next.js API таким чином: + +```ts:pages/api/users/[id].ts +import { type NextApiRequest, type NextApiResponse } from "next"; +import { prisma } from "../../../server/db"; + +const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method !== "GET") { + return res.status(405).end(); + } + + const { id } = req.query; + + if (!id || typeof id !== "string") { + return res.status(400).json({ error: "Invalid id" }); + } + + const examples = await prisma.example.findFirst({ + where: { + id, + }, + }); + + res.status(200).json(examples); +}; + +export default userByIdHandler; +``` + +```ts:pages/users/[id].tsx +import { useState, useEffect } from "react"; +import { useRouter } from "next/router"; + +const UserPage = () => { + const router = useRouter(); + const { id } = router.query; + + const [user, setUser] = useState(null); + useEffect(() => { + fetch(`/api/user/${id}`) + .then((res) => res.json()) + .then((data) => setUser(data)); + }, [id]); +}; +``` + +Порівняйте це з прикладом tRPC вище і ви побачите деякі переваги tRPC: + +- Замість того, щоб вказувати URL для кожного маршруту, який може стати незручним для дебагінгу, якщо ви перемістите щось, ваш весь маршрутизатор - це об'єкт з автозаповненням. +- Вам не потрібно перевіряти, який метод HTTP був використаний. +- Вам не потрібно перевіряти, що запит або тіло запиту містять правильні дані у процедурі, тому що Zod подбає про це. +- Замість створення відповіді, ви можете викидати помилки та повертати значення або об'єкт, як у будь-якій іншій функції TypeScript. +- Викликаючи процедуру на фронтенді, ви отримуєте автозаповнення та перевірку типів. + +## Корисні сніпети + +Тут наведені деякі сніпети, які можуть стати в нагоді. + +### Включення CORS + +Якщо вам потрібно використовувати ваш API з іншого домену, наприклад в монорепозиторії, який включає додаток React Native, вам може знадобитися включити CORS: + +```ts:pages/api/trpc/[trpc].ts +import { type NextApiRequest, type NextApiResponse } from "next"; +import { createNextApiHandler } from "@trpc/server/adapters/next"; +import { appRouter } from "~/server/api/root"; +import { createTRPCContext } from "~/server/api/trpc"; +import cors from "nextjs-cors"; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + // Enable cors + await cors(req, res); + + // Create and call the tRPC handler + return createNextApiHandler({ + router: appRouter, + createContext: createTRPCContext, + })(req, res); +}; + +export default handler; +``` + +### Оптимістичні оновлення + +Оптимістичне оновлення - це коли ми оновлюємо інтерфейс користувача до того, як API-запит завершиться. Це надає користувачеві кращий досвід, тому що він не повинен чекати завершення API-запиту, перш ніж інтерфейс користувача відобразить результат його дії. Однак програми, які цінують коректність даних, повинні уникати оптимістичних оновлень, оскільки вони не є «вірним» уявленням стану бекенда. Ви можете прочитати більше у [документації React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). + +```tsx +const MyComponent = () => { + const listPostQuery = api.post.list.useQuery(); + + const utils = api.useContext(); + const postCreate = api.post.create.useMutation({ + async onMutate(newPost) { + // Cancel outgoing fetches (so they don't overwrite our optimistic update) + await utils.post.list.cancel(); + + // Get the data from the queryCache + const prevData = utils.post.list.getData(); + + // Optimistically update the data with our new post + utils.post.list.setData(undefined, (old) => [...old, newPost]); + + // Return the previous data so we can revert if something goes wrong + return { prevData }; + }, + onError(err, newPost, ctx) { + // If the mutation fails, use the context-value from onMutate + utils.post.list.setData(undefined, ctx.prevData); + }, + onSettled() { + // Sync with server once mutation has settled + utils.post.list.invalidate(); + }, + }); +}; +``` + +### Простий Інтеграційний Тест + +Тут приведено простий інтеграційний тест, який використовує [Vitest](https://vitest.dev), щоб перевірити, що ваш маршрутизатор tRPC працює належним чином, парсер вхідних даних виводить правильний тип і дані, що повертаються, відповідають очікуваному результату. + +```ts +import { type inferProcedureInput } from "@trpc/server"; +import { expect, test } from "vitest"; + +import { appRouter, type AppRouter } from "~/server/api/root"; +import { createInnerTRPCContext } from "~/server/api/trpc"; + +test("example router", async () => { + const ctx = await createInnerTRPCContext({ session: null }); + const caller = appRouter.createCaller(ctx); + + type Input = inferProcedureInput<AppRouter["example"]["hello"]>; + const input: Input = { + text: "test", + }; + + const example = await caller.example.hello(input); + + expect(example).toMatchObject({ greeting: "Hello test" }); +}); +``` + +Якшо ваша процедура захищена, ви можете передати замоканий об'єкт `session` при створенні контексту: + +```ts +test("protected example router", async () => { + const ctx = await createInnerTRPCContext({ + session: { + user: { id: "123", name: "John Doe" }, + expires: "1", + }, + }); + const caller = appRouter.createCaller(ctx); + + // ... +}); +``` + +## Корисні ресурси + +| Ресурс | Посилання | +| ------------------------------------ | ------------------------------------------------------- | +| Документація tRPC | https://www.trpc.io | +| Декілька прикладів використання tRPC | https://github.com/trpc/trpc/tree/next/examples | +| Документація React Query | https://tanstack.com/query/v4/docs/adapters/react-query | diff --git a/www/src/pages/uk/usage/typescript.md b/www/src/pages/uk/usage/typescript.md new file mode 100644 index 0000000000..b9693d5a64 --- /dev/null +++ b/www/src/pages/uk/usage/typescript.md @@ -0,0 +1,67 @@ +--- +title: TypeScript +description: Використання TypeScript +layout: ../../../layouts/docs.astro +lang: uk +--- + +<blockquote className="w-full relative border-l-4 italic bg-t3-purple-200 dark:text-t3-purple-50 text-zinc-900 dark:bg-t3-purple-300/20 p-2 rounded-md text-sm my-3 border-neutral-500 quote"> + <div className="relative w-fit flex items-center justify-center p-1"> + <p className="mb-4 text-lg"> + <span aria-hidden="true">"</span>Build safety nets, not guard rails<span aria-hidden="true">"</span> + </p> + </div> + <cite className="flex items-center justify-end pr-4 pb-2"> + <img + alt="Avatar of @t3dotgg" + className="w-12 rounded-full bg-neutral-500 [margin-inline-end:16px]" + src="/images/theo_300x300.webp" + /> + <div className="flex flex-col items-start not-italic"> + <span className=" text-sm font-semibold">Theo - creator of the T3 Stack</span> + <a + href="https://twitter.com/t3dotgg" + target="_blank" + rel="noopener noreferrer" + className="text-sm" + > + @t3dotgg + </a> + </div> + </cite> +</blockquote> + +Незважаючи на те новачок ви або досвідчений розробник, ми вважаємо, що TypeScript - це маст-хев. Спочатку він може виглядати гнітюче, але, як і багато інших інструментів, багато розробників не повертаються назад після того, як почали його використовувати. + +Він надає зворотний зв'язок в режимі реального часу при написанні коду, визначаючи очікувані типи даних, і надає корисні підказки в редакторі коду або кричить червоними хвилястими лініями, якщо ви намагаєтеся отримати доступ до властивості, якої не існує, або намагаєтеся передати значення неправильного типу, яке в іншому випадку довелося б налагоджувати далі по лінії. + +Це інструмент, який, мабуть, забезпечує найбільшу продуктивність розробникам; він надає документацію для коду, який ви пишете або використовуєте безпосередньо у вашому редакторі та має миттєвий зворотний зв'язок, коли ви неминуче робите помилки, що абсолютно безцінно. + +## Виведення типів (Type Inference) + +Поки багато нових розробників на TypeScript стурбовані написанням TypeScript, багато з його переваг насправді не вимагають від вас зміни вашого коду взагалі, зокрема виведення типів. Висновок типів означає, що якщо щось типізовано, цей тип слідуватиме за ним протягом потоку програми без необхідності повторного оголошення в інших місцях. Це означає, що, наприклад, після того, як ви визначили типи аргументів, які приймає функція, решта функції зазвичай буде безпечною щодо типів без необхідності введення будь-якого додаткового коду, специфічного для TypeScript. Розробники бібліотек витрачають величезну кількість часу на підтримку типів для своїх бібліотек, що означає, що ми, як розробники додатків, можемо отримати вигоду від виведення типів та вбудованої документації у вашому редакторі коду, який ці типи надають. + +<div class="embed"> +<iframe width="560" height="315" src="https://www.youtube.com/embed/RmGHnYUqQ4k" title="You might be using Typescript wrong" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> +</div> + +Перегляньте відео Theo про те, що [ви, можливо, використовуєте TypeScript неправильно](https://www.youtube.com/watch?v=RmGHnYUqQ4k). + +## Потужні застосування виведення типів + +### Zod + +[Zod](https://github.com/colinhacks/zod) - це бібліотека перевірки схем, побудована поверх TypeScript. Напишіть схему, яка є єдиним джерелом істини для ваших даних, і Zod гарантує, що ваші дані будуть дійсними у всьому додатку, навіть поза межами мережі та зовнішніх API. + +### Tanstack Query + +[Tanstack Query](https://tanstack.com/query/v4/) надає вам декларативні, завжди актуальні автоматично керовані запити та мутації, які безпосередньо покращують як Developer, так і User Experience. + +## Корисні ресурси + +| Ресурс | Посилання | +| ---------------------------------------------------------------- | ----------------------------------------------------------------- | +| Посібник з TypeScript | https://www.typescriptlang.org/docs/handbook/ | +| Гайд по TypeScript для новачків | https://github.com/total-typescript/beginners-typescript-tutorial | +| Type Challenges | https://github.com/type-challenges/type-challenges | +| Канал Родні Маллена зі світу TypeScript (Matt Pocock) на YouTube | https://www.youtube.com/c/MattPocockUk/videos | diff --git a/www/src/pages/uk/why.md b/www/src/pages/uk/why.md new file mode 100644 index 0000000000..0cb7e47772 --- /dev/null +++ b/www/src/pages/uk/why.md @@ -0,0 +1,50 @@ +--- +title: Чому CT3A? +description: Чому вам слід вибрати Create T3 App для наступного проекту? +layout: ../../layouts/docs.astro +lang: uk +--- + +Ми почали Create T3 App, тому що [Theo](https://twitter.com/t3dotgg) відмовився робити шаблон своїх улюблених технологій. Натхненний create-next-app, [Astro's CLI](https://astro.build) та загальною любов'ю до типобезпеки, команда Create T3 App працювала, щоб створити кращу можливу відправну точку для нових проектів T3 Stack. + +Якщо ви зацікавлені у використанні Next.js у типобезпечному варіанті, це те, з чого потрібно почати. Якщо ви цікавитеся будь-якими з конкретних технологічних рішень, які ми зробили, читайте далі :) + +## Чому TypeScript? + +JavaScript складний. Навіщо додавати ще більше правил? + +Ми цілком впевнені, що досвід TypeScript дозволяє вам бути кращим розробником. Він надає зворотний зв'язок під час написання коду, визначаючи очікувані типи даних, і, або допомгає корисним автозаповненням у вашому редакторі, або кричить на вас червоними хвилястими лініями, якщо ви намагаєтеся отримати доступ до властивості, якої не існує, або намагаєтеся передати значення неправильного типу, що в іншому випадку довелося б налагоджувати в подальшому. Незалежно від того, новачок ви у веб-розробці або досвідчений професіонал, "суворість" TypeScript забезпечує менш дратівливий, більш послідовний досвід, ніж ванільний JS. + +Типобезпека робить вас швидше. Якщо ви ще не переконані, ви [можете використовувати TypeScript неправильно...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) + +## Чому Next.js? + +Ми любимо React. Він зробив розробку UI доступною у тому вигляді, в якому ми ніколи не могли собі уявити. Він також може вести розробників тернистими шляхами. + +Next.js пропонує злегка суб'єктивний, більш оптимізований підхід до створення додатків з використанням React. Від маршрутизації до визначення API до візуалізації зображень ми довіряємо Next.js, щоб вести розробників до правильних рішень. + +## Чому tRPC/Prisma/Tailwind/и т.д.? + +Хоча ми віримо, що все має бути якнайлегше, ми знаходимо, що ці частини використовуються в кожному проекті, складнішому ніж "лендінг" (напр. проєкти зі складнішими технологіями і логікою). `create-t3-app` виконує чудову роботу, дозволяючи вам вибрати ті частини, які вам потрібні. + +### tRPC + +tRPC надає всі переваги GraphQL, такі як гладка та безперервна розробка клієнта проти типобезпечного сервера без будь-якого бойлерплейту. Це розумний аб'юз TypeScript, яке забезпечує неймовірний досвід розробки. + +### Prisma + +Prisma для SQL це те й що TypeScript для JS. Вона створила досвід розробки, якого раніше не було. Створюючи типи з схеми користувача, сумісної з [кількома базами даних](https://www.prisma.io/docs/concepts/database-connectors), Prisma гарантує безпеку типів від початку до кінця від вашої бази даних до вашої програми. + +Prisma надає цілий [набір інструментів](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows), що полегшують щоденну взаємодію з вашою базою даних. Зокрема, Prisma Client відповідає за запити та робить SQL настільки простим, що ви ледве помітите, що його використовуєте, а Prisma Studio – це зручний GUI для вашої бази даних, який дозволяє швидко читати та маніпулювати даними без необхідності писати код. + +### Tailwind CSS + +Tailwind відчувається як "CSS у режимі дзен". + +Завдяки наданню будівельних блоків у вигляді гарних кольорів за замовчуванням, відступів та інших примітивних налаштувань Tailwind дозволяє легко створювати додаток із гарним зовнішнім виглядом. І на відміну від бібліотек компонентів, він не обмежує вас, коли ви хочете вивести свою програму на наступний рівень і створити щось красиве та унікальне. + +До того ж, завдяки інлайновому підходу, Tailwind спонукає вас стилізувати без занепокоєння про назву класів, організацію файлів або будь-яку іншу проблему, не пов'язану з вирішенням завдання. + +### NextAuth.js + +Коли ви хочете додати систему аутентифікації у вашу програму NextJS, NextAuth.js - відмінне рішення, щоб не морочитися з реалізацією складної системи безпеки. Вона має великий список провайдерів для швидкого додавання аутентифікації через OAuth і надає адаптери для багатьох баз даних та ORM. diff --git a/www/src/pages/zh-hans/usage/prisma.md b/www/src/pages/zh-hans/usage/prisma.md index bb20bf321d..e22cf022c5 100644 --- a/www/src/pages/zh-hans/usage/prisma.md +++ b/www/src/pages/zh-hans/usage/prisma.md @@ -75,4 +75,4 @@ main() | Prisma GitHub | https://github.com/prisma/prisma | | Prisma Migrate 演练场 | https://playground.prisma.io/guides | | NextAuth.JS Prisma 适配器 | https://next-auth.js.org/adapters/prisma | -| Planetscale 连接指引 | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | +| PlanetScale 连接指引 | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale |