From 06a04354d55f56d8234f191884666eedd40688aa Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 08:58:00 +0300 Subject: [PATCH 01/14] environement --- .gitignore | 35 +++++++++++++++++++++-------------- .nvmrc | 1 + example.env | 4 +++- 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 .nvmrc diff --git a/.gitignore b/.gitignore index e540aed..9f749ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,25 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies -node_modules/ -.pnp/ -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions +node_modules +.pnp +.pnp.js # testing -coverage/ +coverage # next.js .next/ out/ +next-env.d.ts + +# nitro +.nitro/ +.output/ + # production -build/ +build # misc .DS_Store @@ -30,16 +31,22 @@ yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* +# local env files +.env +.env*.local # vercel .vercel # typescript -*.tsbuildinfo -next-env.d.ts +dist/ +.cache + +# turbo +.turbo +# tanstack +.tanstack # ========= python ========= # Python-generated files diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..aa50a62 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.21.0 diff --git a/example.env b/example.env index ed7f45e..edcbf5e 100644 --- a/example.env +++ b/example.env @@ -13,7 +13,9 @@ DATABASE_URL=postgresql://user:password@application_db:5432/app_db NEXT_PUBLIC_API_URL=http://localhost:5000 NEXTAUTH_SECRET=your_nextauth_secret_here NEXTAUTH_URL=http://localhost:5001 +AUTH_SECRET='supersecret' +AI_HOST=http://localhost:5000 # Development settings NODE_ENV=development -PYTHONPATH=/app/src \ No newline at end of file +PYTHONPATH=/app/src From bc6849c9dbd3115e2af58fc7d524ed97f8ccf6d3 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:00:06 +0300 Subject: [PATCH 02/14] initing mono repo --- .github/DISCUSSION_TEMPLATE/ideas.yml | 21 +++++++++ .github/ISSUE_TEMPLATE/bug_report.yml | 37 ++++++++++++++++ .github/renovate.json | 16 +++++++ .github/workflows/ci.yml | 57 ++++++++++++++++++++++++ pnpm-workspace.yaml | 46 ++++++++++++++++++-- turbo.json | 62 +++++++++++++++++++++++++++ 6 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 .github/DISCUSSION_TEMPLATE/ideas.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/renovate.json create mode 100644 .github/workflows/ci.yml create mode 100644 turbo.json diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml new file mode 100644 index 0000000..c15eba2 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -0,0 +1,21 @@ +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to file a feature request. Please fill out this form as completely as possible. + - type: textarea + attributes: + label: Describe the feature you'd like to request + description: Please describe the feature as clear and concise as possible. Remember to add context as to why you believe this feature is needed. + validations: + required: true + - type: textarea + attributes: + label: Describe the solution you'd like to see + description: Please describe the solution you would like to see. Adding example usage is a good way to provide context. + validations: + required: true + - type: textarea + attributes: + label: Additional information + description: Add any other information related to the feature here. If your feature request is related to any issues or discussions, link them here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..54199a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,37 @@ +name: 🐞 Bug Report +description: Create a bug report to help us improve +title: "bug: " +labels: ["🐞❔ unconfirmed bug"] +body: + - type: textarea + attributes: + label: Provide environment information + description: | + Run this command in your project root and paste the results in a code block: + ```bash + npx envinfo --system --binaries + ``` + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of the bug, as well as what you expected to happen when encountering it. + validations: + required: true + - type: input + attributes: + label: Link to reproduction + description: Please provide a link to a reproduction of the bug. Issues without a reproduction repo may be ignored. + validations: + required: true + - type: textarea + attributes: + label: To reproduce + description: Describe how to reproduce your bug. Steps, code snippets, reproduction repos etc. + validations: + required: true + - type: textarea + attributes: + label: Additional information + description: Add any other information related to the bug here, screenshots if applicable. diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..28ff1a9 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:base"], + "packageRules": [ + { + "matchPackagePatterns": ["^@governance/"], + "enabled": false + } + ], + "updateInternalDeps": true, + "rangeStrategy": "bump", + "automerge": true, + "npm": { + "fileMatch": ["(^|/)package\\.json$", "(^|/)package\\.json\\.hbs$"] + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7d83d31 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: CI + +on: + pull_request: + branches: ["*"] + push: + branches: ["main"] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +# You can leverage Vercel Remote Caching with Turbo to speed up your builds +# @link https://turborepo.com/docs/core-concepts/remote-caching#remote-caching-on-vercel-builds +env: + FORCE_COLOR: 3 + TURBO_TEAM: ${{ vars.TURBO_TEAM }} + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Setup + uses: ./tooling/github/setup + + - name: Copy env + shell: bash + run: cp .env.example .env + + - name: Lint + run: pnpm lint && pnpm lint:ws + + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Setup + uses: ./tooling/github/setup + + - name: Format + run: pnpm format + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Setup + uses: ./tooling/github/setup + + - name: Typecheck + run: pnpm typecheck diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ec39e75..1f7fc66 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,46 @@ packages: - apps/* - packages/* -ignoredBuiltDependencies: - - sharp - - unrs-resolver + - tooling/* + +catalog: + "@better-auth/cli": 1.4.0-beta.9 + "@eslint/js": ^9.38.0 + "@tailwindcss/postcss": ^4.1.16 + "@tailwindcss/vite": ^4.1.16 + "@tanstack/react-form": ^1.23.8 + "@tanstack/react-query": ^5.90.8 + "@trpc/client": ^11.7.1 + "@trpc/server": ^11.7.1 + "@trpc/tanstack-react-query": ^11.7.1 + "@types/node": ^22.18.12 + "@vitejs/plugin-react": 5.1.0 + better-auth: 1.4.0-beta.9 + eslint: ^9.38.0 + prettier: ^3.6.2 + tailwindcss: ^4.1.16 + typescript: ^5.9.3 + zod: ^4.1.12 + vite: 7.1.12 + +catalogs: + react19: + "@types/react": ~19.1.0 + "@types/react-dom": ~19.1.0 + react: 19.1.4 + react-dom: 19.1.4 + +linkWorkspacePackages: true + +onlyBuiltDependencies: + - "@tailwindcss/oxide" + - esbuild + +overrides: + "@types/minimatch": 5.1.2 + lightningcss: 1.30.1 + vite: 7.1.12 + +publicHoistPattern: + - "@ianvs/prettier-plugin-sort-imports" + - prettier-plugin-tailwindcss diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..0138a3e --- /dev/null +++ b/turbo.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://turborepo.com/schema.json", + "ui": "tui", + "tasks": { + "topo": { + "dependsOn": ["^topo"] + }, + "build": { + "dependsOn": ["^build"], + "outputs": [".cache/tsbuildinfo.json", "dist/**"] + }, + "dev": { + "dependsOn": ["^dev"], + "cache": false, + "persistent": false + }, + "format": { + "outputs": [".cache/.prettiercache"], + "outputLogs": "new-only" + }, + "lint": { + "dependsOn": ["^topo", "^build"], + "outputs": [".cache/.eslintcache"] + }, + "typecheck": { + "dependsOn": ["^topo", "^build"], + "outputs": [".cache/tsbuildinfo.json"] + }, + "clean": { + "cache": false + }, + "//#clean": { + "cache": false + }, + "push": { + "cache": false, + "interactive": true + }, + "studio": { + "cache": false, + "persistent": true + }, + "ui-add": { + "cache": false, + "interactive": true + } + }, + "globalEnv": [ + "POSTGRES_URL", + "AUTH_REDIRECT_PROXY_URL", + "AUTH_SECRET", + "PORT" + ], + "globalPassThroughEnv": [ + "NODE_ENV", + "CI", + "VERCEL", + "VERCEL_ENV", + "VERCEL_URL", + "npm_lifecycle_event" + ] +} From e4b21fd68f847daaa013a5d836736ecd91b445a8 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:00:36 +0300 Subject: [PATCH 03/14] vscode settings --- .vscode/extensions.json | 8 ++++++++ .vscode/launch.json | 17 +++++++++++++++++ .vscode/settings.json | 7 +++++++ 3 files changed, 32 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..fd682ae --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "yoavbls.pretty-ts-errors", + "bradlc.vscode-tailwindcss" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..04f7850 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js", + "type": "node-terminal", + "request": "launch", + "command": "pnpm dev", + "cwd": "${workspaceFolder}/apps/nextjs", + "skipFiles": ["/**"], + "sourceMaps": true, + "sourceMapPathOverrides": { + "/turbopack/[project]/*": "${webRoot}/*" //https://github.com/vercel/next.js/issues/62008 + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..44a73ec --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "eslint.workingDirectories": [ + { + "mode": "auto" + } + ] +} From 7913cb7fe6c39d06893095a8f21650b22d5546bf Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:06:38 +0300 Subject: [PATCH 04/14] moving from web to nextjs --- apps/web/eslint.config.mjs | 18 ---------- apps/web/next.config.ts | 9 ----- apps/web/package.json | 27 --------------- apps/web/postcss.config.mjs | 7 ---- apps/web/public/file.svg | 1 - apps/web/public/globe.svg | 1 - apps/web/public/next.svg | 1 - apps/web/public/vercel.svg | 1 - apps/web/public/window.svg | 1 - apps/web/src/app/favicon.ico | Bin 25931 -> 0 bytes apps/web/src/app/globals.css | 26 -------------- apps/web/src/app/layout.tsx | 34 ------------------ apps/web/src/app/page.tsx | 65 ----------------------------------- apps/web/tsconfig.json | 34 ------------------ 14 files changed, 225 deletions(-) delete mode 100644 apps/web/eslint.config.mjs delete mode 100644 apps/web/next.config.ts delete mode 100644 apps/web/package.json delete mode 100644 apps/web/postcss.config.mjs delete mode 100644 apps/web/public/file.svg delete mode 100644 apps/web/public/globe.svg delete mode 100644 apps/web/public/next.svg delete mode 100644 apps/web/public/vercel.svg delete mode 100644 apps/web/public/window.svg delete mode 100644 apps/web/src/app/favicon.ico delete mode 100644 apps/web/src/app/globals.css delete mode 100644 apps/web/src/app/layout.tsx delete mode 100644 apps/web/src/app/page.tsx delete mode 100644 apps/web/tsconfig.json diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs deleted file mode 100644 index 05e726d..0000000 --- a/apps/web/eslint.config.mjs +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig, globalIgnores } from "eslint/config"; -import nextVitals from "eslint-config-next/core-web-vitals"; -import nextTs from "eslint-config-next/typescript"; - -const eslintConfig = defineConfig([ - ...nextVitals, - ...nextTs, - // Override default ignores of eslint-config-next. - globalIgnores([ - // Default ignores of eslint-config-next: - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", - ]), -]); - -export default eslintConfig; diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts deleted file mode 100644 index 30d7f8b..0000000 --- a/apps/web/next.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ - reactCompiler: true, - output: "standalone", -}; - -export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json deleted file mode 100644 index 52dd8c1..0000000 --- a/apps/web/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "@governance/web", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint" - }, - "dependencies": { - "next": "16.1.3", - "react": "19.2.3", - "react-dom": "19.2.3" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "babel-plugin-react-compiler": "1.0.0", - "eslint": "^9", - "eslint-config-next": "16.1.3", - "tailwindcss": "^4", - "typescript": "^5" - } -} diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs deleted file mode 100644 index 61e3684..0000000 --- a/apps/web/postcss.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -const config = { - plugins: { - "@tailwindcss/postcss": {}, - }, -}; - -export default config; diff --git a/apps/web/public/file.svg b/apps/web/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/apps/web/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/globe.svg b/apps/web/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/apps/web/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/next.svg b/apps/web/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/apps/web/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/vercel.svg b/apps/web/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/apps/web/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/window.svg b/apps/web/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/apps/web/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/src/app/favicon.ico b/apps/web/src/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css deleted file mode 100644 index a2dc41e..0000000 --- a/apps/web/src/app/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -@import "tailwindcss"; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx deleted file mode 100644 index f7fa87e..0000000 --- a/apps/web/src/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx deleted file mode 100644 index 295f8fd..0000000 --- a/apps/web/src/app/page.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import Image from "next/image"; - -export default function Home() { - return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
- -
-
- ); -} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json deleted file mode 100644 index cf9c65d..0000000 --- a/apps/web/tsconfig.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts", - "**/*.mts" - ], - "exclude": ["node_modules"] -} From ca99967da5d7cbd2a4ac63884b0f58b6f089f4a0 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:07:54 +0300 Subject: [PATCH 05/14] tooling and turbo setup --- package.json | 35 +++- tooling/eslint/base.ts | 88 ++++++++++ tooling/eslint/nextjs.ts | 15 ++ tooling/eslint/package.json | 35 ++++ tooling/eslint/react.ts | 19 +++ tooling/eslint/tsconfig.json | 5 + tooling/github/package.json | 3 + tooling/github/setup/action.yml | 16 ++ tooling/prettier/index.js | 45 +++++ tooling/prettier/package.json | 24 +++ tooling/prettier/tsconfig.json | 5 + tooling/tailwind/eslint.config.ts | 5 + tooling/tailwind/package.json | 31 ++++ tooling/tailwind/postcss-config.js | 5 + tooling/tailwind/theme.css | 158 ++++++++++++++++++ tooling/tailwind/tsconfig.json | 5 + tooling/typescript/base.json | 30 ++++ tooling/typescript/compiled-package.json | 12 ++ tooling/typescript/package.json | 7 + turbo/generators/config.ts | 95 +++++++++++ .../generators/templates/eslint.config.ts.hbs | 10 ++ turbo/generators/templates/package.json.hbs | 26 +++ turbo/generators/templates/tsconfig.json.hbs | 6 + 23 files changed, 676 insertions(+), 4 deletions(-) create mode 100644 tooling/eslint/base.ts create mode 100644 tooling/eslint/nextjs.ts create mode 100644 tooling/eslint/package.json create mode 100644 tooling/eslint/react.ts create mode 100644 tooling/eslint/tsconfig.json create mode 100644 tooling/github/package.json create mode 100644 tooling/github/setup/action.yml create mode 100644 tooling/prettier/index.js create mode 100644 tooling/prettier/package.json create mode 100644 tooling/prettier/tsconfig.json create mode 100644 tooling/tailwind/eslint.config.ts create mode 100644 tooling/tailwind/package.json create mode 100644 tooling/tailwind/postcss-config.js create mode 100644 tooling/tailwind/theme.css create mode 100644 tooling/tailwind/tsconfig.json create mode 100644 tooling/typescript/base.json create mode 100644 tooling/typescript/compiled-package.json create mode 100644 tooling/typescript/package.json create mode 100644 turbo/generators/config.ts create mode 100644 turbo/generators/templates/eslint.config.ts.hbs create mode 100644 turbo/generators/templates/package.json.hbs create mode 100644 turbo/generators/templates/tsconfig.json.hbs diff --git a/package.json b/package.json index 1f7332d..61bb058 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,36 @@ { "name": "governance-agent", "private": true, + "engines": { + "node": "^22.21.0", + "pnpm": "^10.19.0" + }, + "packageManager": "pnpm@10.19.0", "scripts": { - "dev": "pnpm --filter web dev", - "build": "pnpm -r build", - "lint": "pnpm -r lint" - } + "build": "turbo run build", + "clean": "git clean -xdf node_modules", + "clean:workspaces": "turbo run clean", + "auth:generate": "pnpm -F @governance/auth generate", + "db:push": "turbo -F @governance/db push", + "db:studio": "turbo -F @governance/db studio", + "dev": "turbo watch dev --continue", + "dev:next": "turbo watch dev -F @governance/nextjs...", + "format": "turbo run format --continue -- --cache --cache-location .cache/.prettiercache", + "format:fix": "turbo run format --continue -- --write --cache --cache-location .cache/.prettiercache", + "lint": "turbo run lint --continue -- --cache --cache-location .cache/.eslintcache", + "lint:fix": "turbo run lint --continue -- --fix --cache --cache-location .cache/.eslintcache", + "lint:ws": "pnpm dlx sherif@latest", + "postinstall": "pnpm lint:ws", + "typecheck": "turbo run typecheck", + "ui-add": "turbo run ui-add" + }, + "devDependencies": { + "@governance/prettier-config": "workspace:*", + "@turbo/gen": "^2.5.8", + "dotenv-cli": "^10.0.0", + "prettier": "catalog:", + "turbo": "^2.5.8", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" } diff --git a/tooling/eslint/base.ts b/tooling/eslint/base.ts new file mode 100644 index 0000000..e126739 --- /dev/null +++ b/tooling/eslint/base.ts @@ -0,0 +1,88 @@ +import * as path from "node:path"; +import { includeIgnoreFile } from "@eslint/compat"; +import eslint from "@eslint/js"; +import importPlugin from "eslint-plugin-import"; +import turboPlugin from "eslint-plugin-turbo"; +import { defineConfig } from "eslint/config"; +import tseslint from "typescript-eslint"; + +/** + * All packages that leverage t3-env should use this rule + */ +export const restrictEnvAccess = defineConfig( + { ignores: ["**/env.ts"] }, + { + files: ["**/*.js", "**/*.ts", "**/*.tsx"], + rules: { + "no-restricted-properties": [ + "error", + { + object: "process", + property: "env", + message: + "Use `import { env } from '~/env'` instead to ensure validated types.", + }, + ], + "no-restricted-imports": [ + "error", + { + name: "process", + importNames: ["env"], + message: + "Use `import { env } from '~/env'` instead to ensure validated types.", + }, + ], + }, + }, +); + +export const baseConfig = defineConfig( + // Ignore files not tracked by VCS and any config files + includeIgnoreFile(path.join(import.meta.dirname, "../../.gitignore")), + { ignores: ["**/*.config.*"] }, + { + files: ["**/*.js", "**/*.ts", "**/*.tsx"], + plugins: { + import: importPlugin, + turbo: turboPlugin, + }, + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + ], + rules: { + ...turboPlugin.configs.recommended.rules, + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + ], + "@typescript-eslint/consistent-type-imports": [ + "warn", + { prefer: "type-imports", fixStyle: "separate-type-imports" }, + ], + "@typescript-eslint/no-misused-promises": [ + 2, + { checksVoidReturn: { attributes: false } }, + ], + "@typescript-eslint/no-unnecessary-condition": [ + "error", + { + allowConstantLoopConditions: true, + }, + ], + "@typescript-eslint/no-non-null-assertion": "error", + "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + }, + }, + { + linterOptions: { reportUnusedDisableDirectives: true }, + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, +); diff --git a/tooling/eslint/nextjs.ts b/tooling/eslint/nextjs.ts new file mode 100644 index 0000000..6858c53 --- /dev/null +++ b/tooling/eslint/nextjs.ts @@ -0,0 +1,15 @@ +import nextPlugin from "@next/eslint-plugin-next"; +import { defineConfig } from "eslint/config"; + +export const nextjsConfig = defineConfig({ + files: ["**/*.ts", "**/*.tsx"], + plugins: { + "@next/next": nextPlugin, + }, + rules: { + ...nextPlugin.configs.recommended.rules, + ...nextPlugin.configs["core-web-vitals"].rules, + // TypeError: context.getAncestors is not a function + "@next/next/no-duplicate-head": "off", + }, +}); diff --git a/tooling/eslint/package.json b/tooling/eslint/package.json new file mode 100644 index 0000000..28832dd --- /dev/null +++ b/tooling/eslint/package.json @@ -0,0 +1,35 @@ +{ + "name": "@governance/eslint-config", + "private": true, + "type": "module", + "exports": { + "./base": "./base.ts", + "./nextjs": "./nextjs.ts", + "./react": "./react.ts" + }, + "scripts": { + "clean": "git clean -xdf .cache .turbo node_modules", + "format": "prettier --check . --ignore-path ../../.gitignore", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@eslint/compat": "^1.4.0", + "@eslint/js": "catalog:", + "@next/eslint-plugin-next": "^16.0.0", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-turbo": "^2.5.8", + "typescript-eslint": "^8.46.2" + }, + "devDependencies": { + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "@types/node": "catalog:", + "eslint": "catalog:", + "prettier": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/tooling/eslint/react.ts b/tooling/eslint/react.ts new file mode 100644 index 0000000..6163287 --- /dev/null +++ b/tooling/eslint/react.ts @@ -0,0 +1,19 @@ +import reactPlugin from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; +import { defineConfig } from "eslint/config"; + +export const reactConfig = defineConfig( + { + files: ["**/*.ts", "**/*.tsx"], + ...reactPlugin.configs.flat.recommended, + ...reactPlugin.configs.flat["jsx-runtime"], + languageOptions: { + ...reactPlugin.configs.flat.recommended?.languageOptions, + ...reactPlugin.configs.flat["jsx-runtime"]?.languageOptions, + globals: { + React: "writable", + }, + }, + }, + reactHooks.configs.flat["recommended-latest"]!, +); diff --git a/tooling/eslint/tsconfig.json b/tooling/eslint/tsconfig.json new file mode 100644 index 0000000..07e49f9 --- /dev/null +++ b/tooling/eslint/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@governance/tsconfig/base.json", + "include": ["."], + "exclude": ["node_modules"] +} diff --git a/tooling/github/package.json b/tooling/github/package.json new file mode 100644 index 0000000..2578007 --- /dev/null +++ b/tooling/github/package.json @@ -0,0 +1,3 @@ +{ + "name": "@governance/github" +} diff --git a/tooling/github/setup/action.yml b/tooling/github/setup/action.yml new file mode 100644 index 0000000..5da1003 --- /dev/null +++ b/tooling/github/setup/action.yml @@ -0,0 +1,16 @@ +name: "Setup and install" +description: "Common setup steps for Actions" + +runs: + using: composite + steps: + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 + with: + node-version-file: ".nvmrc" + + - shell: bash + run: pnpm add -g turbo + + - shell: bash + run: pnpm install diff --git a/tooling/prettier/index.js b/tooling/prettier/index.js new file mode 100644 index 0000000..bf5e574 --- /dev/null +++ b/tooling/prettier/index.js @@ -0,0 +1,45 @@ +/** @typedef {import("prettier").Config} PrettierConfig */ +/** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */ +/** @typedef {import("@ianvs/prettier-plugin-sort-imports").PluginConfig} SortImportsConfig */ + +/** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */ +const config = { + plugins: [ + "@ianvs/prettier-plugin-sort-imports", + "prettier-plugin-tailwindcss", + ], + tailwindFunctions: ["cn", "cva"], + importOrder: [ + "", + "^(react/(.*)$)|^(react$)|^(react-native(.*)$)", + "^(next/(.*)$)|^(next$)", + "^(expo(.*)$)|^(expo$)", + "", + "", + "^@governance", + "^@governance/(.*)$", + "", + "^[.|..|~]", + "^~/", + "^[../]", + "^[./]", + ], + importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], + importOrderTypeScriptVersion: "5.0.0", + overrides: [ + { + files: "*.json.hbs", + options: { + parser: "json", + }, + }, + { + files: "*.ts.hbs", + options: { + parser: "babel", + }, + }, + ], +}; + +export default config; diff --git a/tooling/prettier/package.json b/tooling/prettier/package.json new file mode 100644 index 0000000..cf29476 --- /dev/null +++ b/tooling/prettier/package.json @@ -0,0 +1,24 @@ +{ + "name": "@governance/prettier-config", + "private": true, + "type": "module", + "exports": { + ".": "./index.js" + }, + "scripts": { + "clean": "git clean -xdf .cache .turbo node_modules", + "format": "prettier --check . --ignore-path ../../.gitignore", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.7.0", + "prettier": "catalog:", + "prettier-plugin-tailwindcss": "^0.7.1" + }, + "devDependencies": { + "@governance/tsconfig": "workspace:*", + "@types/node": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/tooling/prettier/tsconfig.json b/tooling/prettier/tsconfig.json new file mode 100644 index 0000000..07e49f9 --- /dev/null +++ b/tooling/prettier/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@governance/tsconfig/base.json", + "include": ["."], + "exclude": ["node_modules"] +} diff --git a/tooling/tailwind/eslint.config.ts b/tooling/tailwind/eslint.config.ts new file mode 100644 index 0000000..69b3171 --- /dev/null +++ b/tooling/tailwind/eslint.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfig } from "@governance/eslint-config/base"; + +export default defineConfig(baseConfig); diff --git a/tooling/tailwind/package.json b/tooling/tailwind/package.json new file mode 100644 index 0000000..9c6948f --- /dev/null +++ b/tooling/tailwind/package.json @@ -0,0 +1,31 @@ +{ + "name": "@governance/tailwind-config", + "private": true, + "type": "module", + "exports": { + "./theme": "./theme.css", + "./postcss-config": "./postcss-config.js" + }, + "license": "MIT", + "scripts": { + "clean": "git clean -xdf .cache .turbo node_modules", + "format": "prettier --check . --ignore-path ../../.gitignore", + "lint": "eslint --flag unstable_native_nodejs_ts_config", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@tailwindcss/postcss": "catalog:", + "postcss": "^8.5.6", + "tailwindcss": "catalog:" + }, + "devDependencies": { + "@governance/eslint-config": "workspace:*", + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "@types/node": "catalog:", + "eslint": "catalog:", + "prettier": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/tooling/tailwind/postcss-config.js b/tooling/tailwind/postcss-config.js new file mode 100644 index 0000000..c2ddf74 --- /dev/null +++ b/tooling/tailwind/postcss-config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/tooling/tailwind/theme.css b/tooling/tailwind/theme.css new file mode 100644 index 0000000..67ae2a3 --- /dev/null +++ b/tooling/tailwind/theme.css @@ -0,0 +1,158 @@ +:root { + --background: oklch(0.9875 0.0045 314.8053); + --foreground: oklch(0.2277 0.0105 312.0161); + --card: oklch(1 0 0); + --card-foreground: oklch(0.2277 0.0105 312.0161); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.2277 0.0105 312.0161); + --primary: oklch(0.5605 0.1911 350.0331); + --primary-foreground: oklch(1 0 0); + --secondary: oklch(0.967 0.0106 316.4921); + --secondary-foreground: oklch(0.4536 0.0226 309.5036); + --muted: oklch(0.967 0.0106 316.4921); + --muted-foreground: oklch(0.5653 0.021 306.4429); + --accent: oklch(0.967 0.0106 316.4921); + --accent-foreground: oklch(0.5605 0.1911 350.0331); + --destructive: oklch(0.6368 0.2078 25.3313); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9419 0.016 310.0997); + --input: oklch(1 0 0); + --ring: oklch(0.5605 0.1911 350.0331); + --chart-1: oklch(0.5605 0.1911 350.0331); + --chart-2: oklch(0.6747 0.1492 345.9482); + --chart-3: oklch(0.7729 0.1045 344.4709); + --chart-4: oklch(0.8625 0.0636 341.4088); + --chart-5: oklch(0.9411 0.0261 343.2843); + --sidebar: oklch(0.967 0.0106 316.4921); + --sidebar-foreground: oklch(0.4536 0.0226 309.5036); + --sidebar-primary: oklch(0.5605 0.1911 350.0331); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.9419 0.016 310.0997); + --sidebar-accent-foreground: oklch(0.5605 0.1911 350.0331); + --sidebar-border: oklch(0.9155 0.0235 310.6964); + --sidebar-ring: oklch(0.5605 0.1911 350.0331); + --radius: 0.75rem; + --shadow-2xs: 0px 2px 10px 0px hsl(0 0% 0% / 0.03); + --shadow-xs: 0px 2px 10px 0px hsl(0 0% 0% / 0.03); + --shadow-sm: + 0px 2px 10px 0px hsl(0 0% 0% / 0.05), 0px 1px 2px -1px hsl(0 0% 0% / 0.05); + --shadow: + 0px 2px 10px 0px hsl(0 0% 0% / 0.05), 0px 1px 2px -1px hsl(0 0% 0% / 0.05); + --shadow-md: + 0px 2px 10px 0px hsl(0 0% 0% / 0.05), 0px 2px 4px -1px hsl(0 0% 0% / 0.05); + --shadow-lg: + 0px 2px 10px 0px hsl(0 0% 0% / 0.05), 0px 4px 6px -1px hsl(0 0% 0% / 0.05); + --shadow-xl: + 0px 2px 10px 0px hsl(0 0% 0% / 0.05), 0px 8px 10px -1px hsl(0 0% 0% / 0.05); + --shadow-2xl: 0px 2px 10px 0px hsl(0 0% 0% / 0.13); + --tracking-normal: 0rem; + --spacing: 0.25rem; + + @variant dark { + --background: oklch(0.1836 0.0111 311.9111); + --foreground: oklch(0.9788 0.0057 308.3962); + --card: oklch(0.1836 0.0111 311.9111); + --card-foreground: oklch(0.9788 0.0057 308.3962); + --popover: oklch(0.1836 0.0111 311.9111); + --popover-foreground: oklch(0.9788 0.0057 308.3962); + --primary: oklch(0.6747 0.1492 345.9482); + --primary-foreground: oklch(0.1836 0.0111 311.9111); + --secondary: oklch(0.2551 0.0142 310.7968); + --secondary-foreground: oklch(0.721 0.0184 308.1777); + --muted: oklch(0.2551 0.0142 310.7968); + --muted-foreground: oklch(0.6288 0.0177 309.9946); + --accent: oklch(0.2551 0.0142 310.7968); + --accent-foreground: oklch(0.6747 0.1492 345.9482); + --destructive: oklch(0.3958 0.1331 25.723); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.2941 0.0175 310.1142); + --input: oklch(0.2551 0.0142 310.7968); + --ring: oklch(0.6747 0.1492 345.9482); + --chart-1: oklch(0.6747 0.1492 345.9482); + --chart-2: oklch(0.5605 0.1911 350.0331); + --chart-3: oklch(0.4988 0.1668 350); + --chart-4: oklch(0.4373 0.1428 349.7487); + --chart-5: oklch(0.3738 0.1177 349.3988); + --sidebar: oklch(0.2103 0.0107 311.9806); + --sidebar-foreground: oklch(0.721 0.0184 308.1777); + --sidebar-primary: oklch(0.6747 0.1492 345.9482); + --sidebar-primary-foreground: oklch(0.1836 0.0111 311.9111); + --sidebar-accent: oklch(0.2551 0.0142 310.7968); + --sidebar-accent-foreground: oklch(0.6747 0.1492 345.9482); + --sidebar-border: oklch(0.2941 0.0175 310.1142); + --sidebar-ring: oklch(0.6747 0.1492 345.9482); + --radius: 0.75rem; + --shadow-2xs: 0px 2px 10px 0px hsl(0 0% 0% / 0.1); + --shadow-xs: 0px 2px 10px 0px hsl(0 0% 0% / 0.1); + --shadow-sm: + 0px 2px 10px 0px hsl(0 0% 0% / 0.2), 0px 1px 2px -1px hsl(0 0% 0% / 0.2); + --shadow: + 0px 2px 10px 0px hsl(0 0% 0% / 0.2), 0px 1px 2px -1px hsl(0 0% 0% / 0.2); + --shadow-md: + 0px 2px 10px 0px hsl(0 0% 0% / 0.2), 0px 2px 4px -1px hsl(0 0% 0% / 0.2); + --shadow-lg: + 0px 2px 10px 0px hsl(0 0% 0% / 0.2), 0px 4px 6px -1px hsl(0 0% 0% / 0.2); + --shadow-xl: + 0px 2px 10px 0px hsl(0 0% 0% / 0.2), 0px 8px 10px -1px hsl(0 0% 0% / 0.2); + --shadow-2xl: 0px 2px 10px 0px hsl(0 0% 0% / 0.5); + } +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --shadow-2xs: var(--shadow-2xs); + --shadow-xs: var(--shadow-xs); + --shadow-sm: var(--shadow-sm); + --shadow: var(--shadow); + --shadow-md: var(--shadow-md); + --shadow-lg: var(--shadow-lg); + --shadow-xl: var(--shadow-xl); + --shadow-2xl: var(--shadow-2xl); + + --tracking-tighter: calc(var(--tracking-normal) - 0.05em); + --tracking-tight: calc(var(--tracking-normal) - 0.025em); + --tracking-normal: var(--tracking-normal); + --tracking-wide: calc(var(--tracking-normal) + 0.025em); + --tracking-wider: calc(var(--tracking-normal) + 0.05em); + --tracking-widest: calc(var(--tracking-normal) + 0.1em); +} diff --git a/tooling/tailwind/tsconfig.json b/tooling/tailwind/tsconfig.json new file mode 100644 index 0000000..07e49f9 --- /dev/null +++ b/tooling/tailwind/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@governance/tsconfig/base.json", + "include": ["."], + "exclude": ["node_modules"] +} diff --git a/tooling/typescript/base.json b/tooling/typescript/base.json new file mode 100644 index 0000000..805687d --- /dev/null +++ b/tooling/typescript/base.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + /** Base Options */ + "esModuleInterop": true, + "skipLibCheck": true, + "target": "ES2022", + "lib": ["ES2022"], + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + + /** Keep TSC performant in monorepos */ + "incremental": true, + "disableSourceOfProjectReferenceRedirect": true, + "tsBuildInfoFile": "${configDir}/.cache/tsbuildinfo.json", + + /** Strictness */ + "strict": true, + "noUncheckedIndexedAccess": true, + "checkJs": true, + + /** Transpile using Bundler (not tsc) */ + "module": "Preserve", + "moduleResolution": "Bundler", + "noEmit": true + }, + "exclude": ["node_modules", "build", "dist", ".next", ".expo"] +} diff --git a/tooling/typescript/compiled-package.json b/tooling/typescript/compiled-package.json new file mode 100644 index 0000000..f20c57b --- /dev/null +++ b/tooling/typescript/compiled-package.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./base.json", + "compilerOptions": { + /** Emit types for internal packages to speed up editor performance. */ + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "noEmit": false, + "outDir": "${configDir}/dist" + } +} diff --git a/tooling/typescript/package.json b/tooling/typescript/package.json new file mode 100644 index 0000000..cacda29 --- /dev/null +++ b/tooling/typescript/package.json @@ -0,0 +1,7 @@ +{ + "name": "@governance/tsconfig", + "private": true, + "files": [ + "*.json" + ] +} diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts new file mode 100644 index 0000000..3bd8902 --- /dev/null +++ b/turbo/generators/config.ts @@ -0,0 +1,95 @@ +import { execSync } from "node:child_process"; +import type { PlopTypes } from "@turbo/gen"; + +interface PackageJson { + name: string; + scripts: Record; + dependencies: Record; + devDependencies: Record; +} + +export default function generator(plop: PlopTypes.NodePlopAPI): void { + plop.setGenerator("init", { + description: "Generate a new package for the Acme Monorepo", + prompts: [ + { + type: "input", + name: "name", + message: + "What is the name of the package? (You can skip the `@governance/` prefix)", + }, + { + type: "input", + name: "deps", + message: + "Enter a space separated list of dependencies you would like to install", + }, + ], + actions: [ + (answers) => { + if ("name" in answers && typeof answers.name === "string") { + if (answers.name.startsWith("@governance/")) { + answers.name = answers.name.replace("@governance/", ""); + } + } + return "Config sanitized"; + }, + { + type: "add", + path: "packages/{{ name }}/eslint.config.ts", + templateFile: "templates/eslint.config.ts.hbs", + }, + { + type: "add", + path: "packages/{{ name }}/package.json", + templateFile: "templates/package.json.hbs", + }, + { + type: "add", + path: "packages/{{ name }}/tsconfig.json", + templateFile: "templates/tsconfig.json.hbs", + }, + { + type: "add", + path: "packages/{{ name }}/src/index.ts", + template: "export const name = '{{ name }}';", + }, + { + type: "modify", + path: "packages/{{ name }}/package.json", + async transform(content, answers) { + if ("deps" in answers && typeof answers.deps === "string") { + const pkg = JSON.parse(content) as PackageJson; + for (const dep of answers.deps.split(" ").filter(Boolean)) { + const version = await fetch( + `https://registry.npmjs.org/-/package/${dep}/dist-tags`, + ) + .then((res) => res.json()) + .then((json) => json.latest); + if (!pkg.dependencies) pkg.dependencies = {}; + pkg.dependencies[dep] = `^${version}`; + } + return JSON.stringify(pkg, null, 2); + } + return content; + }, + }, + async (answers) => { + /** + * Install deps and format everything + */ + if ("name" in answers && typeof answers.name === "string") { + // execSync("pnpm dlx sherif@latest --fix", { + // stdio: "inherit", + // }); + execSync("pnpm i", { stdio: "inherit" }); + execSync( + `pnpm prettier --write packages/${answers.name}/** --list-different`, + ); + return "Package scaffolded"; + } + return "Package not scaffolded"; + }, + ], + }); +} diff --git a/turbo/generators/templates/eslint.config.ts.hbs b/turbo/generators/templates/eslint.config.ts.hbs new file mode 100644 index 0000000..5ac169b --- /dev/null +++ b/turbo/generators/templates/eslint.config.ts.hbs @@ -0,0 +1,10 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfig } from "@governance/eslint-config/base"; + +export default defineConfig( + { + ignores: [], + }, + baseConfig, +); diff --git a/turbo/generators/templates/package.json.hbs b/turbo/generators/templates/package.json.hbs new file mode 100644 index 0000000..f448972 --- /dev/null +++ b/turbo/generators/templates/package.json.hbs @@ -0,0 +1,26 @@ +{ + "name": "@governance/{{ name }}", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "license": "MIT", + "scripts": { + "build": "tsc", + "clean": "git clean -xdf .cache .turbo dist node_modules", + "dev": "tsc", + "format": "prettier --check . --ignore-path ../../.gitignore", + "lint": "eslint", + "typecheck": "tsc --noEmit --emitDeclarationOnly false" + }, + "devDependencies": { + "@governance/eslint-config": "workspace:*", + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "eslint": "catalog:", + "prettier": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/turbo/generators/templates/tsconfig.json.hbs b/turbo/generators/templates/tsconfig.json.hbs new file mode 100644 index 0000000..a56be25 --- /dev/null +++ b/turbo/generators/templates/tsconfig.json.hbs @@ -0,0 +1,6 @@ +{ + "extends": "@governance/tsconfig/compiled-package.json", + "compilerOptions": {}, + "include": ["*.ts", "src"], + "exclude": ["node_modules"] +} From 5c2c3df8f42a9f13f4d98fb745ec374d921c5ce1 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:10:08 +0300 Subject: [PATCH 06/14] zod validators --- packages/validators/eslint.config.ts | 10 +++++++++ packages/validators/package.json | 32 ++++++++++++++++++++++++++++ packages/validators/src/index.ts | 5 +++++ packages/validators/tsconfig.json | 5 +++++ 4 files changed, 52 insertions(+) create mode 100644 packages/validators/eslint.config.ts create mode 100644 packages/validators/package.json create mode 100644 packages/validators/src/index.ts create mode 100644 packages/validators/tsconfig.json diff --git a/packages/validators/eslint.config.ts b/packages/validators/eslint.config.ts new file mode 100644 index 0000000..4ba2143 --- /dev/null +++ b/packages/validators/eslint.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfig } from "@governance/eslint-config/base"; + +export default defineConfig( + { + ignores: ["dist/**"], + }, + baseConfig, +); diff --git a/packages/validators/package.json b/packages/validators/package.json new file mode 100644 index 0000000..61ece4f --- /dev/null +++ b/packages/validators/package.json @@ -0,0 +1,32 @@ +{ + "name": "@governance/validators", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./src/index.ts" + } + }, + "license": "MIT", + "scripts": { + "build": "tsc", + "clean": "git clean -xdf .cache .turbo dist node_modules", + "dev": "tsc", + "format": "prettier --check . --ignore-path ../../.gitignore", + "lint": "eslint --flag unstable_native_nodejs_ts_config", + "typecheck": "tsc --noEmit --emitDeclarationOnly false" + }, + "dependencies": { + "zod": "catalog:" + }, + "devDependencies": { + "@governance/eslint-config": "workspace:*", + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "eslint": "catalog:", + "prettier": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/packages/validators/src/index.ts b/packages/validators/src/index.ts new file mode 100644 index 0000000..0a48304 --- /dev/null +++ b/packages/validators/src/index.ts @@ -0,0 +1,5 @@ +import { z } from "zod/v4"; + +export const unused = z + .string() + .describe(`non-drizzle zod validation should be done in this package`); diff --git a/packages/validators/tsconfig.json b/packages/validators/tsconfig.json new file mode 100644 index 0000000..7effb17 --- /dev/null +++ b/packages/validators/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@governance/tsconfig/compiled-package.json", + "include": ["src"], + "exclude": ["node_modules"] +} From ce6c8d08b2f0823c8f39800235a46a3e535be8fc Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:16:01 +0300 Subject: [PATCH 07/14] db package init --- packages/db/drizzle.config.ts | 14 +++++++++ packages/db/eslint.config.ts | 10 +++++++ packages/db/package.json | 47 ++++++++++++++++++++++++++++++ packages/db/src/auth-schema.ts | 53 ++++++++++++++++++++++++++++++++++ packages/db/src/client.ts | 10 +++++++ packages/db/src/index.ts | 2 ++ packages/db/src/schema.ts | 25 ++++++++++++++++ packages/db/tsconfig.json | 5 ++++ 8 files changed, 166 insertions(+) create mode 100644 packages/db/drizzle.config.ts create mode 100644 packages/db/eslint.config.ts create mode 100644 packages/db/package.json create mode 100644 packages/db/src/auth-schema.ts create mode 100644 packages/db/src/client.ts create mode 100644 packages/db/src/index.ts create mode 100644 packages/db/src/schema.ts create mode 100644 packages/db/tsconfig.json diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts new file mode 100644 index 0000000..5b18085 --- /dev/null +++ b/packages/db/drizzle.config.ts @@ -0,0 +1,14 @@ +import type { Config } from "drizzle-kit"; + +if (!process.env.POSTGRES_URL) { + throw new Error("Missing POSTGRES_URL"); +} + +const nonPoolingUrl = process.env.POSTGRES_URL.replace(":6543", ":5432"); + +export default { + schema: "./src/schema.ts", + dialect: "postgresql", + dbCredentials: { url: nonPoolingUrl }, + casing: "snake_case", +} satisfies Config; diff --git a/packages/db/eslint.config.ts b/packages/db/eslint.config.ts new file mode 100644 index 0000000..4ba2143 --- /dev/null +++ b/packages/db/eslint.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfig } from "@governance/eslint-config/base"; + +export default defineConfig( + { + ignores: ["dist/**"], + }, + baseConfig, +); diff --git a/packages/db/package.json b/packages/db/package.json new file mode 100644 index 0000000..f3d4928 --- /dev/null +++ b/packages/db/package.json @@ -0,0 +1,47 @@ +{ + "name": "@governance/db", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./src/index.ts" + }, + "./client": { + "types": "./dist/client.d.ts", + "default": "./src/client.ts" + }, + "./schema": { + "types": "./dist/schema.d.ts", + "default": "./src/schema.ts" + } + }, + "license": "MIT", + "scripts": { + "build": "tsc", + "clean": "git clean -xdf .cache .turbo dist node_modules", + "dev": "tsc", + "format": "prettier --check . --ignore-path ../../.gitignore", + "lint": "eslint --flag unstable_native_nodejs_ts_config", + "push": "pnpm with-env drizzle-kit push", + "studio": "pnpm with-env drizzle-kit studio", + "typecheck": "tsc --noEmit --emitDeclarationOnly false", + "with-env": "dotenv -e ../../.env --" + }, + "dependencies": { + "@vercel/postgres": "^0.10.0", + "drizzle-orm": "^0.44.7", + "drizzle-zod": "^0.8.3", + "zod": "catalog:" + }, + "devDependencies": { + "@governance/eslint-config": "workspace:*", + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "drizzle-kit": "^0.31.5", + "eslint": "catalog:", + "prettier": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/packages/db/src/auth-schema.ts b/packages/db/src/auth-schema.ts new file mode 100644 index 0000000..0195bd9 --- /dev/null +++ b/packages/db/src/auth-schema.ts @@ -0,0 +1,53 @@ +import { pgTable } from "drizzle-orm/pg-core"; + +export const user = pgTable("user", (t) => ({ + id: t.text().primaryKey(), + name: t.text().notNull(), + email: t.text().notNull().unique(), + emailVerified: t.boolean().notNull(), + image: t.text(), + createdAt: t.timestamp().notNull(), + updatedAt: t.timestamp().notNull(), +})); + +export const session = pgTable("session", (t) => ({ + id: t.text().primaryKey(), + expiresAt: t.timestamp().notNull(), + token: t.text().notNull().unique(), + createdAt: t.timestamp().notNull(), + updatedAt: t.timestamp().notNull(), + ipAddress: t.text(), + userAgent: t.text(), + userId: t + .text() + .notNull() + .references(() => user.id, { onDelete: "cascade" }), +})); + +export const account = pgTable("account", (t) => ({ + id: t.text().primaryKey(), + accountId: t.text().notNull(), + providerId: t.text().notNull(), + userId: t + .text() + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + accessToken: t.text(), + refreshToken: t.text(), + idToken: t.text(), + accessTokenExpiresAt: t.timestamp(), + refreshTokenExpiresAt: t.timestamp(), + scope: t.text(), + password: t.text(), + createdAt: t.timestamp().notNull(), + updatedAt: t.timestamp().notNull(), +})); + +export const verification = pgTable("verification", (t) => ({ + id: t.text().primaryKey(), + identifier: t.text().notNull(), + value: t.text().notNull(), + expiresAt: t.timestamp().notNull(), + createdAt: t.timestamp(), + updatedAt: t.timestamp(), +})); diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts new file mode 100644 index 0000000..e23181e --- /dev/null +++ b/packages/db/src/client.ts @@ -0,0 +1,10 @@ +import { sql } from "@vercel/postgres"; +import { drizzle } from "drizzle-orm/vercel-postgres"; + +import * as schema from "./schema"; + +export const db = drizzle({ + client: sql, + schema, + casing: "snake_case", +}); diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts new file mode 100644 index 0000000..f0585be --- /dev/null +++ b/packages/db/src/index.ts @@ -0,0 +1,2 @@ +export * from "drizzle-orm/sql"; +export { alias } from "drizzle-orm/pg-core"; diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts new file mode 100644 index 0000000..b519825 --- /dev/null +++ b/packages/db/src/schema.ts @@ -0,0 +1,25 @@ +import { sql } from "drizzle-orm"; +import { pgTable } from "drizzle-orm/pg-core"; +import { createInsertSchema } from "drizzle-zod"; +import { z } from "zod/v4"; + +export const Post = pgTable("post", (t) => ({ + id: t.uuid().notNull().primaryKey().defaultRandom(), + title: t.varchar({ length: 256 }).notNull(), + content: t.text().notNull(), + createdAt: t.timestamp().defaultNow().notNull(), + updatedAt: t + .timestamp({ mode: "date", withTimezone: true }) + .$onUpdateFn(() => sql`now()`), +})); + +export const CreatePostSchema = createInsertSchema(Post, { + title: z.string().max(256), + content: z.string().max(256), +}).omit({ + id: true, + createdAt: true, + updatedAt: true, +}); + +export * from "./auth-schema"; diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json new file mode 100644 index 0000000..7effb17 --- /dev/null +++ b/packages/db/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@governance/tsconfig/compiled-package.json", + "include": ["src"], + "exclude": ["node_modules"] +} From 5f490da7503f85396528499e1e5a5f3ac90ab192 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:17:45 +0300 Subject: [PATCH 08/14] auth package init --- packages/auth/env.ts | 19 ++++++++++++ packages/auth/eslint.config.ts | 11 +++++++ packages/auth/package.json | 35 ++++++++++++++++++++++ packages/auth/script/auth-cli.ts | 27 +++++++++++++++++ packages/auth/src/index.ts | 50 ++++++++++++++++++++++++++++++++ packages/auth/tsconfig.json | 5 ++++ 6 files changed, 147 insertions(+) create mode 100644 packages/auth/env.ts create mode 100644 packages/auth/eslint.config.ts create mode 100644 packages/auth/package.json create mode 100644 packages/auth/script/auth-cli.ts create mode 100644 packages/auth/src/index.ts create mode 100644 packages/auth/tsconfig.json diff --git a/packages/auth/env.ts b/packages/auth/env.ts new file mode 100644 index 0000000..25c1354 --- /dev/null +++ b/packages/auth/env.ts @@ -0,0 +1,19 @@ +import { createEnv } from "@t3-oss/env-core"; +import { z } from "zod/v4"; + +export function authEnv() { + return createEnv({ + server: { + AUTH_DISCORD_ID: z.string().min(1), + AUTH_DISCORD_SECRET: z.string().min(1), + AUTH_SECRET: + process.env.NODE_ENV === "production" + ? z.string().min(1) + : z.string().min(1).optional(), + NODE_ENV: z.enum(["development", "production"]).optional(), + }, + runtimeEnv: process.env, + skipValidation: + !!process.env.CI || process.env.npm_lifecycle_event === "lint", + }); +} diff --git a/packages/auth/eslint.config.ts b/packages/auth/eslint.config.ts new file mode 100644 index 0000000..82368a8 --- /dev/null +++ b/packages/auth/eslint.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfig, restrictEnvAccess } from "@governance/eslint-config/base"; + +export default defineConfig( + { + ignores: ["script/**"], + }, + baseConfig, + restrictEnvAccess, +); diff --git a/packages/auth/package.json b/packages/auth/package.json new file mode 100644 index 0000000..7b30360 --- /dev/null +++ b/packages/auth/package.json @@ -0,0 +1,35 @@ +{ + "name": "@governance/auth", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts", + "./middleware": "./src/middleware.ts", + "./client": "./src/client.ts", + "./env": "./env.ts" + }, + "license": "MIT", + "scripts": { + "clean": "git clean -xdf .cache .turbo dist node_modules", + "format": "prettier --check . --ignore-path ../../.gitignore", + "lint": "eslint --flag unstable_native_nodejs_ts_config", + "generate": "dotenv -e ../../.env -- pnpx @better-auth/cli generate --config script/auth-cli.ts --output ../db/src/auth-schema.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@governance/db": "workspace:*", + "@t3-oss/env-core": "^0.13.8", + "better-auth": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@better-auth/cli": "catalog:", + "@governance/eslint-config": "workspace:*", + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "eslint": "catalog:", + "prettier": "catalog:", + "typescript": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/packages/auth/script/auth-cli.ts b/packages/auth/script/auth-cli.ts new file mode 100644 index 0000000..e9d509e --- /dev/null +++ b/packages/auth/script/auth-cli.ts @@ -0,0 +1,27 @@ +/** + * @fileoverview Better Auth CLI Configuration + * + * This file is used exclusively by the Better Auth CLI to generate database schemas. + * DO NOT USE THIS FILE DIRECTLY IN YOUR APPLICATION. + * + * This configuration is consumed by the CLI command: + * `pnpx @better-auth/cli generate --config script/auth-cli.ts --output ../db/src/auth-schema.ts` + * + * For actual authentication usage, import from "../src/index.ts" instead. + */ + +import { initAuth } from "../src/index"; + +/** + * CLI-only authentication configuration for schema generation. + * + * @warning This configuration is NOT intended for runtime use. + * @warning Use the main auth configuration from "../src/index.ts" for your application. + */ +export const auth = initAuth({ + baseUrl: "http://localhost:3000", + productionUrl: "http://localhost:3000", + secret: "secret", + discordClientId: "1234567890", + discordClientSecret: "1234567890", +}); diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts new file mode 100644 index 0000000..82d808b --- /dev/null +++ b/packages/auth/src/index.ts @@ -0,0 +1,50 @@ +import type { BetterAuthOptions, BetterAuthPlugin } from "better-auth"; +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { oAuthProxy } from "better-auth/plugins"; + +import { db } from "@governance/db/client"; + +export function initAuth< + TExtraPlugins extends BetterAuthPlugin[] = [], +>(options: { + baseUrl: string; + productionUrl: string; + secret: string | undefined; + + discordClientId: string; + discordClientSecret: string; + extraPlugins?: TExtraPlugins; +}) { + const config = { + database: drizzleAdapter(db, { + provider: "pg", + }), + baseURL: options.baseUrl, + secret: options.secret, + plugins: [ + oAuthProxy({ + productionURL: options.productionUrl, + }), + + ...(options.extraPlugins ?? []), + ], + socialProviders: { + discord: { + clientId: options.discordClientId, + clientSecret: options.discordClientSecret, + redirectURI: `${options.productionUrl}/api/auth/callback/discord`, + }, + }, + onAPIError: { + onError(error, ctx) { + console.error("BETTER AUTH API ERROR", error, ctx); + }, + }, + } satisfies BetterAuthOptions; + + return betterAuth(config); +} + +export type Auth = ReturnType; +export type Session = Auth["$Infer"]["Session"]; diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json new file mode 100644 index 0000000..3592780 --- /dev/null +++ b/packages/auth/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@governance/tsconfig/base.json", + "include": ["src", "*.ts"], + "exclude": ["node_modules"] +} From c6ffdc98ce2cdd1c10e581c4fc750045d1f35a29 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:18:06 +0300 Subject: [PATCH 09/14] fixing env.ts --- packages/auth/env.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/auth/env.ts b/packages/auth/env.ts index 25c1354..6b40d33 100644 --- a/packages/auth/env.ts +++ b/packages/auth/env.ts @@ -4,8 +4,6 @@ import { z } from "zod/v4"; export function authEnv() { return createEnv({ server: { - AUTH_DISCORD_ID: z.string().min(1), - AUTH_DISCORD_SECRET: z.string().min(1), AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string().min(1) From 287511e6981f1d727930b1c14bb961ecd54aa6d5 Mon Sep 17 00:00:00 2001 From: AF_Askar <68832286+abo3skr2019@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:18:53 +0300 Subject: [PATCH 10/14] shadcn init --- packages/ui/components.json | 17 ++ packages/ui/eslint.config.ts | 12 ++ packages/ui/package.json | 47 ++++++ packages/ui/src/button.tsx | 57 +++++++ packages/ui/src/dropdown-menu.tsx | 242 +++++++++++++++++++++++++++++ packages/ui/src/field.tsx | 249 ++++++++++++++++++++++++++++++ packages/ui/src/index.ts | 4 + packages/ui/src/input.tsx | 21 +++ packages/ui/src/label.tsx | 21 +++ packages/ui/src/separator.tsx | 25 +++ packages/ui/src/theme.tsx | 184 ++++++++++++++++++++++ packages/ui/src/toast.tsx | 27 ++++ packages/ui/tsconfig.json | 10 ++ 13 files changed, 916 insertions(+) create mode 100644 packages/ui/components.json create mode 100644 packages/ui/eslint.config.ts create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/button.tsx create mode 100644 packages/ui/src/dropdown-menu.tsx create mode 100644 packages/ui/src/field.tsx create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/src/input.tsx create mode 100644 packages/ui/src/label.tsx create mode 100644 packages/ui/src/separator.tsx create mode 100644 packages/ui/src/theme.tsx create mode 100644 packages/ui/src/toast.tsx create mode 100644 packages/ui/tsconfig.json diff --git a/packages/ui/components.json b/packages/ui/components.json new file mode 100644 index 0000000..72c5e4e --- /dev/null +++ b/packages/ui/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "unused.css", + "baseColor": "zinc", + "cssVariables": true + }, + "aliases": { + "utils": "@governance/ui", + "components": "src/", + "ui": "src/" + } +} diff --git a/packages/ui/eslint.config.ts b/packages/ui/eslint.config.ts new file mode 100644 index 0000000..9bf949f --- /dev/null +++ b/packages/ui/eslint.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfig } from "@governance/eslint-config/base"; +import { reactConfig } from "@governance/eslint-config/react"; + +export default defineConfig( + { + ignores: ["dist/**"], + }, + baseConfig, + reactConfig, +); diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..fff10b0 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,47 @@ +{ + "name": "@governance/ui", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts", + "./button": "./src/button.tsx", + "./dropdown-menu": "./src/dropdown-menu.tsx", + "./field": "./src/field.tsx", + "./input": "./src/input.tsx", + "./label": "./src/label.tsx", + "./separator": "./src/separator.tsx", + "./theme": "./src/theme.tsx", + "./toast": "./src/toast.tsx" + }, + "license": "MIT", + "scripts": { + "clean": "git clean -xdf .cache .turbo dist node_modules", + "format": "prettier --check . --ignore-path ../../.gitignore", + "lint": "eslint --flag unstable_native_nodejs_ts_config", + "typecheck": "tsc --noEmit --emitDeclarationOnly false", + "ui-add": "pnpm dlx shadcn@latest add && prettier src --write --list-different" + }, + "dependencies": { + "@radix-ui/react-icons": "^1.3.2", + "class-variance-authority": "^0.7.1", + "radix-ui": "^1.4.3", + "sonner": "^2.0.7", + "tailwind-merge": "^3.3.1" + }, + "devDependencies": { + "@governance/eslint-config": "workspace:*", + "@governance/prettier-config": "workspace:*", + "@governance/tsconfig": "workspace:*", + "@types/react": "catalog:react19", + "eslint": "catalog:", + "prettier": "catalog:", + "react": "catalog:react19", + "typescript": "catalog:", + "zod": "catalog:" + }, + "peerDependencies": { + "react": "catalog:react19", + "zod": "catalog:" + }, + "prettier": "@governance/prettier-config" +} diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx new file mode 100644 index 0000000..b7ccd66 --- /dev/null +++ b/packages/ui/src/button.tsx @@ -0,0 +1,57 @@ +import type { VariantProps } from "class-variance-authority"; +import { cva } from "class-variance-authority"; +import { Slot as SlotPrimitive } from "radix-ui"; + +import { cn } from "@governance/ui"; + +export const buttonVariants = cva( + "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs", + destructive: + "bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white shadow-xs", + outline: + "bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? SlotPrimitive.Slot : "button"; + + return ( + + ); +} diff --git a/packages/ui/src/dropdown-menu.tsx b/packages/ui/src/dropdown-menu.tsx new file mode 100644 index 0000000..f3fb849 --- /dev/null +++ b/packages/ui/src/dropdown-menu.tsx @@ -0,0 +1,242 @@ +"use client"; + +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons"; +import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"; + +import { cn } from "@governance/ui"; + +export function DropdownMenu({ + ...props +}: React.ComponentProps) { + return ; +} + +export function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +export function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean; + variant?: "default" | "destructive"; +}) { + return ( + + ); +} + +export function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +export function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +export function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean; +}) { + return ( + + ); +} + +export function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ); +} + +export function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return ; +} + +export function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean; +}) { + return ( + + {children} + + + ); +} + +export function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} diff --git a/packages/ui/src/field.tsx b/packages/ui/src/field.tsx new file mode 100644 index 0000000..0f6b687 --- /dev/null +++ b/packages/ui/src/field.tsx @@ -0,0 +1,249 @@ +"use client"; + +import type { VariantProps } from "class-variance-authority"; +import { useMemo } from "react"; +import { cva } from "class-variance-authority"; + +import { cn } from "@governance/ui"; +import { Label } from "@governance/ui/label"; +import { Separator } from "@governance/ui/separator"; + +export function FieldSet({ + className, + ...props +}: React.ComponentProps<"fieldset">) { + return ( +
[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", + className, + )} + {...props} + /> + ); +} + +export function FieldLegend({ + className, + variant = "legend", + ...props +}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) { + return ( + + ); +} + +export function FieldGroup({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
[data-slot=field-group]]:gap-4", + className, + )} + {...props} + /> + ); +} + +const fieldVariants = cva( + "group/field data-[invalid=true]:text-destructive flex w-full gap-3", + { + variants: { + orientation: { + vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"], + horizontal: [ + "flex-row items-center", + "[&>[data-slot=field-label]]:flex-auto", + "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + responsive: [ + "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto", + "@md/field-group:[&>[data-slot=field-label]]:flex-auto", + "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + }, + }, + defaultVariants: { + orientation: "vertical", + }, + }, +); + +export function Field({ + className, + orientation = "vertical", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ); +} + +export function FieldContent({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export function FieldLabel({ + className, + ...props +}: React.ComponentProps) { + return ( +