ui-syncup 0.3.13 → 0.4.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/ai-spec-workflow/SKILL.md +58 -0
- package/.agents/skills/ai-spec-workflow/references/AI_SPECIFICATION_WORKFLOW.md +1434 -0
- package/.agents/skills/ai-spec-workflow/references/templates/design-template.md +729 -0
- package/.agents/skills/ai-spec-workflow/references/templates/requirements-template.md +179 -0
- package/.agents/skills/ai-spec-workflow/references/templates/tasks-template.md +501 -0
- package/.agents/skills/animation-designer/SKILL.md +688 -0
- package/.agents/skills/animation-designer/manifest.yaml +44 -0
- package/.agents/skills/brainstorming/SKILL.md +54 -0
- package/.agents/skills/contract-driven-ui/SKILL.md +270 -0
- package/.agents/skills/dispatching-parallel-agents/SKILL.md +180 -0
- package/.agents/skills/executing-plans/SKILL.md +76 -0
- package/.agents/skills/executing-specs/SKILL.md +53 -0
- package/.agents/skills/finishing-a-development-branch/SKILL.md +200 -0
- package/.agents/skills/github-workflow-automation/SKILL.md +846 -0
- package/.agents/skills/react-best-practices/AGENTS.md +2249 -0
- package/.agents/skills/react-best-practices/README.md +123 -0
- package/.agents/skills/react-best-practices/SKILL.md +121 -0
- package/.agents/skills/react-best-practices/metadata.json +15 -0
- package/.agents/skills/react-best-practices/rules/_sections.md +46 -0
- package/.agents/skills/react-best-practices/rules/_template.md +28 -0
- package/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agents/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/.agents/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/.agents/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/.agents/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/.agents/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/.agents/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agents/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agents/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/.agents/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agents/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/.agents/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agents/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/.agents/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agents/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agents/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agents/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agents/skills/react-best-practices/rules/server-cache-react.md +26 -0
- package/.agents/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/.agents/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/.agents/skills/react-ui-patterns/SKILL.md +289 -0
- package/.agents/skills/receiving-code-review/SKILL.md +213 -0
- package/.agents/skills/requesting-code-review/SKILL.md +105 -0
- package/.agents/skills/requesting-code-review/code-reviewer.md +146 -0
- package/.agents/skills/reviewing-code/SKILL.md +28 -0
- package/.agents/skills/shadcn/SKILL.md +240 -0
- package/.agents/skills/shadcn/agents/openai.yml +5 -0
- package/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/.agents/skills/shadcn/cli.md +255 -0
- package/.agents/skills/shadcn/customization.md +202 -0
- package/.agents/skills/shadcn/evals/evals.json +47 -0
- package/.agents/skills/shadcn/mcp.md +94 -0
- package/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/.agents/skills/shadcn/rules/composition.md +195 -0
- package/.agents/skills/shadcn/rules/forms.md +192 -0
- package/.agents/skills/shadcn/rules/icons.md +101 -0
- package/.agents/skills/shadcn/rules/styling.md +162 -0
- package/.agents/skills/steering-creation/SKILL.md +221 -0
- package/.agents/skills/steering-creation/references/STEERING_CREATION_INSTRUCTION.md +850 -0
- package/.agents/skills/subagent-driven-development/SKILL.md +240 -0
- package/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +20 -0
- package/.agents/skills/subagent-driven-development/implementer-prompt.md +78 -0
- package/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/.agents/skills/systematic-debugging/SKILL.md +296 -0
- package/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/.agents/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/.agents/skills/systematic-debugging/find-polluter.sh +63 -0
- package/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/.agents/skills/systematic-debugging/test-academic.md +14 -0
- package/.agents/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/.agents/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/.agents/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/.agents/skills/test-driven-development/SKILL.md +371 -0
- package/.agents/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/.agents/skills/using-git-worktrees/SKILL.md +217 -0
- package/.agents/skills/using-superpowers/SKILL.md +87 -0
- package/.agents/skills/verification-before-completion/SKILL.md +139 -0
- package/.agents/skills/web-design-guidelines/SKILL.md +36 -0
- package/.agents/skills/writing-plans/SKILL.md +116 -0
- package/.agents/skills/writing-skills/SKILL.md +655 -0
- package/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/.agents/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/.agents/skills/writing-skills/persuasion-principles.md +187 -0
- package/.agents/skills/writing-skills/render-graphs.js +168 -0
- package/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/.ai/steering/product.md +51 -0
- package/.ai/steering/structure.md +275 -0
- package/.ai/steering/tech.md +188 -0
- package/.claude/agents/database-architect.md +96 -0
- package/.claude/agents/deployment-pipeline-architect.md +122 -0
- package/.claude/agents/nextjs-expert.md +69 -0
- package/.claude/agents/ui-design-expert.md +106 -0
- package/.claudeignore +69 -0
- package/.dockerignore +8 -0
- package/.env.development +86 -0
- package/.env.example +171 -0
- package/.env.production +139 -0
- package/.env.test +58 -0
- package/.gitattributes +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +20 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +23 -0
- package/.github/workflows/ci.yml +64 -0
- package/.github/workflows/release.yml +174 -0
- package/.nvmrc +1 -0
- package/.releaserc.json +18 -0
- package/.vercelignore +73 -0
- package/AGENTS.md +544 -0
- package/CHANGELOG.md +69 -0
- package/CODE_OF_CONDUCT.md +21 -0
- package/CONTRIBUTING.md +32 -0
- package/Dockerfile +84 -0
- package/LICENSE +21 -0
- package/README.md +328 -59
- package/SECURITY.md +16 -0
- package/bun.lock +3853 -0
- package/cli/README.md +94 -0
- package/cli/bun.lock +306 -0
- package/cli/index.ts +96 -0
- package/cli/package-lock.json +2157 -0
- package/cli/package.json +30 -0
- package/cli/src/commands/backup.ts +78 -0
- package/cli/src/commands/doctor.ts +82 -0
- package/cli/src/commands/init.ts +234 -0
- package/cli/src/commands/logs.ts +26 -0
- package/cli/src/commands/open.ts +23 -0
- package/cli/src/commands/remove.ts +44 -0
- package/cli/src/commands/restart.ts +21 -0
- package/cli/src/commands/restore.ts +90 -0
- package/cli/src/commands/start.ts +26 -0
- package/cli/src/commands/status.ts +25 -0
- package/cli/src/commands/stop.ts +20 -0
- package/cli/src/commands/upgrade.ts +28 -0
- package/cli/src/lib/docker.ts +40 -0
- package/cli/src/lib/env.ts +42 -0
- package/cli/src/lib/ui.ts +43 -0
- package/cli/tsconfig.json +13 -0
- package/cli/tsup.config.ts +12 -0
- package/components.json +24 -0
- package/docker/README.md +430 -0
- package/docker/compose.dev-minio.yml +30 -0
- package/docker/compose.dev.yml +39 -0
- package/docker/compose.local.yml +84 -0
- package/docker/compose.yml +153 -0
- package/docs/VERSIONING.md +101 -0
- package/docs/database/DRIZZLE_COMMANDS_EXPLAINED.md +1779 -0
- package/docs/database/DRIZZLE_ZOD_POSTGRESQL_INSTRUCTION.md +646 -0
- package/docs/database/MIGRATION_BEST_PRACTICES.md +601 -0
- package/docs/database/MIGRATION_ROLLBACK.md +1080 -0
- package/docs/database/MIGRATION_SYSTEM.md +165 -0
- package/docs/database/MIGRATION_TROUBLESHOOTING.md +881 -0
- package/docs/development/ENVIRONMENT_CONFIG.md +896 -0
- package/docs/development/LOCAL_DEVELOPMENT.md +456 -0
- package/docs/development/REMOTE_DATABASE_SETUP.md +786 -0
- package/docs/development/STORAGE_SETUP.md +207 -0
- package/docs/development/SUPABASE_LOCAL_SETUP.md +178 -0
- package/docs/development/TESTING.md +714 -0
- package/docs/feature-architectures/LOADING_ARCHITECTURE.md +343 -0
- package/docs/feature-architectures/NOTIFICATION_ARCHITECTURE.md +858 -0
- package/docs/feature-architectures/RATE_LIMIT_RESET.md +147 -0
- package/docs/feature-architectures/RBAC.md +1132 -0
- package/docs/feature-architectures/RESOURCE_LIMITS.md +69 -0
- package/docs/feature-architectures/SECURITY.md +284 -0
- package/docs/feature-architectures/WORKSPACES.md +278 -0
- package/docs/plans/admin-setup-wizard-routing-plan.md +623 -0
- package/drizzle/0000_purple_wilson_fisk.sql +360 -0
- package/drizzle/0001_drop_instance_public_url.sql +1 -0
- package/drizzle/meta/0000_snapshot.json +3118 -0
- package/drizzle/meta/_journal.json +20 -0
- package/drizzle.config.ts +13 -0
- package/eslint.config.mjs +44 -0
- package/install.sh +180 -0
- package/next.config.ts +91 -0
- package/package.json +128 -22
- package/playwright.config.ts +70 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/logo.svg +11 -0
- package/public/next.svg +1 -0
- package/public/playground/CPM-101/as-is-image.jpg +0 -0
- package/public/playground/CPM-101/to-be-image.jpg +0 -0
- package/public/playground/TEST-1/LinkedIn-skeleton-screen.png +0 -0
- package/public/playground/TEST-1/https___dev-to-uploads.s3.amazonaws.com_uploads_articles_vuahe90ka1mkx9aepmea.webp +0 -0
- package/public/playground/TEST-1/linkedin_skeletonscreen.jpg +0 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/__tests__/migrate.integration.test.ts +642 -0
- package/scripts/__tests__/migrate.property.test.ts +1714 -0
- package/scripts/__tests__/migrate.test.ts +536 -0
- package/scripts/admin-reset-password.ts +114 -0
- package/scripts/check-email-queue.ts +99 -0
- package/scripts/check-sessions.ts +50 -0
- package/scripts/db-pull-data.sh +73 -0
- package/scripts/force-verify-email.sh +13 -0
- package/scripts/migrate.ts +693 -0
- package/scripts/process-email-queue.ts +26 -0
- package/scripts/reset-db.ts +47 -0
- package/scripts/reset-rate-limit.sh +26 -0
- package/scripts/reset-remote-db.sql +31 -0
- package/scripts/retry-failed-emails.ts +67 -0
- package/scripts/seed.ts +605 -0
- package/scripts/setup-monitoring.sh +440 -0
- package/scripts/sync-migration-tracking.ts +113 -0
- package/scripts/test-ci-error-handling.sh +237 -0
- package/scripts/test-ci-workflow.sh +200 -0
- package/scripts/test-migration.sh +151 -0
- package/scripts/validate-env.ts +25 -0
- package/scripts/validate-migration-system.ts +566 -0
- package/scripts/verify-ci-status-reporting.sh +206 -0
- package/scripts/verify-user-email.sql +22 -0
- package/scripts/verify-vercel-integration.ts +292 -0
- package/seed_data.md +54 -0
- package/src/app/(protected)/(team)/(routes)/[projectSlug]/error.tsx +89 -0
- package/src/app/(protected)/(team)/(routes)/[projectSlug]/loading.tsx +101 -0
- package/src/app/(protected)/(team)/(routes)/[projectSlug]/page.tsx +91 -0
- package/src/app/(protected)/(team)/(routes)/issue/[issueKey]/README.md +192 -0
- package/src/app/(protected)/(team)/(routes)/issue/[issueKey]/error.tsx +58 -0
- package/src/app/(protected)/(team)/(routes)/issue/[issueKey]/loading.tsx +14 -0
- package/src/app/(protected)/(team)/(routes)/issue/[issueKey]/not-found.tsx +47 -0
- package/src/app/(protected)/(team)/(routes)/issue/[issueKey]/page.tsx +91 -0
- package/src/app/(protected)/(team)/projects/page.tsx +16 -0
- package/src/app/(protected)/(team)/team/settings/(section)/instance/page.tsx +52 -0
- package/src/app/(protected)/(team)/team/settings/(section)/integrations/loading.tsx +5 -0
- package/src/app/(protected)/(team)/team/settings/(section)/integrations/page.tsx +23 -0
- package/src/app/(protected)/(team)/team/settings/(section)/members/loading.tsx +5 -0
- package/src/app/(protected)/(team)/team/settings/(section)/members/page.tsx +35 -0
- package/src/app/(protected)/(team)/team/settings/layout.tsx +72 -0
- package/src/app/(protected)/(team)/team/settings/loading.tsx +5 -0
- package/src/app/(protected)/(team)/team/settings/page.tsx +71 -0
- package/src/app/(protected)/dev/auth/README.md +151 -0
- package/src/app/(protected)/dev/auth/page.tsx +590 -0
- package/src/app/(protected)/layout.test.tsx +209 -0
- package/src/app/(protected)/layout.tsx +28 -0
- package/src/app/(protected)/onboarding/page.tsx +27 -0
- package/src/app/(protected)/settings/integrations/page.tsx +23 -0
- package/src/app/(protected)/settings/layout.tsx +26 -0
- package/src/app/(protected)/settings/notifications/page.tsx +26 -0
- package/src/app/(protected)/settings/other/page.tsx +23 -0
- package/src/app/(protected)/settings/page.tsx +23 -0
- package/src/app/(protected)/settings/preferences/page.tsx +23 -0
- package/src/app/(protected)/settings/security/page.tsx +37 -0
- package/src/app/(public)/forgot-password/page.tsx +20 -0
- package/src/app/(public)/invite/project/[token]/error.tsx +50 -0
- package/src/app/(public)/invite/project/[token]/loading.tsx +39 -0
- package/src/app/(public)/invite/project/[token]/page.tsx +156 -0
- package/src/app/(public)/layout.tsx +9 -0
- package/src/app/(public)/privacy-policy/page.tsx +12 -0
- package/src/app/(public)/reset-password/page.tsx +37 -0
- package/src/app/(public)/setup/__tests__/page.test.tsx +30 -0
- package/src/app/(public)/setup/page.tsx +17 -0
- package/src/app/(public)/share/issue/[token]/page.tsx +51 -0
- package/src/app/(public)/sign-in/page.tsx +55 -0
- package/src/app/(public)/sign-up/page.tsx +23 -0
- package/src/app/(public)/verify-email/page.tsx +22 -0
- package/src/app/(public)/verify-email-confirm/page.tsx +40 -0
- package/src/app/api/auth/[...all]/route.ts +6 -0
- package/src/app/api/auth/delete-account/route.ts +134 -0
- package/src/app/api/auth/dev/force-verify/route.ts +180 -0
- package/src/app/api/auth/dev/reset-rate-limit/route.ts +144 -0
- package/src/app/api/auth/dev/sessions/route.ts +172 -0
- package/src/app/api/auth/forgot-password/__tests__/forgot-password.property.test.ts +397 -0
- package/src/app/api/auth/forgot-password/route.ts +277 -0
- package/src/app/api/auth/logout/route.ts +115 -0
- package/src/app/api/auth/me/route.ts +123 -0
- package/src/app/api/auth/providers/__tests__/route.test.ts +236 -0
- package/src/app/api/auth/providers/route.ts +119 -0
- package/src/app/api/auth/resend-verification/route.ts +262 -0
- package/src/app/api/auth/reset-password/__tests__/reset-password.property.test.ts +493 -0
- package/src/app/api/auth/reset-password/__tests__/route.test.ts +284 -0
- package/src/app/api/auth/reset-password/route.ts +251 -0
- package/src/app/api/auth/verify-email/route.ts +232 -0
- package/src/app/api/example-cors/route.ts +61 -0
- package/src/app/api/health/route.ts +14 -0
- package/src/app/api/invite/project/[token]/__tests__/accept-invitation.integration.test.ts +348 -0
- package/src/app/api/invite/project/[token]/decline/route.ts +99 -0
- package/src/app/api/invite/project/[token]/route.ts +269 -0
- package/src/app/api/issues/[issueId]/activities/route.ts +213 -0
- package/src/app/api/issues/[issueId]/attachments/[attachmentId]/annotations/[annotationId]/comments/[commentId]/route.ts +486 -0
- package/src/app/api/issues/[issueId]/attachments/[attachmentId]/annotations/[annotationId]/comments/route.ts +283 -0
- package/src/app/api/issues/[issueId]/attachments/[attachmentId]/annotations/[annotationId]/read/route.ts +242 -0
- package/src/app/api/issues/[issueId]/attachments/[attachmentId]/annotations/[annotationId]/route.ts +534 -0
- package/src/app/api/issues/[issueId]/attachments/[attachmentId]/annotations/route.ts +514 -0
- package/src/app/api/issues/[issueId]/attachments/[attachmentId]/route.ts +161 -0
- package/src/app/api/issues/[issueId]/attachments/route.ts +376 -0
- package/src/app/api/issues/[issueId]/route.ts +516 -0
- package/src/app/api/notifications/[id]/read/route.ts +131 -0
- package/src/app/api/notifications/__tests__/notifications.integration.test.ts +350 -0
- package/src/app/api/notifications/read-all/route.ts +72 -0
- package/src/app/api/notifications/route.ts +148 -0
- package/src/app/api/notifications/unread-count/route.ts +77 -0
- package/src/app/api/projects/[id]/activities/route.ts +174 -0
- package/src/app/api/projects/[id]/invitations/[invitationId]/resend/route.ts +99 -0
- package/src/app/api/projects/[id]/invitations/[invitationId]/route.ts +96 -0
- package/src/app/api/projects/[id]/invitations/route.ts +254 -0
- package/src/app/api/projects/[id]/issues/route.ts +452 -0
- package/src/app/api/projects/[id]/join/route.ts +207 -0
- package/src/app/api/projects/[id]/members/[memberId]/route.ts +364 -0
- package/src/app/api/projects/[id]/members/me/route.ts +121 -0
- package/src/app/api/projects/[id]/members/route.ts +129 -0
- package/src/app/api/projects/[id]/route.ts +476 -0
- package/src/app/api/projects/route.ts +394 -0
- package/src/app/api/setup/admin/route.ts +255 -0
- package/src/app/api/setup/complete/__tests__/route.test.ts +60 -0
- package/src/app/api/setup/complete/route.ts +244 -0
- package/src/app/api/setup/config/route.ts +195 -0
- package/src/app/api/setup/export/route.ts +111 -0
- package/src/app/api/setup/health/route.ts +74 -0
- package/src/app/api/setup/import/route.ts +154 -0
- package/src/app/api/setup/status/route.ts +82 -0
- package/src/app/api/setup/workspace/route.ts +252 -0
- package/src/app/api/teams/[teamId]/export/route.ts +115 -0
- package/src/app/api/teams/[teamId]/invitations/[invitationId]/resend/route.ts +132 -0
- package/src/app/api/teams/[teamId]/invitations/[invitationId]/route.ts +117 -0
- package/src/app/api/teams/[teamId]/invitations/route.ts +363 -0
- package/src/app/api/teams/[teamId]/members/[userId]/route.ts +335 -0
- package/src/app/api/teams/[teamId]/members/route.ts +184 -0
- package/src/app/api/teams/[teamId]/members/search/route.ts +202 -0
- package/src/app/api/teams/[teamId]/route.ts +423 -0
- package/src/app/api/teams/[teamId]/switch/route.ts +140 -0
- package/src/app/api/teams/[teamId]/transfer-ownership/route.ts +212 -0
- package/src/app/api/teams/invitations/[token]/accept/route.ts +140 -0
- package/src/app/api/teams/invitations/by-id/[id]/accept/route.ts +98 -0
- package/src/app/api/teams/invitations/by-id/[id]/decline/route.ts +90 -0
- package/src/app/api/teams/route.ts +278 -0
- package/src/app/api/uploads/media/route.ts +118 -0
- package/src/app/api/uploads/presigned/route.ts +49 -0
- package/src/app/api/user/linked-accounts/route.ts +35 -0
- package/src/app/email-preview/page.tsx +11 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/global-error.tsx +21 -0
- package/src/app/layout.tsx +50 -0
- package/src/app/page.tsx +5 -0
- package/src/components/icons/atlassian-icon.tsx +22 -0
- package/src/components/icons/index.ts +1 -0
- package/src/components/layout/SIDEBAR_LAYOUT_BEST_PRACTICES.md +240 -0
- package/src/components/layout/app-shell-header-store.tsx +20 -0
- package/src/components/layout/app-shell-skeleton.tsx +89 -0
- package/src/components/layout/app-shell-wrapper.tsx +32 -0
- package/src/components/layout/app-shell.test.tsx +155 -0
- package/src/components/layout/app-shell.tsx +100 -0
- package/src/components/shared/headers/app-header-configurator.tsx +42 -0
- package/src/components/shared/headers/app-header.tsx +103 -0
- package/src/components/shared/headers/header-user-menu.tsx +247 -0
- package/src/components/shared/headers/index.ts +44 -0
- package/src/components/shared/headers/page-header.tsx +25 -0
- package/src/components/shared/notifications/__tests__/notification-bell.test.tsx +159 -0
- package/src/components/shared/notifications/__tests__/notification-dropdown.test.tsx +296 -0
- package/src/components/shared/notifications/__tests__/notification-item.test.tsx +328 -0
- package/src/components/shared/notifications/index.ts +45 -0
- package/src/components/shared/notifications/notification-actions.tsx +295 -0
- package/src/components/shared/notifications/notification-bell-button.tsx +77 -0
- package/src/components/shared/notifications/notification-dropdown.tsx +160 -0
- package/src/components/shared/notifications/notification-group-item.tsx +268 -0
- package/src/components/shared/notifications/notification-item.tsx +193 -0
- package/src/components/shared/notifications/notification-load-more.tsx +50 -0
- package/src/components/shared/notifications/notification-panel.tsx +49 -0
- package/src/components/shared/notifications/utils.tsx +127 -0
- package/src/components/shared/permission-guard/index.ts +1 -0
- package/src/components/shared/permission-guard/permission-tooltip.tsx +45 -0
- package/src/components/shared/relative-time.tsx +53 -0
- package/src/components/shared/section-container.tsx +32 -0
- package/src/components/shared/service-status-banner.tsx +121 -0
- package/src/components/shared/settings-sidebar/index.ts +2 -0
- package/src/components/shared/settings-sidebar/team-setting-aside.tsx +97 -0
- package/src/components/shared/settings-sidebar/user-settings-aside.tsx +66 -0
- package/src/components/shared/sidebar/app-sidebar.tsx +146 -0
- package/src/components/shared/sidebar/index.ts +36 -0
- package/src/components/shared/sidebar/sidebar-main.tsx +81 -0
- package/src/components/shared/sidebar/sidebar-project.tsx +61 -0
- package/src/components/shared/sidebar/sidebar-team-avatar.tsx +126 -0
- package/src/components/shared/sidebar/sidebar-team-switcher.tsx +185 -0
- package/src/components/shared/sidebar/type.ts +97 -0
- package/src/components/ui/alert-dialog.tsx +157 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar-upload.tsx +147 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +244 -0
- package/src/components/ui/image-cropper-dialog.tsx +167 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/optimized-image.tsx +220 -0
- package/src/components/ui/pagination.tsx +127 -0
- package/src/components/ui/popover.tsx +48 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +187 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/sidebar.tsx +733 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +40 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/components/ui/textarea.tsx +23 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/config/__tests__/workspace.property.test.ts +40 -0
- package/src/config/auth.ts +62 -0
- package/src/config/integrations.ts +126 -0
- package/src/config/quotas.ts +20 -0
- package/src/config/roles.ts +463 -0
- package/src/config/settings-nav.ts +39 -0
- package/src/config/team-settings-nav.ts +37 -0
- package/src/config/user-settings-nav.ts +42 -0
- package/src/config/version.ts +1 -0
- package/src/config/workspace.ts +64 -0
- package/src/features/annotations/README.md +283 -0
- package/src/features/annotations/api/annotations-api.ts +194 -0
- package/src/features/annotations/api/comments-api.ts +147 -0
- package/src/features/annotations/api/index.ts +71 -0
- package/src/features/annotations/api/save-annotation.ts +150 -0
- package/src/features/annotations/api/schemas.ts +142 -0
- package/src/features/annotations/components/annotated-attachment-view.tsx +576 -0
- package/src/features/annotations/components/annotation-action-sheet.tsx +140 -0
- package/src/features/annotations/components/annotation-annotations-panel.tsx +213 -0
- package/src/features/annotations/components/annotation-box.tsx +539 -0
- package/src/features/annotations/components/annotation-canvas.tsx +534 -0
- package/src/features/annotations/components/annotation-comment-input.tsx +145 -0
- package/src/features/annotations/components/annotation-context-menu.tsx +164 -0
- package/src/features/annotations/components/annotation-drawer.tsx +231 -0
- package/src/features/annotations/components/annotation-layer.tsx +271 -0
- package/src/features/annotations/components/annotation-pin.tsx +318 -0
- package/src/features/annotations/components/annotation-popover.tsx +562 -0
- package/src/features/annotations/components/annotation-thread-panel.tsx +485 -0
- package/src/features/annotations/components/annotation-thread-preview.tsx +195 -0
- package/src/features/annotations/components/annotation-toolbar.tsx +244 -0
- package/src/features/annotations/components/keyboard-shortcuts-modal.tsx +79 -0
- package/src/features/annotations/docs/ANNOTATIONS_ARCHITECTURE.md +67 -0
- package/src/features/annotations/docs/ANNOTATION_SAVE_ARCHITECTURE.md +422 -0
- package/src/features/annotations/docs/ANNOTATION_SAVE_FEATURE.md +408 -0
- package/src/features/annotations/docs/BOX_ANNOTATION_GUIDE.md +542 -0
- package/src/features/annotations/docs/NEXTSTEP.md +28 -0
- package/src/features/annotations/docs/STALE_CLOSURE_FIX.md +344 -0
- package/src/features/annotations/docs/UNDO_REDO_QUICK_START.md +545 -0
- package/src/features/annotations/docs/local_first_canvas_autosave_architecture.md +674 -0
- package/src/features/annotations/examples/complete-example.tsx +266 -0
- package/src/features/annotations/examples/save-annotation-example.tsx +309 -0
- package/src/features/annotations/hooks/__tests__/use-annotation-permissions.property.test.tsx +493 -0
- package/src/features/annotations/hooks/index.ts +36 -0
- package/src/features/annotations/hooks/use-annotation-batch-save.ts +109 -0
- package/src/features/annotations/hooks/use-annotation-comments.ts +353 -0
- package/src/features/annotations/hooks/use-annotation-drafts.ts +137 -0
- package/src/features/annotations/hooks/use-annotation-edit-state.ts +99 -0
- package/src/features/annotations/hooks/use-annotation-history-tracker.ts +159 -0
- package/src/features/annotations/hooks/use-annotation-integration.ts +916 -0
- package/src/features/annotations/hooks/use-annotation-permissions.ts +210 -0
- package/src/features/annotations/hooks/use-annotation-popover.ts +175 -0
- package/src/features/annotations/hooks/use-annotation-save.ts +208 -0
- package/src/features/annotations/hooks/use-annotation-tools.ts +237 -0
- package/src/features/annotations/hooks/use-annotations-with-history.ts +332 -0
- package/src/features/annotations/hooks/use-auto-save.ts +94 -0
- package/src/features/annotations/index.ts +111 -0
- package/src/features/annotations/types/annotation.ts +201 -0
- package/src/features/annotations/types/index.ts +28 -0
- package/src/features/annotations/utils/history-manager.ts +73 -0
- package/src/features/annotations/utils/index.ts +2 -0
- package/src/features/annotations/utils/map-attachments-to-threads.ts +28 -0
- package/src/features/annotations/utils/position-comment-input.ts +136 -0
- package/src/features/annotations/utils/re-sequence-labels.ts +92 -0
- package/src/features/annotations/utils/validate-annotation-label.ts +120 -0
- package/src/features/auth/api/types.ts +101 -0
- package/src/features/auth/components/__tests__/role-gate.test.tsx +448 -0
- package/src/features/auth/components/__tests__/social-login-buttons.test.tsx +313 -0
- package/src/features/auth/components/auth-card.tsx +36 -0
- package/src/features/auth/components/forgot-password-form.tsx +115 -0
- package/src/features/auth/components/index.ts +14 -0
- package/src/features/auth/components/invite-code-input.tsx +155 -0
- package/src/features/auth/components/invited-user-form.tsx +309 -0
- package/src/features/auth/components/onboarding-form.tsx +195 -0
- package/src/features/auth/components/password-strength-indicator.tsx +113 -0
- package/src/features/auth/components/reset-password-form.tsx +138 -0
- package/src/features/auth/components/role-gate.tsx +124 -0
- package/src/features/auth/components/self-registration-choice.tsx +153 -0
- package/src/features/auth/components/sign-in-form.tsx +159 -0
- package/src/features/auth/components/sign-up-form.tsx +158 -0
- package/src/features/auth/components/social-login-buttons.tsx +219 -0
- package/src/features/auth/hooks/__tests__/use-onboarding.test.tsx +109 -0
- package/src/features/auth/hooks/__tests__/use-session.test.tsx +160 -0
- package/src/features/auth/hooks/index.ts +15 -0
- package/src/features/auth/hooks/use-accept-invitation.ts +194 -0
- package/src/features/auth/hooks/use-delete-account.ts +86 -0
- package/src/features/auth/hooks/use-force-verify.ts +89 -0
- package/src/features/auth/hooks/use-forgot-password.ts +144 -0
- package/src/features/auth/hooks/use-link-account.ts +78 -0
- package/src/features/auth/hooks/use-linked-accounts.ts +88 -0
- package/src/features/auth/hooks/use-onboarding.ts +159 -0
- package/src/features/auth/hooks/use-resend-verification.ts +139 -0
- package/src/features/auth/hooks/use-reset-password.ts +151 -0
- package/src/features/auth/hooks/use-reset-rate-limit.ts +56 -0
- package/src/features/auth/hooks/use-self-registration.ts +202 -0
- package/src/features/auth/hooks/use-session.ts +81 -0
- package/src/features/auth/hooks/use-sessions.ts +59 -0
- package/src/features/auth/hooks/use-sign-in.ts +234 -0
- package/src/features/auth/hooks/use-sign-out.ts +88 -0
- package/src/features/auth/hooks/use-sign-up.ts +194 -0
- package/src/features/auth/hooks/use-unlink-account.ts +100 -0
- package/src/features/auth/hooks/use-verify-email-token.ts +125 -0
- package/src/features/auth/index.ts +75 -0
- package/src/features/auth/screens/forgot-password-screen.tsx +33 -0
- package/src/features/auth/screens/index.ts +7 -0
- package/src/features/auth/screens/onboarding-screen.tsx +49 -0
- package/src/features/auth/screens/reset-password-screen.tsx +33 -0
- package/src/features/auth/screens/sign-in-screen.tsx +61 -0
- package/src/features/auth/screens/sign-up-screen.tsx +37 -0
- package/src/features/auth/screens/verify-email-confirm-screen.tsx +286 -0
- package/src/features/auth/screens/verify-email-screen.tsx +146 -0
- package/src/features/auth/types/index.ts +14 -0
- package/src/features/auth/utils/__tests__/validators.test.ts +331 -0
- package/src/features/auth/utils/password-strength.ts +129 -0
- package/src/features/auth/utils/validators.ts +124 -0
- package/src/features/email-preview/actions/render-email.ts +21 -0
- package/src/features/email-preview/screens/email-preview-screen.tsx +81 -0
- package/src/features/folder-scaffold-template/index.ts +0 -0
- package/src/features/instance-settings/components/index.ts +6 -0
- package/src/features/instance-settings/components/instance-settings-form.tsx +180 -0
- package/src/features/instance-settings/components/instance-status-display.tsx +158 -0
- package/src/features/instance-settings/index.ts +7 -0
- package/src/features/instance-settings/screens/index.ts +5 -0
- package/src/features/instance-settings/screens/instance-settings-screen.tsx +59 -0
- package/src/features/issues/README.md +330 -0
- package/src/features/issues/api/create-issue.ts +19 -0
- package/src/features/issues/api/delete-issue.ts +27 -0
- package/src/features/issues/api/get-issue-activities.ts +58 -0
- package/src/features/issues/api/get-issue-details.ts +25 -0
- package/src/features/issues/api/get-project-issues-server.ts +44 -0
- package/src/features/issues/api/get-project-issues.ts +21 -0
- package/src/features/issues/api/index.ts +44 -0
- package/src/features/issues/api/update-issue.ts +31 -0
- package/src/features/issues/api/upload-attachment.ts +81 -0
- package/src/features/issues/components/activity-timeline.tsx +440 -0
- package/src/features/issues/components/canvas-state-indicator.tsx +90 -0
- package/src/features/issues/components/centered-canvas-view.tsx +739 -0
- package/src/features/issues/components/image-selector.tsx +123 -0
- package/src/features/issues/components/image-upload-zone.tsx +262 -0
- package/src/features/issues/components/infinite-canvas-background.tsx +163 -0
- package/src/features/issues/components/inline-editable-select.tsx +173 -0
- package/src/features/issues/components/inline-editable-text.tsx +225 -0
- package/src/features/issues/components/inline-editable-textarea.tsx +219 -0
- package/src/features/issues/components/inline-editable-user-select.tsx +202 -0
- package/src/features/issues/components/issue-deletion-dialog.tsx +142 -0
- package/src/features/issues/components/issue-details-panel.tsx +101 -0
- package/src/features/issues/components/issues-create-dialog.tsx +578 -0
- package/src/features/issues/components/issues-list-filter.tsx +312 -0
- package/src/features/issues/components/issues-list.tsx +151 -0
- package/src/features/issues/components/issues-priority-badge.tsx +77 -0
- package/src/features/issues/components/issues-status-badge.tsx +100 -0
- package/src/features/issues/components/metadata-section.tsx +389 -0
- package/src/features/issues/components/optimized-attachment-view.tsx +528 -0
- package/src/features/issues/components/optimized-image.tsx +257 -0
- package/src/features/issues/components/panel-header.tsx +186 -0
- package/src/features/issues/components/preload.ts +31 -0
- package/src/features/issues/components/priority-selector.tsx +101 -0
- package/src/features/issues/components/responsive-issue-layout-skeleton.tsx +139 -0
- package/src/features/issues/components/responsive-issue-layout.tsx +617 -0
- package/src/features/issues/components/status-selector.tsx +320 -0
- package/src/features/issues/components/type-selector.tsx +102 -0
- package/src/features/issues/components/upload-progress-overlay.tsx +35 -0
- package/src/features/issues/components/uploaded-image-preview.tsx +173 -0
- package/src/features/issues/components/workflow-control.tsx +318 -0
- package/src/features/issues/components/zoom-controls.tsx +150 -0
- package/src/features/issues/config/index.ts +47 -0
- package/src/features/issues/config/options.ts +323 -0
- package/src/features/issues/config/workflow.ts +102 -0
- package/src/features/issues/docs/ARCHITECTURE_DIAGRAM.md +321 -0
- package/src/features/issues/docs/BACKEND_ARCHITECTURE.md +194 -0
- package/src/features/issues/docs/IMAGE_COMPONENTS_ARCHITECTURE.md +363 -0
- package/src/features/issues/docs/IMAGE_COMPONENTS_IMPORTS.md +412 -0
- package/src/features/issues/docs/IMPLEMENTATION_CHECKLIST.md +210 -0
- package/src/features/issues/docs/ROUTE_SETUP_COMPLETE.md +242 -0
- package/src/features/issues/hooks/index.ts +78 -0
- package/src/features/issues/hooks/use-canvas-transform.ts +255 -0
- package/src/features/issues/hooks/use-create-issue.ts +28 -0
- package/src/features/issues/hooks/use-elastic-scroll.ts +296 -0
- package/src/features/issues/hooks/use-issue-activities.ts +71 -0
- package/src/features/issues/hooks/use-issue-delete.ts +84 -0
- package/src/features/issues/hooks/use-issue-details.ts +70 -0
- package/src/features/issues/hooks/use-issue-filters.ts +50 -0
- package/src/features/issues/hooks/use-issue-update.ts +93 -0
- package/src/features/issues/hooks/use-keyboard-shortcuts.ts +104 -0
- package/src/features/issues/hooks/use-optimized-image.ts +228 -0
- package/src/features/issues/hooks/use-project-issues.ts +14 -0
- package/src/features/issues/index.ts +65 -0
- package/src/features/issues/screens/issue-details-screen.tsx +207 -0
- package/src/features/issues/screens/issue-details-skeletons.tsx +295 -0
- package/src/features/issues/screens/issue-share-screen.tsx +56 -0
- package/src/features/issues/types/index.ts +48 -0
- package/src/features/issues/types/issue.ts +291 -0
- package/src/features/issues/utils/filter-issues.ts +141 -0
- package/src/features/issues/utils/index.ts +14 -0
- package/src/features/legal/index.ts +1 -0
- package/src/features/legal/screens/privacy-policy-screen.tsx +307 -0
- package/src/features/notifications/api/get-notifications.ts +58 -0
- package/src/features/notifications/api/get-unread-count.ts +37 -0
- package/src/features/notifications/api/index.ts +35 -0
- package/src/features/notifications/api/mark-all-as-read.ts +37 -0
- package/src/features/notifications/api/mark-as-read.ts +41 -0
- package/src/features/notifications/api/types.ts +109 -0
- package/src/features/notifications/hooks/__tests__/use-notification-subscription.test.ts +206 -0
- package/src/features/notifications/hooks/index.ts +28 -0
- package/src/features/notifications/hooks/use-mark-all-as-read.ts +106 -0
- package/src/features/notifications/hooks/use-mark-as-read.ts +106 -0
- package/src/features/notifications/hooks/use-notification-subscription.ts +244 -0
- package/src/features/notifications/hooks/use-notification-toast.ts +161 -0
- package/src/features/notifications/hooks/use-notifications.ts +80 -0
- package/src/features/notifications/hooks/use-unread-count.ts +60 -0
- package/src/features/notifications/index.ts +48 -0
- package/src/features/notifications/utils/group-notifications.ts +152 -0
- package/src/features/notifications/utils/index.ts +9 -0
- package/src/features/projects/api/create-invitation.ts +45 -0
- package/src/features/projects/api/create-project.ts +64 -0
- package/src/features/projects/api/delete-project.ts +50 -0
- package/src/features/projects/api/get-project-activities.ts +43 -0
- package/src/features/projects/api/get-project-members.ts +53 -0
- package/src/features/projects/api/get-project.ts +49 -0
- package/src/features/projects/api/get-projects.ts +61 -0
- package/src/features/projects/api/index.ts +27 -0
- package/src/features/projects/api/join-project.ts +52 -0
- package/src/features/projects/api/leave-project.ts +51 -0
- package/src/features/projects/api/list-invitations.ts +36 -0
- package/src/features/projects/api/remove-member.ts +60 -0
- package/src/features/projects/api/resend-invitation.ts +36 -0
- package/src/features/projects/api/revoke-invitation.ts +36 -0
- package/src/features/projects/api/types.ts +286 -0
- package/src/features/projects/api/update-member-role.ts +70 -0
- package/src/features/projects/api/update-project.ts +69 -0
- package/src/features/projects/components/__tests__/project-detail-activity-feed.test.tsx +106 -0
- package/src/features/projects/components/__tests__/project-invitation-dialog.test.tsx +211 -0
- package/src/features/projects/components/__tests__/project-member-manager-dialog.test.tsx +254 -0
- package/src/features/projects/components/index.ts +21 -0
- package/src/features/projects/components/project-actions.tsx +248 -0
- package/src/features/projects/components/project-create-dialog.tsx +410 -0
- package/src/features/projects/components/project-detail-activity-feed.tsx +206 -0
- package/src/features/projects/components/project-detail-header.tsx +103 -0
- package/src/features/projects/components/project-detail-overview.tsx +128 -0
- package/src/features/projects/components/project-icon-selector.test.tsx +49 -0
- package/src/features/projects/components/project-icon-selector.tsx +76 -0
- package/src/features/projects/components/project-invitation-dialog.tsx +368 -0
- package/src/features/projects/components/project-issues.tsx +128 -0
- package/src/features/projects/components/project-leave-button.tsx +69 -0
- package/src/features/projects/components/project-list-card.tsx +246 -0
- package/src/features/projects/components/project-list-filters.tsx +320 -0
- package/src/features/projects/components/project-member-manager-dialog.tsx +419 -0
- package/src/features/projects/components/project-settings-dialog.tsx +204 -0
- package/src/features/projects/components/project-stats.tsx +46 -0
- package/src/features/projects/components/project-title-section.tsx +78 -0
- package/src/features/projects/config/icons.ts +91 -0
- package/src/features/projects/hooks/index.ts +28 -0
- package/src/features/projects/hooks/use-create-invitation.ts +83 -0
- package/src/features/projects/hooks/use-create-project.ts +77 -0
- package/src/features/projects/hooks/use-delete-project.ts +84 -0
- package/src/features/projects/hooks/use-join-project.ts +43 -0
- package/src/features/projects/hooks/use-leave-project.ts +84 -0
- package/src/features/projects/hooks/use-project-activities.ts +39 -0
- package/src/features/projects/hooks/use-project-filters.ts +86 -0
- package/src/features/projects/hooks/use-project-invitations.ts +66 -0
- package/src/features/projects/hooks/use-project-members.ts +57 -0
- package/src/features/projects/hooks/use-project.ts +67 -0
- package/src/features/projects/hooks/use-projects.ts +49 -0
- package/src/features/projects/hooks/use-recent-projects.ts +58 -0
- package/src/features/projects/hooks/use-remove-member.ts +89 -0
- package/src/features/projects/hooks/use-resend-invitation.ts +68 -0
- package/src/features/projects/hooks/use-revoke-invitation.ts +71 -0
- package/src/features/projects/hooks/use-team-member-suggestions.ts +133 -0
- package/src/features/projects/hooks/use-update-member-role.ts +92 -0
- package/src/features/projects/hooks/use-update-project.ts +88 -0
- package/src/features/projects/index.ts +91 -0
- package/src/features/projects/screens/index.ts +3 -0
- package/src/features/projects/screens/invitation-acceptance-screen.tsx +320 -0
- package/src/features/projects/screens/project-detail-screen-wrapper.tsx +47 -0
- package/src/features/projects/screens/project-detail-screen.tsx +661 -0
- package/src/features/projects/screens/projects-list-screen.tsx +161 -0
- package/src/features/projects/types/index.ts +59 -0
- package/src/features/projects/utils/format-helpers.ts +16 -0
- package/src/features/projects/utils/index.ts +2 -0
- package/src/features/projects/utils/role-helpers.ts +25 -0
- package/src/features/setup/api/complete-setup.ts +21 -0
- package/src/features/setup/api/create-admin.ts +21 -0
- package/src/features/setup/api/create-first-workspace.ts +21 -0
- package/src/features/setup/api/get-instance-status.ts +12 -0
- package/src/features/setup/api/get-service-health.ts +12 -0
- package/src/features/setup/api/index.ts +44 -0
- package/src/features/setup/api/save-instance-config.ts +21 -0
- package/src/features/setup/api/types.ts +122 -0
- package/src/features/setup/components/__tests__/setup-wizard-ui.test.tsx +362 -0
- package/src/features/setup/components/admin-account-step.tsx +205 -0
- package/src/features/setup/components/first-workspace-step.tsx +120 -0
- package/src/features/setup/components/index.ts +9 -0
- package/src/features/setup/components/instance-config-step.tsx +107 -0
- package/src/features/setup/components/mail-config-step.tsx +205 -0
- package/src/features/setup/components/sample-data-step.tsx +131 -0
- package/src/features/setup/components/service-health-step.tsx +180 -0
- package/src/features/setup/components/service-status-badge.tsx +50 -0
- package/src/features/setup/components/setup-progress.tsx +103 -0
- package/src/features/setup/components/setup-wizard.tsx +169 -0
- package/src/features/setup/hooks/index.ts +12 -0
- package/src/features/setup/hooks/use-complete-setup.ts +23 -0
- package/src/features/setup/hooks/use-create-admin.ts +25 -0
- package/src/features/setup/hooks/use-create-first-workspace.ts +24 -0
- package/src/features/setup/hooks/use-instance-status.ts +21 -0
- package/src/features/setup/hooks/use-save-instance-config.ts +23 -0
- package/src/features/setup/hooks/use-service-health.ts +21 -0
- package/src/features/setup/hooks/use-setup-wizard.ts +152 -0
- package/src/features/setup/hooks/use-workspace-mode.ts +19 -0
- package/src/features/setup/index.ts +30 -0
- package/src/features/setup/screens/index.ts +1 -0
- package/src/features/setup/screens/setup-screen.tsx +40 -0
- package/src/features/setup/types/index.ts +69 -0
- package/src/features/setup/utils/index.ts +78 -0
- package/src/features/team-settings/components/index.ts +39 -0
- package/src/features/team-settings/components/loading-states.tsx +296 -0
- package/src/features/team-settings/components/permission-guard.tsx +23 -0
- package/src/features/team-settings/components/settings-card.tsx +22 -0
- package/src/features/team-settings/components/settings-context-provider.tsx +51 -0
- package/src/features/team-settings/components/settings-error-boundary.tsx +366 -0
- package/src/features/team-settings/components/settings-navigation.tsx +87 -0
- package/src/features/team-settings/components/settings-section.tsx +23 -0
- package/src/features/team-settings/components/team-danger-zone.tsx +275 -0
- package/src/features/team-settings/components/team-information-form.tsx +116 -0
- package/src/features/team-settings/components/team-invitations-list.tsx +463 -0
- package/src/features/team-settings/components/team-members-list.tsx +342 -0
- package/src/features/team-settings/components/team-permission-guard.tsx +56 -0
- package/src/features/team-settings/components/team-setting-integrations.tsx +27 -0
- package/src/features/team-settings/components/team-setting-member.tsx +28 -0
- package/src/features/team-settings/components/team-settings-general.tsx +131 -0
- package/src/features/team-settings/components/transfer-ownership-modal.tsx +164 -0
- package/src/features/team-settings/components/unauthorized-access.tsx +52 -0
- package/src/features/team-settings/hooks/__tests__/use-team-settings.test.tsx +139 -0
- package/src/features/team-settings/hooks/index.ts +1 -0
- package/src/features/team-settings/hooks/use-team-settings.ts +148 -0
- package/src/features/team-settings/hooks/use-transfer-ownership.ts +45 -0
- package/src/features/team-settings/index.ts +25 -0
- package/src/features/team-settings/screens/index.ts +1 -0
- package/src/features/team-settings/screens/team-settings-screen.tsx +33 -0
- package/src/features/team-settings/types/index.ts +78 -0
- package/src/features/team-settings/utils/index.ts +6 -0
- package/src/features/team-settings/utils/mock-data.ts +40 -0
- package/src/features/teams/api/cancel-invitation.ts +25 -0
- package/src/features/teams/api/create-invitation.ts +28 -0
- package/src/features/teams/api/create-team.ts +14 -0
- package/src/features/teams/api/delete-team.ts +19 -0
- package/src/features/teams/api/get-invitations.ts +25 -0
- package/src/features/teams/api/get-team-members.ts +25 -0
- package/src/features/teams/api/get-team.ts +13 -0
- package/src/features/teams/api/get-teams.ts +19 -0
- package/src/features/teams/api/index.ts +49 -0
- package/src/features/teams/api/leave-team.ts +22 -0
- package/src/features/teams/api/remove-member.ts +25 -0
- package/src/features/teams/api/resend-invitation.ts +26 -0
- package/src/features/teams/api/switch-team.ts +13 -0
- package/src/features/teams/api/types.ts +122 -0
- package/src/features/teams/api/update-member-roles.ts +28 -0
- package/src/features/teams/api/update-team.ts +17 -0
- package/src/features/teams/components/create-team-dialog.tsx +105 -0
- package/src/features/teams/hooks/__tests__/cache-invalidation.property.test.tsx +268 -0
- package/src/features/teams/hooks/index.ts +35 -0
- package/src/features/teams/hooks/use-can-manage-members.ts +21 -0
- package/src/features/teams/hooks/use-can-manage-team.ts +21 -0
- package/src/features/teams/hooks/use-cancel-invitation.ts +52 -0
- package/src/features/teams/hooks/use-create-invitation.ts +59 -0
- package/src/features/teams/hooks/use-create-team.ts +38 -0
- package/src/features/teams/hooks/use-delete-team.ts +43 -0
- package/src/features/teams/hooks/use-invitations.ts +31 -0
- package/src/features/teams/hooks/use-leave-team.ts +43 -0
- package/src/features/teams/hooks/use-remove-member.ts +58 -0
- package/src/features/teams/hooks/use-resend-invitation.ts +52 -0
- package/src/features/teams/hooks/use-switch-team.ts +41 -0
- package/src/features/teams/hooks/use-team-members.ts +31 -0
- package/src/features/teams/hooks/use-team-permissions.ts +102 -0
- package/src/features/teams/hooks/use-team.ts +30 -0
- package/src/features/teams/hooks/use-teams.ts +26 -0
- package/src/features/teams/hooks/use-update-member-roles.ts +64 -0
- package/src/features/teams/hooks/use-update-team.ts +47 -0
- package/src/features/teams/index.ts +111 -0
- package/src/features/user-settings/actions/set-password.ts +63 -0
- package/src/features/user-settings/api/index.ts +16 -0
- package/src/features/user-settings/components/__tests__/security-settings.test.tsx +125 -0
- package/src/features/user-settings/components/delete-account-dialog.tsx +185 -0
- package/src/features/user-settings/components/index.ts +13 -0
- package/src/features/user-settings/components/integrations-list.tsx +152 -0
- package/src/features/user-settings/components/notification-preferences.tsx +112 -0
- package/src/features/user-settings/components/other-settings.tsx +126 -0
- package/src/features/user-settings/components/password-section.tsx +297 -0
- package/src/features/user-settings/components/security-settings.tsx +184 -0
- package/src/features/user-settings/components/user-preferences.tsx +146 -0
- package/src/features/user-settings/hooks/index.ts +8 -0
- package/src/features/user-settings/hooks/use-notification-preferences.ts +65 -0
- package/src/features/user-settings/hooks/use-user-preferences.ts +52 -0
- package/src/features/user-settings/index.ts +22 -0
- package/src/features/user-settings/screens/index.ts +11 -0
- package/src/features/user-settings/screens/integrations-screen.tsx +24 -0
- package/src/features/user-settings/screens/notifications-screen.tsx +29 -0
- package/src/features/user-settings/screens/other-settings-screen.tsx +24 -0
- package/src/features/user-settings/screens/setting-preferences-screen.tsx +24 -0
- package/src/features/user-settings/screens/user-settings-screen.tsx +23 -0
- package/src/features/user-settings/types/index.ts +42 -0
- package/src/features/user-settings/utils/index.ts +8 -0
- package/src/hooks/use-long-press.ts +196 -0
- package/src/hooks/use-media-upload.ts +95 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/use-team.ts +32 -0
- package/src/instrumentation.ts +14 -0
- package/src/lib/__tests__/auth-config.test.ts +166 -0
- package/src/lib/__tests__/config.test.ts +42 -0
- package/src/lib/__tests__/db.test.ts +101 -0
- package/src/lib/__tests__/env.property.test.ts +197 -0
- package/src/lib/__tests__/env.test.ts +177 -0
- package/src/lib/__tests__/health-check.test.ts +87 -0
- package/src/lib/__tests__/oauth-errors.test.ts +184 -0
- package/src/lib/__tests__/oauth-redirect.test.ts +224 -0
- package/src/lib/__tests__/oauth-scopes.test.ts +268 -0
- package/src/lib/__tests__/storage.test.ts +58 -0
- package/src/lib/__tests__/url-validator.test.ts +232 -0
- package/src/lib/api-client.ts +93 -0
- package/src/lib/auth-client.ts +5 -0
- package/src/lib/auth-config.ts +146 -0
- package/src/lib/auth.ts +209 -0
- package/src/lib/config.ts +228 -0
- package/src/lib/cors.ts +96 -0
- package/src/lib/date.ts +16 -0
- package/src/lib/db.ts +88 -0
- package/src/lib/env.ts +489 -0
- package/src/lib/feedback.ts +29 -0
- package/src/lib/health-check.ts +229 -0
- package/src/lib/logger.ts +204 -0
- package/src/lib/oauth-errors.ts +138 -0
- package/src/lib/performance.ts +367 -0
- package/src/lib/query-server.ts +35 -0
- package/src/lib/query.tsx +43 -0
- package/src/lib/resend.ts +36 -0
- package/src/lib/security-headers.ts +165 -0
- package/src/lib/setup-status.ts +34 -0
- package/src/lib/storage.ts +150 -0
- package/src/lib/supabase-client.ts +58 -0
- package/src/lib/testing/test-db.ts +75 -0
- package/src/lib/url-validator.ts +168 -0
- package/src/lib/utils.ts +6 -0
- package/src/mocks/README.md +42 -0
- package/src/mocks/activity.fixtures.ts +281 -0
- package/src/mocks/annotation.fixtures.ts +325 -0
- package/src/mocks/attachment.fixtures.ts +80 -0
- package/src/mocks/email.fixtures.ts +61 -0
- package/src/mocks/index.ts +33 -0
- package/src/mocks/issue.fixtures.ts +364 -0
- package/src/mocks/landing.fixtures.ts +118 -0
- package/src/mocks/notification.fixtures.ts +111 -0
- package/src/mocks/project-activity.fixtures.ts +69 -0
- package/src/mocks/project-invitation.fixtures.ts +95 -0
- package/src/mocks/project-member.fixtures.ts +93 -0
- package/src/mocks/project.fixtures.ts +249 -0
- package/src/mocks/settings.fixtures.ts +56 -0
- package/src/mocks/share.fixtures.ts +48 -0
- package/src/mocks/team-member.fixtures.ts +147 -0
- package/src/mocks/team.fixtures.ts +35 -0
- package/src/mocks/user-settings.fixtures.ts +77 -0
- package/src/mocks/user.fixtures.ts +21 -0
- package/src/providers/theme-provider.tsx +11 -0
- package/src/proxy/__tests__/proxy-setup-gate.test.ts +43 -0
- package/src/proxy.ts +169 -0
- package/src/server/annotations/__tests__/annotation-limit.property.test.ts +291 -0
- package/src/server/annotations/__tests__/attachment-deletion-cascade.property.test.ts +385 -0
- package/src/server/annotations/__tests__/comment-delete-authorization.property.test.ts +419 -0
- package/src/server/annotations/__tests__/sanitize.property.test.ts +302 -0
- package/src/server/annotations/annotation-service.ts +305 -0
- package/src/server/annotations/comment-service.ts +288 -0
- package/src/server/annotations/index.ts +61 -0
- package/src/server/annotations/permission-utils.ts +125 -0
- package/src/server/annotations/sanitize.ts +134 -0
- package/src/server/annotations/types.ts +161 -0
- package/src/server/audit/audit-service.ts +85 -0
- package/src/server/audit/index.ts +11 -0
- package/src/server/audit/types.ts +75 -0
- package/src/server/auth/__tests__/README.md +368 -0
- package/src/server/auth/__tests__/account-linking.integration.test.ts +410 -0
- package/src/server/auth/__tests__/auth-integration.test.ts +811 -0
- package/src/server/auth/__tests__/cookies.test.ts +337 -0
- package/src/server/auth/__tests__/login.property.test.ts +428 -0
- package/src/server/auth/__tests__/oauth-integration.test.ts +555 -0
- package/src/server/auth/__tests__/password.test.ts +194 -0
- package/src/server/auth/__tests__/rate-limiter.test.ts +450 -0
- package/src/server/auth/__tests__/rbac.test.ts +474 -0
- package/src/server/auth/__tests__/session.test.ts +599 -0
- package/src/server/auth/__tests__/signup.property.test.ts +224 -0
- package/src/server/auth/__tests__/token-encryption.test.ts +171 -0
- package/src/server/auth/__tests__/tokens.test.ts +476 -0
- package/src/server/auth/__tests__/verify-email.property.test.ts +372 -0
- package/src/server/auth/cookies.ts +184 -0
- package/src/server/auth/password.ts +94 -0
- package/src/server/auth/rate-limiter.ts +257 -0
- package/src/server/auth/rbac.ts +1168 -0
- package/src/server/auth/session.ts +392 -0
- package/src/server/auth/token-encryption.ts +201 -0
- package/src/server/auth/tokens.ts +397 -0
- package/src/server/db/schema/account.ts +21 -0
- package/src/server/db/schema/annotation-read-status.ts +57 -0
- package/src/server/db/schema/better-auth-verifications.ts +27 -0
- package/src/server/db/schema/email-jobs.ts +24 -0
- package/src/server/db/schema/index.ts +20 -0
- package/src/server/db/schema/instance-settings.ts +42 -0
- package/src/server/db/schema/issue-activities.ts +97 -0
- package/src/server/db/schema/issue-attachments.ts +101 -0
- package/src/server/db/schema/issues.ts +139 -0
- package/src/server/db/schema/notifications.ts +119 -0
- package/src/server/db/schema/project-activities.ts +116 -0
- package/src/server/db/schema/project-invitations.ts +34 -0
- package/src/server/db/schema/project-members.ts +29 -0
- package/src/server/db/schema/projects.ts +46 -0
- package/src/server/db/schema/sessions.ts +17 -0
- package/src/server/db/schema/team-invitations.ts +22 -0
- package/src/server/db/schema/team-members.ts +18 -0
- package/src/server/db/schema/teams.ts +15 -0
- package/src/server/db/schema/user-roles.ts +14 -0
- package/src/server/db/schema/users.ts +17 -0
- package/src/server/db/schema/verification-tokens.ts +15 -0
- package/src/server/email/README.md +258 -0
- package/src/server/email/__tests__/client.property.test.ts +218 -0
- package/src/server/email/__tests__/payload.property.test.ts +205 -0
- package/src/server/email/__tests__/queue.test.ts +407 -0
- package/src/server/email/__tests__/render-template.test.tsx +172 -0
- package/src/server/email/client.ts +70 -0
- package/src/server/email/index.ts +20 -0
- package/src/server/email/providers/console-provider.ts +43 -0
- package/src/server/email/providers/index.ts +5 -0
- package/src/server/email/providers/resend-provider.ts +59 -0
- package/src/server/email/providers/smtp-provider.ts +65 -0
- package/src/server/email/queue.ts +365 -0
- package/src/server/email/render-template.tsx +117 -0
- package/src/server/email/templates/layout.tsx +66 -0
- package/src/server/email/templates/ownership-transfer-email.tsx +75 -0
- package/src/server/email/templates/password-reset-email.tsx +72 -0
- package/src/server/email/templates/project-invitation-email.tsx +70 -0
- package/src/server/email/templates/security-alert-email.tsx +161 -0
- package/src/server/email/templates/team-invitation-email.tsx +68 -0
- package/src/server/email/templates/verification-email.tsx +61 -0
- package/src/server/email/templates/welcome-email.tsx +60 -0
- package/src/server/email/worker.ts +182 -0
- package/src/server/issues/activity-service.ts +422 -0
- package/src/server/issues/attachment-service.ts +379 -0
- package/src/server/issues/index.ts +68 -0
- package/src/server/issues/issue-service.ts +569 -0
- package/src/server/issues/types.ts +233 -0
- package/src/server/notifications/__tests__/notification-service.integration.test.ts +405 -0
- package/src/server/notifications/index.ts +13 -0
- package/src/server/notifications/notification-service.ts +526 -0
- package/src/server/notifications/types.ts +234 -0
- package/src/server/projects/__tests__/activity-logging.integration.test.ts +319 -0
- package/src/server/projects/__tests__/invitation-email.integration.test.ts +258 -0
- package/src/server/projects/__tests__/invitation-service.integration.test.ts +651 -0
- package/src/server/projects/__tests__/member-service.property.test.ts +397 -0
- package/src/server/projects/__tests__/project-service.property.test.ts +116 -0
- package/src/server/projects/activity-service.ts +548 -0
- package/src/server/projects/index.ts +11 -0
- package/src/server/projects/invitation-service.ts +773 -0
- package/src/server/projects/member-service.ts +458 -0
- package/src/server/projects/project-service.ts +491 -0
- package/src/server/projects/schemas.ts +310 -0
- package/src/server/projects/types.ts +166 -0
- package/src/server/projects/utils.ts +128 -0
- package/src/server/setup/__tests__/health-check.property.test.ts +70 -0
- package/src/server/setup/__tests__/sample-data.property.test.ts +82 -0
- package/src/server/setup/__tests__/setup.property.test.ts +95 -0
- package/src/server/setup/health-check-service.ts +294 -0
- package/src/server/setup/index.ts +35 -0
- package/src/server/setup/sample-data-service.ts +233 -0
- package/src/server/setup/setup-service.ts +229 -0
- package/src/server/setup/types.ts +96 -0
- package/src/server/teams/__tests__/export.property.test.ts +542 -0
- package/src/server/teams/__tests__/get-members.integration.test.ts +105 -0
- package/src/server/teams/__tests__/invitation-flow.integration.test.ts +402 -0
- package/src/server/teams/__tests__/invitations.property.test.ts +235 -0
- package/src/server/teams/__tests__/member-management-flow.integration.test.ts +306 -0
- package/src/server/teams/__tests__/members.property.test.ts +180 -0
- package/src/server/teams/__tests__/ownership-transfer.property.test.ts +173 -0
- package/src/server/teams/__tests__/resource-limits.property.test.ts +382 -0
- package/src/server/teams/__tests__/team-context-management.integration.test.ts +396 -0
- package/src/server/teams/__tests__/team-context.property.test.ts +854 -0
- package/src/server/teams/__tests__/team-creation-flow.integration.test.ts +310 -0
- package/src/server/teams/__tests__/team-creation.property.test.ts +280 -0
- package/src/server/teams/errors.ts +396 -0
- package/src/server/teams/export-service.ts +383 -0
- package/src/server/teams/index.ts +10 -0
- package/src/server/teams/invitation-service.ts +708 -0
- package/src/server/teams/member-service.ts +334 -0
- package/src/server/teams/resource-limits.ts +211 -0
- package/src/server/teams/slug.ts +58 -0
- package/src/server/teams/team-context.ts +648 -0
- package/src/server/teams/team-service.ts +660 -0
- package/src/server/teams/types.ts +81 -0
- package/src/server/teams/validation.ts +209 -0
- package/src/styles/globals.css +160 -0
- package/src/types/deployment.ts +39 -0
- package/supabase/config.toml +357 -0
- package/supabase/seed.sql +480 -0
- package/tests/e2e/.gitkeep +0 -0
- package/tests/e2e/QUICK_START.md +98 -0
- package/tests/e2e/README.md +301 -0
- package/tests/e2e/auth.spec.ts +583 -0
- package/tests/e2e/global-setup.ts +23 -0
- package/tests/e2e/global-teardown.ts +23 -0
- package/tests/e2e/helpers/auth-helpers.ts +310 -0
- package/tests/e2e/helpers/test-fixtures.ts +286 -0
- package/tests/e2e/smoke-test.spec.ts +330 -0
- package/tsconfig.json +48 -0
- package/vitest.config.ts +50 -0
- package/vitest.setup.ts +56 -0
- package/dist/index.js +0 -778
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
# Drizzle + Zod + PostgreSQL — Developer Instruction (for TypeScript apps)
|
|
2
|
+
|
|
3
|
+
> **Audience:** Backend/Full‑stack developers using Node.js/TypeScript (e.g., Next.js, Express, Fastify).
|
|
4
|
+
> **Goal:** A consistent, safe, and productive pattern that combines **Zod** (runtime validation), **Drizzle ORM** (typed SQL), and **PostgreSQL** (source of truth).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
## Table of Contents
|
|
8
|
+
1. Principles & Architecture
|
|
9
|
+
2. Tech Stack & Installation
|
|
10
|
+
3. Project Structure
|
|
11
|
+
4. Environment & Configuration
|
|
12
|
+
5. Database Schema with Drizzle
|
|
13
|
+
6. Zod Schemas (from Drizzle + custom rules)
|
|
14
|
+
7. Migrations & Seeding
|
|
15
|
+
8. Query Patterns (CRUD, pagination, transactions)
|
|
16
|
+
9. API Integration (Route examples)
|
|
17
|
+
10. Error Handling & Mapping (Zod ↔ Postgres)
|
|
18
|
+
11. Data Types & Gotchas (decimal, bigint, date, jsonb)
|
|
19
|
+
12. Security & Permissions
|
|
20
|
+
13. Performance & Indexing
|
|
21
|
+
14. Testing Strategy
|
|
22
|
+
15. CI/CD & Release Management
|
|
23
|
+
16. Conventions & Checklists
|
|
24
|
+
17. Troubleshooting
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
## 1) Principles & Architecture
|
|
28
|
+
- **Single source of truth:** PostgreSQL schema & constraints define reality (uniques, FKs, checks). ORM types help, but DB enforces.
|
|
29
|
+
- **Validate at the boundary:** All external input is parsed with **Zod** before touching business logic or DB.
|
|
30
|
+
- **No duplication of shapes:** Generate Zod from Drizzle where possible, then layer domain‑specific rules/refinements.
|
|
31
|
+
- **Explicitness:** Name fields, indexes, and relations clearly. Prefer explicit transactions for multi‑step writes.
|
|
32
|
+
- **Observability:** Log SQL (in non‑prod), surface Zod errors clearly, map Postgres codes to HTTP.
|
|
33
|
+
|
|
34
|
+
High‑level flow:
|
|
35
|
+
1) **Request →** Zod validates/coerces.
|
|
36
|
+
2) **Service/Repo →** Drizzle executes typed SQL.
|
|
37
|
+
3) **Response →** Optional Zod shaping/serialization.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
## 2) Tech Stack & Installation
|
|
41
|
+
**Core packages**
|
|
42
|
+
```bash
|
|
43
|
+
bun add drizzle-orm pg zod drizzle-zod
|
|
44
|
+
bun add -D drizzle-kit tsx typescript @types/node dotenv
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**CLI setup**
|
|
48
|
+
- `drizzle-kit` generates migrations from your TS schema and applies them.
|
|
49
|
+
- Use `tsx` (or `ts-node`) to run TS scripts.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
## 3) Project Structure (Feature-First Architecture)
|
|
53
|
+
```
|
|
54
|
+
src/
|
|
55
|
+
server/ # Server-only logic (auth, DB, RBAC)
|
|
56
|
+
db/
|
|
57
|
+
schema/
|
|
58
|
+
users.ts # User table schema
|
|
59
|
+
issues.ts # Issues table schema
|
|
60
|
+
projects.ts # Projects table schema
|
|
61
|
+
teams.ts # Teams table schema
|
|
62
|
+
index.ts # Re-exports all tables & relations
|
|
63
|
+
client.ts # Postgres connection + Drizzle instance
|
|
64
|
+
index.ts # Exports db client
|
|
65
|
+
seeds/
|
|
66
|
+
seed.ts # Database seed scripts
|
|
67
|
+
auth/ # Auth server logic
|
|
68
|
+
cookies.ts # httpOnly cookie management
|
|
69
|
+
tokens.ts # JWT sign/verify/rotate
|
|
70
|
+
session.ts # getSession(), requireSession()
|
|
71
|
+
rbac.ts # roles, permissions, guards
|
|
72
|
+
|
|
73
|
+
features/ # Feature-first organization
|
|
74
|
+
auth/
|
|
75
|
+
api/
|
|
76
|
+
types.ts # Zod DTOs (login, signup, etc.)
|
|
77
|
+
hooks/
|
|
78
|
+
use-session.ts # Client cache of /api/auth/me
|
|
79
|
+
use-sign-in.ts # POST /api/auth/login
|
|
80
|
+
index.ts
|
|
81
|
+
types/
|
|
82
|
+
index.ts # Session, User, Role domain types
|
|
83
|
+
utils/
|
|
84
|
+
validators.ts # Zod schemas for credentials
|
|
85
|
+
components/
|
|
86
|
+
sign-in-form.tsx
|
|
87
|
+
role-gate.tsx
|
|
88
|
+
index.ts
|
|
89
|
+
|
|
90
|
+
issues/
|
|
91
|
+
api/
|
|
92
|
+
get-issues.ts # GET /issues fetcher
|
|
93
|
+
get-issue.ts # GET /issues/:id fetcher
|
|
94
|
+
create-issue.ts # POST /issues fetcher
|
|
95
|
+
update-issue.ts # PATCH /issues/:id fetcher
|
|
96
|
+
types.ts # Zod DTOs (transport layer)
|
|
97
|
+
index.ts
|
|
98
|
+
hooks/
|
|
99
|
+
use-issues.ts # React Query list wrapper
|
|
100
|
+
use-issue.ts # React Query detail wrapper
|
|
101
|
+
use-create-issue.ts # React Query mutation
|
|
102
|
+
index.ts
|
|
103
|
+
types/
|
|
104
|
+
index.ts # Domain types (Issue, Status, Priority)
|
|
105
|
+
components/
|
|
106
|
+
issues-table.tsx
|
|
107
|
+
create-issue-dialog.tsx
|
|
108
|
+
index.ts
|
|
109
|
+
screens/
|
|
110
|
+
issues-list-screen.tsx # Composed feature UI
|
|
111
|
+
index.ts
|
|
112
|
+
|
|
113
|
+
projects/
|
|
114
|
+
api/
|
|
115
|
+
get-projects.ts
|
|
116
|
+
create-project.ts
|
|
117
|
+
types.ts
|
|
118
|
+
index.ts
|
|
119
|
+
hooks/
|
|
120
|
+
use-projects.ts
|
|
121
|
+
use-create-project.ts
|
|
122
|
+
index.ts
|
|
123
|
+
types/
|
|
124
|
+
index.ts
|
|
125
|
+
components/
|
|
126
|
+
projects-grid.tsx
|
|
127
|
+
index.ts
|
|
128
|
+
|
|
129
|
+
app/ # Next.js App Router
|
|
130
|
+
api/ # Route handlers
|
|
131
|
+
auth/
|
|
132
|
+
login/route.ts # POST /api/auth/login
|
|
133
|
+
logout/route.ts # POST /api/auth/logout
|
|
134
|
+
me/route.ts # GET /api/auth/me
|
|
135
|
+
issues/
|
|
136
|
+
route.ts # GET/POST /api/issues
|
|
137
|
+
[id]/route.ts # GET/PATCH/DELETE /api/issues/:id
|
|
138
|
+
projects/
|
|
139
|
+
route.ts # GET/POST /api/projects
|
|
140
|
+
[id]/route.ts # GET/PATCH/DELETE /api/projects/:id
|
|
141
|
+
|
|
142
|
+
lib/ # App plumbing
|
|
143
|
+
api-client.ts # fetch wrapper (credentials: 'include')
|
|
144
|
+
query.ts # QueryClient setup/provider
|
|
145
|
+
env.ts # Zod-validated env (DB_URL, etc.)
|
|
146
|
+
cn.ts # tailwind-merge + clsx
|
|
147
|
+
logger.ts
|
|
148
|
+
|
|
149
|
+
utils/ # Pure helpers
|
|
150
|
+
errors.ts # Error mappers (Postgres → HTTP)
|
|
151
|
+
pagination.ts # Pagination helpers
|
|
152
|
+
|
|
153
|
+
config/ # Single sources of truth
|
|
154
|
+
roles.ts # TEAM_*/PROJECT_* roles + PERMISSIONS
|
|
155
|
+
workflows.ts # Issue status model
|
|
156
|
+
nav.ts # Navigation configuration
|
|
157
|
+
|
|
158
|
+
drizzle.config.ts # Root-level drizzle-kit config
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> **Key differences from generic structure:**
|
|
162
|
+
> - Database logic lives in `server/db/` (server-only)
|
|
163
|
+
> - Zod validation split by layer:
|
|
164
|
+
> - **Transport DTOs** in `features/<name>/api/types.ts` (request/response shapes)
|
|
165
|
+
> - **Domain types** in `features/<name>/types/` (business logic)
|
|
166
|
+
> - **Validators** in `features/<name>/utils/validators.ts` (custom Zod refinements)
|
|
167
|
+
> - No generic `services/` folder—business logic flows through feature layers:
|
|
168
|
+
> - `app/api/` route handlers → `features/<name>/api/` fetchers (client-side)
|
|
169
|
+
> - Server-side logic in route handlers uses `server/db/` directly
|
|
170
|
+
> - Strict layering: `app/` → `features/` → `components/` → `lib`/`server`
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
## 4) Environment & Configuration
|
|
174
|
+
### `DATABASE_URL`
|
|
175
|
+
```
|
|
176
|
+
postgres://USER:PASSWORD@HOST:5432/DB?sslmode=require
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `env.ts` (validate env with Zod)
|
|
180
|
+
```ts
|
|
181
|
+
import { z } from "zod";
|
|
182
|
+
|
|
183
|
+
export const Env = z.object({
|
|
184
|
+
DATABASE_URL: z.string().url().includes("postgres"),
|
|
185
|
+
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
export type Env = z.infer<typeof Env>;
|
|
189
|
+
export const env = Env.parse(process.env);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `drizzle.config.ts`
|
|
193
|
+
```ts
|
|
194
|
+
import { defineConfig } from "drizzle-kit";
|
|
195
|
+
|
|
196
|
+
export default defineConfig({
|
|
197
|
+
schema: "./src/server/db/schema/index.ts",
|
|
198
|
+
out: "./drizzle",
|
|
199
|
+
dialect: "postgresql",
|
|
200
|
+
dbCredentials: {
|
|
201
|
+
url: process.env.DATABASE_URL!,
|
|
202
|
+
},
|
|
203
|
+
strict: true,
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### DB client (`src/server/db/client.ts`)
|
|
208
|
+
```ts
|
|
209
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
210
|
+
import { Pool } from "pg";
|
|
211
|
+
import * as schema from "./schema";
|
|
212
|
+
import { env } from "@/env";
|
|
213
|
+
|
|
214
|
+
const pool = new Pool({ connectionString: env.DATABASE_URL, max: 10 });
|
|
215
|
+
export const db = drizzle(pool, { schema });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Scripts** (in `package.json`)
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"scripts": {
|
|
222
|
+
"db:generate": "drizzle-kit generate",
|
|
223
|
+
"db:migrate": "drizzle-kit migrate",
|
|
224
|
+
"db:studio": "drizzle-kit studio",
|
|
225
|
+
"seed": "tsx src/server/db/seeds/seed.ts"
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
## 5) Database Schema with Drizzle
|
|
232
|
+
**Example:** `users` and `issues` tables.
|
|
233
|
+
```ts
|
|
234
|
+
// src/server/db/schema/users.ts
|
|
235
|
+
import { pgTable, uuid, varchar, timestamp, integer } from "drizzle-orm/pg-core";
|
|
236
|
+
import { sql } from "drizzle-orm";
|
|
237
|
+
|
|
238
|
+
export const users = pgTable("users", {
|
|
239
|
+
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
240
|
+
email: varchar("email", { length: 320 }).notNull().unique(),
|
|
241
|
+
name: varchar("name", { length: 120 }).notNull(),
|
|
242
|
+
age: integer("age"),
|
|
243
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
```ts
|
|
247
|
+
// src/server/db/schema/issues.ts
|
|
248
|
+
import { pgTable, uuid, varchar, text, timestamp } from "drizzle-orm/pg-core";
|
|
249
|
+
import { users } from "./users";
|
|
250
|
+
|
|
251
|
+
export const issues = pgTable("issues", {
|
|
252
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
253
|
+
title: varchar("title", { length: 200 }).notNull(),
|
|
254
|
+
description: text("description"),
|
|
255
|
+
reporterId: uuid("reporter_id").references(() => users.id, { onDelete: "restrict" }).notNull(),
|
|
256
|
+
assigneeId: uuid("assignee_id").references(() => users.id, { onDelete: "set null" }),
|
|
257
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
```ts
|
|
261
|
+
// src/server/db/schema/index.ts
|
|
262
|
+
export * from "./users";
|
|
263
|
+
export * from "./issues";
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Conventions**
|
|
267
|
+
- **snake_case** columns, plural table names.
|
|
268
|
+
- Always include `created_at` (and `updated_at` if needed).
|
|
269
|
+
- Use `uuid` PK with `gen_random_uuid()` (enable `pgcrypto` extension in your DB).
|
|
270
|
+
- Declare foreign keys with explicit `onDelete` policy.
|
|
271
|
+
- Add indexes for common filters (email, foreign keys, created_at).
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
## 6) Zod Schemas (generated + domain rules)
|
|
275
|
+
Use `drizzle-zod` to avoid duplicating shapes, then layer constraints.
|
|
276
|
+
```ts
|
|
277
|
+
// src/validation/users.ts
|
|
278
|
+
import { z } from "zod";
|
|
279
|
+
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
|
280
|
+
import { users } from "@/db/schema";
|
|
281
|
+
|
|
282
|
+
export const InsertUser = createInsertSchema(users, {
|
|
283
|
+
email: (z) => z.string().email(),
|
|
284
|
+
name: (z) => z.string().min(1).max(120),
|
|
285
|
+
age: (z) => z.number().int().gte(13).optional(),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
export const SelectUser = createSelectSchema(users);
|
|
289
|
+
export type TInsertUser = z.infer<typeof InsertUser>;
|
|
290
|
+
export type TSelectUser = z.infer<typeof SelectUser>;
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Custom refinements & transforms:
|
|
294
|
+
```ts
|
|
295
|
+
export const CreateIssue = z.object({
|
|
296
|
+
title: z.string().min(3).max(200),
|
|
297
|
+
description: z.string().max(10_000).optional(),
|
|
298
|
+
reporterId: z.string().uuid(),
|
|
299
|
+
assigneeId: z.string().uuid().optional(),
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Guideline:**
|
|
304
|
+
- **Boundary in:** Parse request payloads with Zod (`parse`/`safeParse`).
|
|
305
|
+
- **Boundary out:** Optionally shape responses (strip internals, serialize dates via `.toISOString()`).
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
## 7) Migrations & Seeding
|
|
309
|
+
**Generate & apply**
|
|
310
|
+
```bash
|
|
311
|
+
pnpm db:generate # scans TS schema → SQL migrations in /drizzle
|
|
312
|
+
pnpm db:migrate # applies migrations to target DB
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Seed script**
|
|
316
|
+
```ts
|
|
317
|
+
// src/server/db/seeds/seed.ts
|
|
318
|
+
import { db } from "@/db/client";
|
|
319
|
+
import { users, issues } from "@/db/schema";
|
|
320
|
+
|
|
321
|
+
await db.insert(users).values([
|
|
322
|
+
{ email: "ada@example.com", name: "Ada Lovelace" },
|
|
323
|
+
{ email: "alan@example.com", name: "Alan Turing" },
|
|
324
|
+
]);
|
|
325
|
+
|
|
326
|
+
const [ada] = await db.select().from(users).where(users.email.eq("ada@example.com"));
|
|
327
|
+
await db.insert(issues).values({ title: "First issue", reporterId: ada.id });
|
|
328
|
+
|
|
329
|
+
console.log("Seed completed");
|
|
330
|
+
process.exit(0);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Rules**
|
|
334
|
+
- Never hand‑edit generated SQL; regenerate from TS.
|
|
335
|
+
- One migration per logical change; commit migrations.
|
|
336
|
+
- Seeding is idempotent where possible.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
## 8) Query Patterns
|
|
340
|
+
**CRUD**
|
|
341
|
+
```ts
|
|
342
|
+
// Create
|
|
343
|
+
await db.insert(users).values(data);
|
|
344
|
+
|
|
345
|
+
// Read
|
|
346
|
+
const rows = await db.select().from(users).limit(50);
|
|
347
|
+
|
|
348
|
+
// Update
|
|
349
|
+
await db.update(users).set({ name: "New Name" }).where(users.id.eq(id));
|
|
350
|
+
|
|
351
|
+
// Delete
|
|
352
|
+
await db.delete(users).where(users.id.eq(id));
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Pagination (cursor)**
|
|
356
|
+
```ts
|
|
357
|
+
// cursor = { createdAt: iso, id: uuid }
|
|
358
|
+
// fetch newer than cursor (or older, depending on sort)
|
|
359
|
+
```
|
|
360
|
+
Use `(created_at, id)` composite cursor to ensure stable ordering.
|
|
361
|
+
|
|
362
|
+
**Transactions**
|
|
363
|
+
```ts
|
|
364
|
+
await db.transaction(async (tx) => {
|
|
365
|
+
const u = await tx.insert(users).values(data).returning();
|
|
366
|
+
await tx.insert(issues).values({ title: "Welcome", reporterId: u[0].id });
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
## 9) API Integration (Route examples)
|
|
372
|
+
**Next.js Route Handler (POST /api/users)**
|
|
373
|
+
```ts
|
|
374
|
+
// src/api/users/POST.ts
|
|
375
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
376
|
+
import { db } from "@/db/client";
|
|
377
|
+
import { InsertUser } from "@/validation/users";
|
|
378
|
+
import { users } from "@/db/schema";
|
|
379
|
+
import { mapPostgresError } from "@/utils/errors";
|
|
380
|
+
|
|
381
|
+
export async function POST(req: NextRequest) {
|
|
382
|
+
const body = await req.json();
|
|
383
|
+
const parsed = InsertUser.safeParse(body);
|
|
384
|
+
if (!parsed.success) {
|
|
385
|
+
return NextResponse.json({ errors: parsed.error.flatten() }, { status: 400 });
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const [created] = await db.insert(users).values(parsed.data).returning();
|
|
390
|
+
return NextResponse.json({ id: created.id }, { status: 201 });
|
|
391
|
+
} catch (e) {
|
|
392
|
+
const { status, message } = mapPostgresError(e);
|
|
393
|
+
return NextResponse.json({ error: message }, { status });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Express example (GET /users)**
|
|
399
|
+
```ts
|
|
400
|
+
app.get("/users", async (req, res) => {
|
|
401
|
+
const rows = await db.select().from(users).limit(100);
|
|
402
|
+
res.json(rows);
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
## 10) Error Handling & Mapping
|
|
408
|
+
**Zod errors → 400** with `error.flatten()`.
|
|
409
|
+
|
|
410
|
+
**Postgres errors → HTTP**
|
|
411
|
+
- `23505` **unique_violation** → 409 Conflict (e.g., duplicate email)
|
|
412
|
+
- `23503` **foreign_key_violation** → 409 or 422 (invalid relation)
|
|
413
|
+
- `23514` **check_violation** → 422
|
|
414
|
+
- Default → 500
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
// src/utils/errors.ts
|
|
418
|
+
export function mapPostgresError(e: any) {
|
|
419
|
+
const code = e?.code as string | undefined;
|
|
420
|
+
switch (code) {
|
|
421
|
+
case "23505":
|
|
422
|
+
return { status: 409, message: "Duplicate key" };
|
|
423
|
+
case "23503":
|
|
424
|
+
return { status: 422, message: "Related record missing" };
|
|
425
|
+
case "23514":
|
|
426
|
+
return { status: 422, message: "Constraint failed" };
|
|
427
|
+
default:
|
|
428
|
+
return { status: 500, message: "Internal error" };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
## 11) Data Types & Gotchas
|
|
435
|
+
- **text vs varchar(n):** Prefer `text`. Add a `CHECK` only when the length is a real business rule—not a UI whim.
|
|
436
|
+
```sql
|
|
437
|
+
ALTER TABLE users ADD CONSTRAINT name_len CHECK (char_length(name) <= 120);
|
|
438
|
+
```
|
|
439
|
+
- **Email case-insensitivity:** Use `CITEXT` or a functional index. Works great with soft deletes.
|
|
440
|
+
```sql
|
|
441
|
+
CREATE EXTENSION IF NOT EXISTS citext;
|
|
442
|
+
ALTER TABLE users ALTER COLUMN email TYPE citext;
|
|
443
|
+
CREATE UNIQUE INDEX users_email_unq_active
|
|
444
|
+
ON users (email) WHERE deleted_at IS NULL;
|
|
445
|
+
-- or:
|
|
446
|
+
CREATE UNIQUE INDEX users_email_unq_lower_active
|
|
447
|
+
ON users ((lower(email))) WHERE deleted_at IS NULL;
|
|
448
|
+
```
|
|
449
|
+
- **numeric/decimal:** Node `pg` returns strings. Use integer cents or a decimal lib for arithmetic; if you keep `numeric`, parse/transform at the edge.
|
|
450
|
+
- **bigint:** Use `z.bigint()` or string→BigInt transform; be explicit in your Zod schema.
|
|
451
|
+
- **timestamptz:** Store UTC. Serialize to ISO-8601. Prefer `timestamptz` over `timestamp`.
|
|
452
|
+
- **jsonb:** Model the shape with Zod; index with GIN when you query by keys/paths.
|
|
453
|
+
```sql
|
|
454
|
+
CREATE INDEX products_meta_gin ON products USING GIN (meta jsonb_path_ops);
|
|
455
|
+
```
|
|
456
|
+
- **UUIDs:** Generate in DB (`gen_random_uuid()`), not clients. Enable `pgcrypto` in the database.
|
|
457
|
+
```sql
|
|
458
|
+
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
459
|
+
```
|
|
460
|
+
---
|
|
461
|
+
## 12) Security & Permissions
|
|
462
|
+
- **Parameterized queries** only (Drizzle does this by default). No string‑built SQL.
|
|
463
|
+
- **AuthZ checks in services** (e.g., ensure `reporterId` matches current user or role grants access).
|
|
464
|
+
- **Input validation** with Zod (lengths, formats, enums). Never trust client types.
|
|
465
|
+
- **Secrets** from env are Zod‑validated; never hardcode.
|
|
466
|
+
- Consider **Row‑Level Security (RLS)** in Postgres for multi‑tenant setups.
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
## 13) Performance & Indexing
|
|
470
|
+
- **Index the essentials:** all FKs, frequently filtered columns, and your pagination sort `(created_at, id)`.
|
|
471
|
+
- **Composite & covering indexes:** Order columns by selectivity and sort; add `INCLUDE(...)` to cover projections where it matters.
|
|
472
|
+
```sql
|
|
473
|
+
CREATE INDEX issues_list_idx ON issues (reporter_id, created_at DESC, id DESC) INCLUDE (title, status);
|
|
474
|
+
```
|
|
475
|
+
- **Partial indexes:** Apply uniqueness or speed only to the live working set (e.g., soft-deleted rows excluded).
|
|
476
|
+
```sql
|
|
477
|
+
CREATE UNIQUE INDEX users_email_unq_active
|
|
478
|
+
ON users (email) WHERE deleted_at IS NULL;
|
|
479
|
+
```
|
|
480
|
+
- **Functional indexes:** Normalize values you query by (e.g., `lower(email)`, `coalesce(status,'open')`).
|
|
481
|
+
```sql
|
|
482
|
+
CREATE INDEX users_email_lower_idx ON users ((lower(email)));
|
|
483
|
+
```
|
|
484
|
+
- **JSONB indexing:** Use GIN for key/path queries; keep payloads small and stable. Denormalize only what you measure.
|
|
485
|
+
- **Pagination:** Prefer keyset (cursor) pagination over deep `OFFSET`.
|
|
486
|
+
- **Connection pooling:** Pool size 10–20 for typical Node apps; use PgBouncer in transaction mode for serverless/concurrency spikes.
|
|
487
|
+
- **Analyze & vacuum:** Let autovacuum run; monitor table bloat on heavy update/delete tables.
|
|
488
|
+
---
|
|
489
|
+
## 14) Testing Strategy
|
|
490
|
+
- **Unit**: Zod schemas (valid/invalid cases).
|
|
491
|
+
- **Integration**: Spin up a Postgres (Docker) per test run, apply migrations, run DB tests.
|
|
492
|
+
- **Contract**: If exposing an API, snapshot response shapes (after Zod shaping).
|
|
493
|
+
|
|
494
|
+
Example unit test idea:
|
|
495
|
+
```ts
|
|
496
|
+
expect(InsertUser.safeParse({ email: "bad", name: "" }).success).toBe(false);
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
## 15) CI/CD & Release Management
|
|
501
|
+
- CI runs: typecheck, lint, unit tests, generate migrations, apply to test DB.
|
|
502
|
+
- Release: apply migrations before deploy (or during maintenance window).
|
|
503
|
+
- Keep rollback plan (backups or reversible migrations).
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
## 16) Conventions & Checklists
|
|
507
|
+
**Naming**
|
|
508
|
+
- Tables plural, columns snake_case.
|
|
509
|
+
- Foreign keys end with `_id`.
|
|
510
|
+
|
|
511
|
+
**Columns**
|
|
512
|
+
- `id uuid PK`, `created_at timestamptz not null default now()`.
|
|
513
|
+
- Optional: `updated_at` via trigger or application logic.
|
|
514
|
+
|
|
515
|
+
**Validation**
|
|
516
|
+
- Zod at every external boundary. Use `.coerce` for numbers/dates from strings.
|
|
517
|
+
|
|
518
|
+
**Routes**
|
|
519
|
+
- `POST` validates body; `GET` validates query params; respond with shaped DTO.
|
|
520
|
+
|
|
521
|
+
**Migrations**
|
|
522
|
+
- One migration per change; commit them; never edit applied migrations.
|
|
523
|
+
|
|
524
|
+
**Code review checklist**
|
|
525
|
+
- [ ] Zod schema exists for each endpoint input.
|
|
526
|
+
- [ ] DB constraints (unique/FK/check) match domain rules.
|
|
527
|
+
- [ ] Indexes exist for frequent queries.
|
|
528
|
+
- [ ] Errors mapped with correct HTTP codes.
|
|
529
|
+
- [ ] Transaction used for multi‑write operations.
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
## 17) Troubleshooting
|
|
533
|
+
- **Unique violation (23505)**: check for duplicate key; add pre‑check (optional) but rely on DB.
|
|
534
|
+
- **Foreign key (23503)**: ensure related row exists; check `onDelete` policy.
|
|
535
|
+
- **Decimal as string**: add transformer or parse before arithmetic.
|
|
536
|
+
- **Timezone drift**: standardize on UTC; use `timestamptz`.
|
|
537
|
+
- **Migration drift**: regenerate and re‑apply; avoid manual DB edits outside migrations.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
### Quick Start Summary
|
|
541
|
+
1. Create tables in `src/server/db/schema/*` (Drizzle).
|
|
542
|
+
2. Generate & run migrations (`db:generate`, `db:migrate`).
|
|
543
|
+
3. Create Zod schemas via `drizzle-zod`, add refinements.
|
|
544
|
+
4. Build services with Drizzle queries (+ transactions).
|
|
545
|
+
5. Validate requests with Zod in your API routes. Map DB errors.
|
|
546
|
+
6. Test locally (Docker Postgres), seed, then deploy.
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
## 18) Soft delete done right
|
|
550
|
+
- Add `deleted_at timestamptz`.
|
|
551
|
+
- Make uniqueness **partial** so it applies only to active rows.
|
|
552
|
+
- Ensure all read queries scope `deleted_at IS NULL` (wrap in views or repository functions).
|
|
553
|
+
|
|
554
|
+
```sql
|
|
555
|
+
ALTER TABLE users ADD COLUMN deleted_at timestamptz;
|
|
556
|
+
CREATE UNIQUE INDEX users_email_unq_active
|
|
557
|
+
ON users ((lower(email))) WHERE deleted_at IS NULL;
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
In services, always include a default scope; expose an explicit `{ withDeleted: true }` override when needed.
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
## 19) Multi‑tenancy & Row‑Level Security (RLS)
|
|
564
|
+
- Add `tenant_id uuid not null` to all tenant‑scoped tables.
|
|
565
|
+
- Make uniques composite: `(tenant_id, …)`.
|
|
566
|
+
- Enable RLS and isolate by a session variable.
|
|
567
|
+
|
|
568
|
+
```sql
|
|
569
|
+
ALTER TABLE issues ADD COLUMN tenant_id uuid NOT NULL;
|
|
570
|
+
ALTER TABLE issues ENABLE ROW LEVEL SECURITY;
|
|
571
|
+
|
|
572
|
+
CREATE POLICY tenant_isolation ON issues
|
|
573
|
+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
574
|
+
|
|
575
|
+
-- at session start (per request):
|
|
576
|
+
SELECT set_config('app.tenant_id', $1, true);
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
With Drizzle, call `db.execute(sql`select set_config('app.tenant_id', ${tenantId}, true)`)` right after acquiring a connection/transaction.
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
## 20) Operational hardening (timeouts, pools, retries)
|
|
583
|
+
Set strict timeouts to fail fast and avoid stuck connections:
|
|
584
|
+
|
|
585
|
+
```sql
|
|
586
|
+
ALTER ROLE app_user SET statement_timeout = '3s';
|
|
587
|
+
ALTER ROLE app_user SET lock_timeout = '1s';
|
|
588
|
+
ALTER ROLE app_user SET idle_in_transaction_session_timeout = '15s';
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
- Use short transactions; never hold connections across awaits that don't touch the DB.
|
|
592
|
+
- Retry on **serialization failures** (`40001`) with jitter; never retry on unique violations.
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
## 21) Security posture & roles
|
|
596
|
+
- Separate roles: `app_user` (read/write limited), `migrations` (DDL), `readonly` (analytics).
|
|
597
|
+
- Grant least privilege; schema‑qualify objects; avoid `public` schema for app data.
|
|
598
|
+
- Encrypt secrets; don’t log raw PII; consider column‑level encryption for high‑risk fields (pgcrypto/KMS).
|
|
599
|
+
|
|
600
|
+
```sql
|
|
601
|
+
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
|
602
|
+
CREATE SCHEMA app AUTHORIZATION migrations;
|
|
603
|
+
GRANT USAGE ON SCHEMA app TO app_user;
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
---
|
|
607
|
+
## 22) Observability & SLOs
|
|
608
|
+
- Enable `pg_stat_statements`; set `log_min_duration_statement` (e.g., 200ms in staging).
|
|
609
|
+
- Track p50/p95/p99 for key queries; alert on pool saturation and statement timeouts.
|
|
610
|
+
- Surface Zod errors as structured payloads; map Postgres codes (`23505`, `23503`, `40001`, `57014`) to sensible HTTP statuses.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
## 23) Upserts & conflict handling
|
|
614
|
+
Prefer explicit upserts; keep conflict targets aligned with your business keys.
|
|
615
|
+
|
|
616
|
+
```ts
|
|
617
|
+
// Drizzle example
|
|
618
|
+
await db.insert(users)
|
|
619
|
+
.values(data)
|
|
620
|
+
.onConflictDoUpdate({
|
|
621
|
+
target: users.email, // or (users.tenantId, users.email)
|
|
622
|
+
set: { name: data.name, updatedAt: new Date() },
|
|
623
|
+
});
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
For event logs, consider `ON CONFLICT DO NOTHING` with idempotency keys.
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
**Appendix: Numeric transformer example**
|
|
630
|
+
```ts
|
|
631
|
+
import { numeric } from "drizzle-orm/pg-core";
|
|
632
|
+
|
|
633
|
+
export const products = pgTable("products", {
|
|
634
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
635
|
+
price: numeric("price").$type<string>(), // pg returns string
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
// In Zod
|
|
639
|
+
export const InsertProduct = z.object({
|
|
640
|
+
price: z.union([z.number(), z.string()])
|
|
641
|
+
.transform((v) => (typeof v === "string" ? Number(v) : v))
|
|
642
|
+
.refine((n) => Number.isFinite(n) && n >= 0, "Invalid price"),
|
|
643
|
+
});
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
This document is your team’s baseline. Extend with your domain models (tenancy, roles, billing) and keep the flow: **Zod at the edges, Drizzle for data, Postgres for truth**.
|