solokit 0.1.1__py3-none-any.whl
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.
- solokit/__init__.py +10 -0
- solokit/__version__.py +3 -0
- solokit/cli.py +374 -0
- solokit/core/__init__.py +1 -0
- solokit/core/cache.py +102 -0
- solokit/core/command_runner.py +278 -0
- solokit/core/config.py +453 -0
- solokit/core/config_validator.py +204 -0
- solokit/core/constants.py +291 -0
- solokit/core/error_formatter.py +279 -0
- solokit/core/error_handlers.py +346 -0
- solokit/core/exceptions.py +1567 -0
- solokit/core/file_ops.py +309 -0
- solokit/core/logging_config.py +166 -0
- solokit/core/output.py +99 -0
- solokit/core/performance.py +57 -0
- solokit/core/protocols.py +141 -0
- solokit/core/types.py +312 -0
- solokit/deployment/__init__.py +1 -0
- solokit/deployment/executor.py +411 -0
- solokit/git/__init__.py +1 -0
- solokit/git/integration.py +619 -0
- solokit/init/__init__.py +41 -0
- solokit/init/claude_commands_installer.py +87 -0
- solokit/init/dependency_installer.py +313 -0
- solokit/init/docs_structure.py +90 -0
- solokit/init/env_generator.py +160 -0
- solokit/init/environment_validator.py +334 -0
- solokit/init/git_hooks_installer.py +71 -0
- solokit/init/git_setup.py +188 -0
- solokit/init/gitignore_updater.py +195 -0
- solokit/init/initial_commit.py +145 -0
- solokit/init/initial_scans.py +109 -0
- solokit/init/orchestrator.py +246 -0
- solokit/init/readme_generator.py +207 -0
- solokit/init/session_structure.py +239 -0
- solokit/init/template_installer.py +424 -0
- solokit/learning/__init__.py +1 -0
- solokit/learning/archiver.py +115 -0
- solokit/learning/categorizer.py +126 -0
- solokit/learning/curator.py +428 -0
- solokit/learning/extractor.py +352 -0
- solokit/learning/reporter.py +351 -0
- solokit/learning/repository.py +254 -0
- solokit/learning/similarity.py +342 -0
- solokit/learning/validator.py +144 -0
- solokit/project/__init__.py +1 -0
- solokit/project/init.py +1162 -0
- solokit/project/stack.py +436 -0
- solokit/project/sync_plugin.py +438 -0
- solokit/project/tree.py +375 -0
- solokit/quality/__init__.py +1 -0
- solokit/quality/api_validator.py +424 -0
- solokit/quality/checkers/__init__.py +25 -0
- solokit/quality/checkers/base.py +114 -0
- solokit/quality/checkers/context7.py +221 -0
- solokit/quality/checkers/custom.py +162 -0
- solokit/quality/checkers/deployment.py +323 -0
- solokit/quality/checkers/documentation.py +179 -0
- solokit/quality/checkers/formatting.py +161 -0
- solokit/quality/checkers/integration.py +394 -0
- solokit/quality/checkers/linting.py +159 -0
- solokit/quality/checkers/security.py +261 -0
- solokit/quality/checkers/spec_completeness.py +127 -0
- solokit/quality/checkers/tests.py +184 -0
- solokit/quality/env_validator.py +306 -0
- solokit/quality/gates.py +655 -0
- solokit/quality/reporters/__init__.py +10 -0
- solokit/quality/reporters/base.py +25 -0
- solokit/quality/reporters/console.py +98 -0
- solokit/quality/reporters/json_reporter.py +34 -0
- solokit/quality/results.py +98 -0
- solokit/session/__init__.py +1 -0
- solokit/session/briefing/__init__.py +245 -0
- solokit/session/briefing/documentation_loader.py +53 -0
- solokit/session/briefing/formatter.py +476 -0
- solokit/session/briefing/git_context.py +282 -0
- solokit/session/briefing/learning_loader.py +212 -0
- solokit/session/briefing/milestone_builder.py +78 -0
- solokit/session/briefing/orchestrator.py +137 -0
- solokit/session/briefing/stack_detector.py +51 -0
- solokit/session/briefing/tree_generator.py +52 -0
- solokit/session/briefing/work_item_loader.py +209 -0
- solokit/session/briefing.py +353 -0
- solokit/session/complete.py +1188 -0
- solokit/session/status.py +246 -0
- solokit/session/validate.py +452 -0
- solokit/templates/.claude/commands/end.md +109 -0
- solokit/templates/.claude/commands/init.md +159 -0
- solokit/templates/.claude/commands/learn-curate.md +88 -0
- solokit/templates/.claude/commands/learn-search.md +62 -0
- solokit/templates/.claude/commands/learn-show.md +69 -0
- solokit/templates/.claude/commands/learn.md +136 -0
- solokit/templates/.claude/commands/start.md +114 -0
- solokit/templates/.claude/commands/status.md +22 -0
- solokit/templates/.claude/commands/validate.md +27 -0
- solokit/templates/.claude/commands/work-delete.md +119 -0
- solokit/templates/.claude/commands/work-graph.md +139 -0
- solokit/templates/.claude/commands/work-list.md +26 -0
- solokit/templates/.claude/commands/work-new.md +114 -0
- solokit/templates/.claude/commands/work-next.md +25 -0
- solokit/templates/.claude/commands/work-show.md +24 -0
- solokit/templates/.claude/commands/work-update.md +141 -0
- solokit/templates/CHANGELOG.md +17 -0
- solokit/templates/WORK_ITEM_TYPES.md +141 -0
- solokit/templates/__init__.py +1 -0
- solokit/templates/bug_spec.md +217 -0
- solokit/templates/config.schema.json +150 -0
- solokit/templates/dashboard_refine/base/.gitignore +36 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/layout.tsx +22 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/page.tsx +68 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/users/page.tsx +77 -0
- solokit/templates/dashboard_refine/base/app/globals.css +60 -0
- solokit/templates/dashboard_refine/base/app/layout.tsx +23 -0
- solokit/templates/dashboard_refine/base/app/page.tsx +9 -0
- solokit/templates/dashboard_refine/base/components/client-refine-wrapper.tsx +21 -0
- solokit/templates/dashboard_refine/base/components/layout/header.tsx +44 -0
- solokit/templates/dashboard_refine/base/components/layout/sidebar.tsx +82 -0
- solokit/templates/dashboard_refine/base/components/ui/button.tsx +53 -0
- solokit/templates/dashboard_refine/base/components/ui/card.tsx +78 -0
- solokit/templates/dashboard_refine/base/components/ui/table.tsx +116 -0
- solokit/templates/dashboard_refine/base/components.json +16 -0
- solokit/templates/dashboard_refine/base/lib/refine.tsx +65 -0
- solokit/templates/dashboard_refine/base/lib/utils.ts +13 -0
- solokit/templates/dashboard_refine/base/next.config.ts +10 -0
- solokit/templates/dashboard_refine/base/package.json.template +40 -0
- solokit/templates/dashboard_refine/base/postcss.config.mjs +8 -0
- solokit/templates/dashboard_refine/base/providers/refine-provider.tsx +26 -0
- solokit/templates/dashboard_refine/base/tailwind.config.ts +57 -0
- solokit/templates/dashboard_refine/base/tsconfig.json +27 -0
- solokit/templates/dashboard_refine/docker/Dockerfile +57 -0
- solokit/templates/dashboard_refine/docker/docker-compose.prod.yml +31 -0
- solokit/templates/dashboard_refine/docker/docker-compose.yml +21 -0
- solokit/templates/dashboard_refine/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/dashboard_refine/tier-1-essential/package.json.tier1.template +57 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/setup.ts +26 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/unit/example.test.tsx +73 -0
- solokit/templates/dashboard_refine/tier-2-standard/package.json.tier2.template +62 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/eslint.config.mjs +22 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/package.json.tier3.template +79 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/stryker.conf.json +38 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/dashboard.spec.ts +88 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/user-management.spec.ts +102 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/integration/dashboard.test.tsx +90 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/type-coverage.json +16 -0
- solokit/templates/dashboard_refine/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/dashboard_refine/tier-4-production/k6/dashboard-load-test.js +70 -0
- solokit/templates/dashboard_refine/tier-4-production/next.config.ts +46 -0
- solokit/templates/dashboard_refine/tier-4-production/package.json.tier4.template +89 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/deployment_spec.md +500 -0
- solokit/templates/feature_spec.md +248 -0
- solokit/templates/fullstack_nextjs/base/.gitignore +36 -0
- solokit/templates/fullstack_nextjs/base/app/api/example/route.ts +65 -0
- solokit/templates/fullstack_nextjs/base/app/globals.css +27 -0
- solokit/templates/fullstack_nextjs/base/app/layout.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/app/page.tsx +32 -0
- solokit/templates/fullstack_nextjs/base/components/example-component.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/lib/prisma.ts +17 -0
- solokit/templates/fullstack_nextjs/base/lib/utils.ts +13 -0
- solokit/templates/fullstack_nextjs/base/lib/validations.ts +20 -0
- solokit/templates/fullstack_nextjs/base/next.config.ts +7 -0
- solokit/templates/fullstack_nextjs/base/package.json.template +32 -0
- solokit/templates/fullstack_nextjs/base/postcss.config.mjs +8 -0
- solokit/templates/fullstack_nextjs/base/prisma/schema.prisma +21 -0
- solokit/templates/fullstack_nextjs/base/tailwind.config.ts +19 -0
- solokit/templates/fullstack_nextjs/base/tsconfig.json +27 -0
- solokit/templates/fullstack_nextjs/docker/Dockerfile +60 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.prod.yml +57 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.yml +47 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/package.json.tier1.template +48 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/api/example.test.ts +88 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/unit/example.test.tsx +22 -0
- solokit/templates/fullstack_nextjs/tier-2-standard/package.json.tier2.template +52 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/package.json.tier3.template +68 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/stryker.conf.json +33 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/e2e/flow.spec.ts +59 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/integration/api.test.ts +165 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/fullstack_nextjs/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/fullstack_nextjs/tier-4-production/k6/load-test.js +45 -0
- solokit/templates/fullstack_nextjs/tier-4-production/next.config.ts +46 -0
- solokit/templates/fullstack_nextjs/tier-4-production/package.json.tier4.template +77 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/git-hooks/prepare-commit-msg +24 -0
- solokit/templates/integration_test_spec.md +363 -0
- solokit/templates/learnings.json +15 -0
- solokit/templates/ml_ai_fastapi/base/.gitignore +104 -0
- solokit/templates/ml_ai_fastapi/base/alembic/env.py +96 -0
- solokit/templates/ml_ai_fastapi/base/alembic.ini +114 -0
- solokit/templates/ml_ai_fastapi/base/pyproject.toml.template +91 -0
- solokit/templates/ml_ai_fastapi/base/requirements.txt.template +28 -0
- solokit/templates/ml_ai_fastapi/base/src/__init__.py +5 -0
- solokit/templates/ml_ai_fastapi/base/src/api/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/dependencies.py +20 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/example.py +134 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/health.py +66 -0
- solokit/templates/ml_ai_fastapi/base/src/core/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/core/config.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/core/database.py +50 -0
- solokit/templates/ml_ai_fastapi/base/src/main.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/models/__init__.py +7 -0
- solokit/templates/ml_ai_fastapi/base/src/models/example.py +61 -0
- solokit/templates/ml_ai_fastapi/base/src/services/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/services/example.py +115 -0
- solokit/templates/ml_ai_fastapi/docker/Dockerfile +59 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.prod.yml +112 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.yml +77 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyproject.toml.tier1.template +112 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyrightconfig.json +41 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pytest.ini +69 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/requirements-dev.txt +17 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/ruff.toml +81 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/conftest.py +72 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/test_main.py +49 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/test_example.py +113 -0
- solokit/templates/ml_ai_fastapi/tier-2-standard/pyproject.toml.tier2.template +130 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/locustfile.py +99 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/mutmut_config.py +53 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/pyproject.toml.tier3.template +150 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/conftest.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/test_api.py +131 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/pyproject.toml.tier4.template +162 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/requirements-prod.txt +25 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/api/routes/metrics.py +19 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/logging.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/monitoring.py +68 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/sentry.py +66 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/logging.py +79 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/tracing.py +60 -0
- solokit/templates/refactor_spec.md +287 -0
- solokit/templates/saas_t3/base/.gitignore +36 -0
- solokit/templates/saas_t3/base/app/api/trpc/[trpc]/route.ts +33 -0
- solokit/templates/saas_t3/base/app/globals.css +27 -0
- solokit/templates/saas_t3/base/app/layout.tsx +23 -0
- solokit/templates/saas_t3/base/app/page.tsx +31 -0
- solokit/templates/saas_t3/base/lib/api.tsx +77 -0
- solokit/templates/saas_t3/base/lib/utils.ts +13 -0
- solokit/templates/saas_t3/base/next.config.ts +7 -0
- solokit/templates/saas_t3/base/package.json.template +38 -0
- solokit/templates/saas_t3/base/postcss.config.mjs +8 -0
- solokit/templates/saas_t3/base/prisma/schema.prisma +20 -0
- solokit/templates/saas_t3/base/server/api/root.ts +19 -0
- solokit/templates/saas_t3/base/server/api/routers/example.ts +28 -0
- solokit/templates/saas_t3/base/server/api/trpc.ts +52 -0
- solokit/templates/saas_t3/base/server/db.ts +17 -0
- solokit/templates/saas_t3/base/tailwind.config.ts +19 -0
- solokit/templates/saas_t3/base/tsconfig.json +27 -0
- solokit/templates/saas_t3/docker/Dockerfile +60 -0
- solokit/templates/saas_t3/docker/docker-compose.prod.yml +59 -0
- solokit/templates/saas_t3/docker/docker-compose.yml +49 -0
- solokit/templates/saas_t3/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/saas_t3/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/saas_t3/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/saas_t3/tier-1-essential/package.json.tier1.template +54 -0
- solokit/templates/saas_t3/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/saas_t3/tier-1-essential/tests/unit/example.test.tsx +24 -0
- solokit/templates/saas_t3/tier-2-standard/package.json.tier2.template +58 -0
- solokit/templates/saas_t3/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/saas_t3/tier-3-comprehensive/package.json.tier3.template +74 -0
- solokit/templates/saas_t3/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/saas_t3/tier-3-comprehensive/stryker.conf.json +34 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/e2e/home.spec.ts +41 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/integration/api.test.ts +44 -0
- solokit/templates/saas_t3/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/saas_t3/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/saas_t3/tier-4-production/k6/load-test.js +51 -0
- solokit/templates/saas_t3/tier-4-production/next.config.ts +46 -0
- solokit/templates/saas_t3/tier-4-production/package.json.tier4.template +83 -0
- solokit/templates/saas_t3/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/saas_t3/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/vercel.json +37 -0
- solokit/templates/security_spec.md +287 -0
- solokit/templates/stack-versions.yaml +617 -0
- solokit/templates/status_update.json +6 -0
- solokit/templates/template-registry.json +257 -0
- solokit/templates/work_items.json +11 -0
- solokit/testing/__init__.py +1 -0
- solokit/testing/integration_runner.py +550 -0
- solokit/testing/performance.py +637 -0
- solokit/visualization/__init__.py +1 -0
- solokit/visualization/dependency_graph.py +788 -0
- solokit/work_items/__init__.py +1 -0
- solokit/work_items/creator.py +217 -0
- solokit/work_items/delete.py +264 -0
- solokit/work_items/get_dependencies.py +185 -0
- solokit/work_items/get_dependents.py +113 -0
- solokit/work_items/get_metadata.py +121 -0
- solokit/work_items/get_next_recommendations.py +133 -0
- solokit/work_items/manager.py +235 -0
- solokit/work_items/milestones.py +137 -0
- solokit/work_items/query.py +376 -0
- solokit/work_items/repository.py +267 -0
- solokit/work_items/scheduler.py +184 -0
- solokit/work_items/spec_parser.py +838 -0
- solokit/work_items/spec_validator.py +493 -0
- solokit/work_items/updater.py +157 -0
- solokit/work_items/validator.py +205 -0
- solokit-0.1.1.dist-info/METADATA +640 -0
- solokit-0.1.1.dist-info/RECORD +323 -0
- solokit-0.1.1.dist-info/WHEEL +5 -0
- solokit-0.1.1.dist-info/entry_points.txt +2 -0
- solokit-0.1.1.dist-info/licenses/LICENSE +21 -0
- solokit-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Request/Response logging middleware
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
from fastapi import Request, Response # type: ignore[import-not-found]
|
|
9
|
+
from src.core.logging import logger # type: ignore[import-not-found]
|
|
10
|
+
from starlette.middleware.base import BaseHTTPMiddleware # type: ignore[import-not-found]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LoggingMiddleware(BaseHTTPMiddleware):
|
|
14
|
+
"""
|
|
15
|
+
Middleware to log all HTTP requests and responses.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
19
|
+
"""
|
|
20
|
+
Process request and log details.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
request: FastAPI request
|
|
24
|
+
call_next: Next middleware/route handler
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Response: FastAPI response
|
|
28
|
+
"""
|
|
29
|
+
# Start timer
|
|
30
|
+
start_time = time.time()
|
|
31
|
+
|
|
32
|
+
# Extract request details
|
|
33
|
+
method = request.method
|
|
34
|
+
path = request.url.path
|
|
35
|
+
client_host = request.client.host if request.client else "unknown"
|
|
36
|
+
|
|
37
|
+
# Log incoming request
|
|
38
|
+
logger.info(
|
|
39
|
+
"request_started",
|
|
40
|
+
method=method,
|
|
41
|
+
path=path,
|
|
42
|
+
client=client_host,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Process request
|
|
46
|
+
try:
|
|
47
|
+
response = await call_next(request)
|
|
48
|
+
|
|
49
|
+
# Calculate duration
|
|
50
|
+
duration = time.time() - start_time
|
|
51
|
+
|
|
52
|
+
# Log successful response
|
|
53
|
+
logger.info(
|
|
54
|
+
"request_completed",
|
|
55
|
+
method=method,
|
|
56
|
+
path=path,
|
|
57
|
+
status_code=response.status_code,
|
|
58
|
+
duration=f"{duration:.3f}s",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Add custom headers
|
|
62
|
+
response.headers["X-Process-Time"] = str(duration)
|
|
63
|
+
|
|
64
|
+
return response
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
# Calculate duration
|
|
68
|
+
duration = time.time() - start_time
|
|
69
|
+
|
|
70
|
+
# Log error
|
|
71
|
+
logger.error(
|
|
72
|
+
"request_failed",
|
|
73
|
+
method=method,
|
|
74
|
+
path=path,
|
|
75
|
+
duration=f"{duration:.3f}s",
|
|
76
|
+
error=str(e),
|
|
77
|
+
exc_info=True,
|
|
78
|
+
)
|
|
79
|
+
raise
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenTelemetry tracing middleware
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
from fastapi import Request, Response # type: ignore[import-not-found]
|
|
8
|
+
from opentelemetry import trace # type: ignore[import-not-found]
|
|
9
|
+
from opentelemetry.instrumentation.fastapi import (
|
|
10
|
+
FastAPIInstrumentor, # type: ignore[import-not-found]
|
|
11
|
+
)
|
|
12
|
+
from src.core.config import settings # type: ignore[import-not-found]
|
|
13
|
+
from starlette.middleware.base import BaseHTTPMiddleware # type: ignore[import-not-found]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TracingMiddleware(BaseHTTPMiddleware):
|
|
17
|
+
"""
|
|
18
|
+
Middleware to add distributed tracing with OpenTelemetry.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
22
|
+
"""
|
|
23
|
+
Add tracing context to request.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
request: FastAPI request
|
|
27
|
+
call_next: Next middleware/route handler
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Response: FastAPI response
|
|
31
|
+
"""
|
|
32
|
+
tracer = trace.get_tracer(__name__)
|
|
33
|
+
|
|
34
|
+
with tracer.start_as_current_span(
|
|
35
|
+
f"{request.method} {request.url.path}",
|
|
36
|
+
attributes={
|
|
37
|
+
"http.method": request.method,
|
|
38
|
+
"http.url": str(request.url),
|
|
39
|
+
"http.client": request.client.host if request.client else "unknown",
|
|
40
|
+
},
|
|
41
|
+
) as span:
|
|
42
|
+
response = await call_next(request)
|
|
43
|
+
|
|
44
|
+
# Add response attributes to span
|
|
45
|
+
span.set_attribute("http.status_code", response.status_code)
|
|
46
|
+
|
|
47
|
+
return response
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def configure_tracing(app: Any) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Configure OpenTelemetry tracing for FastAPI application.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
app: FastAPI application instance
|
|
56
|
+
"""
|
|
57
|
+
# Only configure tracing in production
|
|
58
|
+
if settings.ENVIRONMENT != "development":
|
|
59
|
+
# Instrument FastAPI
|
|
60
|
+
FastAPIInstrumentor.instrument_app(app)
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Refactor: [Refactor Title]
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
TEMPLATE INSTRUCTIONS:
|
|
5
|
+
- Replace [Refactor Title] with a concise description of what's being refactored
|
|
6
|
+
- Include before/after code examples to show the improvement
|
|
7
|
+
- Clearly document why this refactor is valuable
|
|
8
|
+
- Be explicit about scope to avoid scope creep
|
|
9
|
+
- Remove these instructions before finalizing the spec
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
<!-- What is being refactored and why is it important? -->
|
|
15
|
+
|
|
16
|
+
What is being refactored and why?
|
|
17
|
+
|
|
18
|
+
**Example:**
|
|
19
|
+
> Refactor the UserService class to use dependency injection instead of direct instantiation of dependencies. This will improve testability, reduce coupling, and make the codebase more maintainable as we scale.
|
|
20
|
+
|
|
21
|
+
## Current State
|
|
22
|
+
|
|
23
|
+
<!-- Description of the current implementation with code examples -->
|
|
24
|
+
|
|
25
|
+
**Example:**
|
|
26
|
+
> The `UserService` class directly instantiates its dependencies (DatabaseClient, EmailService, CacheManager), making it difficult to test and tightly coupled to specific implementations.
|
|
27
|
+
|
|
28
|
+
**Current Code:**
|
|
29
|
+
```typescript
|
|
30
|
+
// src/services/UserService.ts (BEFORE)
|
|
31
|
+
class UserService {
|
|
32
|
+
private db: DatabaseClient;
|
|
33
|
+
private email: EmailService;
|
|
34
|
+
private cache: CacheManager;
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
// Hard-coded dependencies - difficult to test!
|
|
38
|
+
this.db = new DatabaseClient(config.database);
|
|
39
|
+
this.email = new EmailService(config.email);
|
|
40
|
+
this.cache = new CacheManager(config.redis);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async createUser(data: UserData): Promise<User> {
|
|
44
|
+
const user = await this.db.insert('users', data);
|
|
45
|
+
await this.email.sendWelcomeEmail(user.email);
|
|
46
|
+
await this.cache.set(`user:${user.id}`, user);
|
|
47
|
+
return user;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Problems with Current Approach
|
|
53
|
+
|
|
54
|
+
<!-- Specific issues with the current implementation -->
|
|
55
|
+
|
|
56
|
+
- **Tight Coupling:** Service is tightly coupled to specific implementations of DatabaseClient, EmailService, and CacheManager
|
|
57
|
+
- **Difficult to Test:** Cannot mock dependencies in unit tests without complex workarounds
|
|
58
|
+
- **Configuration Hardcoding:** Config is accessed directly, making it hard to override for different environments
|
|
59
|
+
- **Single Responsibility Violation:** Constructor is responsible for both dependency creation and initialization
|
|
60
|
+
- **No Interface Contracts:** Dependencies don't implement interfaces, making swapping implementations impossible
|
|
61
|
+
|
|
62
|
+
**Test Difficulty Example:**
|
|
63
|
+
```typescript
|
|
64
|
+
// Current test - requires real dependencies!
|
|
65
|
+
describe('UserService', () => {
|
|
66
|
+
it('creates a user', async () => {
|
|
67
|
+
const service = new UserService(); // Can't inject mocks!
|
|
68
|
+
// Test requires real database, email server, Redis...
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Proposed Refactor
|
|
74
|
+
|
|
75
|
+
### New Approach
|
|
76
|
+
|
|
77
|
+
<!-- Description of the refactored implementation with code examples -->
|
|
78
|
+
|
|
79
|
+
**Example:**
|
|
80
|
+
> Introduce dependency injection using constructor injection with interface-based dependencies. Create interfaces for each dependency and inject them through the constructor.
|
|
81
|
+
|
|
82
|
+
**Refactored Code:**
|
|
83
|
+
```typescript
|
|
84
|
+
// src/interfaces/IDatabase.ts (NEW)
|
|
85
|
+
interface IDatabase {
|
|
86
|
+
insert(table: string, data: any): Promise<any>;
|
|
87
|
+
find(table: string, id: string): Promise<any>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/interfaces/IEmailService.ts (NEW)
|
|
91
|
+
interface IEmailService {
|
|
92
|
+
sendWelcomeEmail(email: string): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/interfaces/ICacheManager.ts (NEW)
|
|
96
|
+
interface ICacheManager {
|
|
97
|
+
set(key: string, value: any): Promise<void>;
|
|
98
|
+
get(key: string): Promise<any>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/services/UserService.ts (AFTER)
|
|
102
|
+
class UserService {
|
|
103
|
+
constructor(
|
|
104
|
+
private db: IDatabase,
|
|
105
|
+
private email: IEmailService,
|
|
106
|
+
private cache: ICacheManager
|
|
107
|
+
) {
|
|
108
|
+
// Dependencies injected - easy to test!
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async createUser(data: UserData): Promise<User> {
|
|
112
|
+
const user = await this.db.insert('users', data);
|
|
113
|
+
await this.email.sendWelcomeEmail(user.email);
|
|
114
|
+
await this.cache.set(`user:${user.id}`, user);
|
|
115
|
+
return user;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/container.ts (NEW) - Dependency injection container
|
|
120
|
+
export const container = {
|
|
121
|
+
userService: new UserService(
|
|
122
|
+
new DatabaseClient(config.database),
|
|
123
|
+
new EmailService(config.email),
|
|
124
|
+
new CacheManager(config.redis)
|
|
125
|
+
)
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Benefits
|
|
130
|
+
|
|
131
|
+
<!-- Concrete benefits of the refactored approach -->
|
|
132
|
+
|
|
133
|
+
- **Testability:** Can easily inject mocks/stubs in tests
|
|
134
|
+
- **Loose Coupling:** Service depends on interfaces, not concrete implementations
|
|
135
|
+
- **Flexibility:** Easy to swap implementations (e.g., use different cache provider)
|
|
136
|
+
- **Maintainability:** Clear dependency contracts via interfaces
|
|
137
|
+
- **Single Responsibility:** Constructor only receives dependencies, doesn't create them
|
|
138
|
+
|
|
139
|
+
**Testing Improvement:**
|
|
140
|
+
```typescript
|
|
141
|
+
// New test - easy to mock dependencies!
|
|
142
|
+
describe('UserService', () => {
|
|
143
|
+
it('creates a user', async () => {
|
|
144
|
+
const mockDb = { insert: jest.fn().mockResolvedValue({ id: '123' }) };
|
|
145
|
+
const mockEmail = { sendWelcomeEmail: jest.fn() };
|
|
146
|
+
const mockCache = { set: jest.fn() };
|
|
147
|
+
|
|
148
|
+
const service = new UserService(mockDb, mockEmail, mockCache);
|
|
149
|
+
await service.createUser({ name: 'Test' });
|
|
150
|
+
|
|
151
|
+
expect(mockDb.insert).toHaveBeenCalled();
|
|
152
|
+
expect(mockEmail.sendWelcomeEmail).toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Trade-offs
|
|
158
|
+
|
|
159
|
+
<!-- Any downsides or considerations -->
|
|
160
|
+
|
|
161
|
+
- **Slightly More Boilerplate:** Need to define interfaces for dependencies
|
|
162
|
+
- **Container Setup:** Need to set up dependency injection container
|
|
163
|
+
- **Learning Curve:** Team needs to understand DI pattern
|
|
164
|
+
- **Migration Effort:** Existing code needs to be updated to use new pattern
|
|
165
|
+
|
|
166
|
+
## Implementation Plan
|
|
167
|
+
|
|
168
|
+
<!-- Step-by-step plan for implementing the refactor -->
|
|
169
|
+
|
|
170
|
+
1. **Phase 1: Create Interfaces** (0.5 sessions)
|
|
171
|
+
- Define `IDatabase`, `IEmailService`, `ICacheManager` interfaces
|
|
172
|
+
- Ensure existing implementations satisfy interfaces
|
|
173
|
+
|
|
174
|
+
2. **Phase 2: Refactor UserService** (0.5 sessions)
|
|
175
|
+
- Update `UserService` constructor to accept injected dependencies
|
|
176
|
+
- Update all methods to use injected dependencies
|
|
177
|
+
|
|
178
|
+
3. **Phase 3: Create DI Container** (0.5 sessions)
|
|
179
|
+
- Create `src/container.ts` with dependency wiring
|
|
180
|
+
- Update application entry point to use container
|
|
181
|
+
|
|
182
|
+
4. **Phase 4: Update Tests** (1 session)
|
|
183
|
+
- Refactor all UserService tests to use mocks
|
|
184
|
+
- Add new tests for edge cases now easy to test
|
|
185
|
+
|
|
186
|
+
5. **Phase 5: Update Callers** (0.5 sessions)
|
|
187
|
+
- Update all code that instantiates UserService
|
|
188
|
+
- Ensure all use container.userService instead
|
|
189
|
+
|
|
190
|
+
## Scope
|
|
191
|
+
|
|
192
|
+
### In Scope
|
|
193
|
+
|
|
194
|
+
<!-- What will be refactored in this work item -->
|
|
195
|
+
|
|
196
|
+
- UserService class refactoring
|
|
197
|
+
- Interface definitions for Database, EmailService, CacheManager
|
|
198
|
+
- Basic dependency injection container
|
|
199
|
+
- UserService test refactoring
|
|
200
|
+
- Documentation updates
|
|
201
|
+
|
|
202
|
+
### Out of Scope
|
|
203
|
+
|
|
204
|
+
<!-- What will NOT be refactored -->
|
|
205
|
+
|
|
206
|
+
- Refactoring other services (future work)
|
|
207
|
+
- Advanced DI container features (auto-wiring, decorators)
|
|
208
|
+
- Refactoring DatabaseClient, EmailService, CacheManager internals
|
|
209
|
+
- Performance optimization (separate work item)
|
|
210
|
+
|
|
211
|
+
## Risk Assessment
|
|
212
|
+
|
|
213
|
+
<!-- Identify and mitigate risks -->
|
|
214
|
+
|
|
215
|
+
- **Risk Level:** Medium
|
|
216
|
+
- **Risks:**
|
|
217
|
+
- Breaking existing functionality if dependency wiring is incorrect
|
|
218
|
+
- Tests might miss integration issues if mocking is too aggressive
|
|
219
|
+
- Performance regression if DI container adds overhead
|
|
220
|
+
- **Mitigation:**
|
|
221
|
+
- Comprehensive test coverage before and after refactor
|
|
222
|
+
- Integration tests to verify end-to-end functionality
|
|
223
|
+
- Performance benchmarks to detect regressions
|
|
224
|
+
- Gradual rollout with feature flag
|
|
225
|
+
- Code review focusing on dependency correctness
|
|
226
|
+
|
|
227
|
+
## Acceptance Criteria
|
|
228
|
+
|
|
229
|
+
<!-- Define specific, measurable criteria for considering this refactor complete -->
|
|
230
|
+
<!-- Minimum 3 items required for spec validation -->
|
|
231
|
+
|
|
232
|
+
- [ ] All code in scope has been refactored according to the proposed approach
|
|
233
|
+
- [ ] All existing functionality preserved (no breaking changes)
|
|
234
|
+
- [ ] All existing tests pass without modification
|
|
235
|
+
- [ ] Code quality metrics improved (complexity, coupling, testability)
|
|
236
|
+
- [ ] No performance regressions detected
|
|
237
|
+
- [ ] New tests added for previously untestable scenarios
|
|
238
|
+
- [ ] Code review completed and approved
|
|
239
|
+
- [ ] Documentation updated to reflect new structure
|
|
240
|
+
|
|
241
|
+
**Example criteria for a specific refactor:**
|
|
242
|
+
- [ ] UserService uses dependency injection with interface-based contracts
|
|
243
|
+
- [ ] All UserService dependencies injected through constructor
|
|
244
|
+
- [ ] Unit test code reduced by >30% due to improved mockability
|
|
245
|
+
- [ ] Cyclomatic complexity reduced from 12 to 6
|
|
246
|
+
- [ ] 100% test coverage maintained or improved
|
|
247
|
+
|
|
248
|
+
## Testing Strategy
|
|
249
|
+
|
|
250
|
+
<!-- How to verify the refactor doesn't break anything -->
|
|
251
|
+
|
|
252
|
+
### Automated Tests
|
|
253
|
+
- [ ] All existing UserService tests pass with new implementation
|
|
254
|
+
- [ ] New tests added for previously untestable scenarios
|
|
255
|
+
- [ ] Integration tests verify end-to-end functionality
|
|
256
|
+
- [ ] Performance tests show no regression (< 5% latency increase)
|
|
257
|
+
|
|
258
|
+
### Manual Testing
|
|
259
|
+
- [ ] User creation flow works end-to-end
|
|
260
|
+
- [ ] Error handling works correctly
|
|
261
|
+
- [ ] Email notifications are sent
|
|
262
|
+
- [ ] Cache is populated correctly
|
|
263
|
+
|
|
264
|
+
### Code Quality Metrics
|
|
265
|
+
- [ ] Cyclomatic complexity reduced by at least 20%
|
|
266
|
+
- [ ] Test coverage increased from 65% to 85%+
|
|
267
|
+
- [ ] No new linting errors introduced
|
|
268
|
+
- [ ] Code duplication reduced
|
|
269
|
+
|
|
270
|
+
## Dependencies
|
|
271
|
+
|
|
272
|
+
<!-- List any dependencies on other work -->
|
|
273
|
+
|
|
274
|
+
- None (this is a self-contained refactor)
|
|
275
|
+
|
|
276
|
+
## Estimated Effort
|
|
277
|
+
|
|
278
|
+
3 sessions
|
|
279
|
+
|
|
280
|
+
<!--
|
|
281
|
+
Breakdown:
|
|
282
|
+
- Phase 1: 0.5 sessions
|
|
283
|
+
- Phase 2: 0.5 sessions
|
|
284
|
+
- Phase 3: 0.5 sessions
|
|
285
|
+
- Phase 4: 1 session
|
|
286
|
+
- Phase 5: 0.5 sessions
|
|
287
|
+
-->
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.js
|
|
7
|
+
.yarn/install-state.gz
|
|
8
|
+
|
|
9
|
+
# testing
|
|
10
|
+
/coverage
|
|
11
|
+
|
|
12
|
+
# next.js
|
|
13
|
+
/.next/
|
|
14
|
+
/out/
|
|
15
|
+
|
|
16
|
+
# production
|
|
17
|
+
/build
|
|
18
|
+
|
|
19
|
+
# misc
|
|
20
|
+
.DS_Store
|
|
21
|
+
*.pem
|
|
22
|
+
|
|
23
|
+
# debug
|
|
24
|
+
npm-debug.log*
|
|
25
|
+
yarn-debug.log*
|
|
26
|
+
yarn-error.log*
|
|
27
|
+
|
|
28
|
+
# local env files
|
|
29
|
+
.env*.local
|
|
30
|
+
|
|
31
|
+
# vercel
|
|
32
|
+
.vercel
|
|
33
|
+
|
|
34
|
+
# typescript
|
|
35
|
+
*.tsbuildinfo
|
|
36
|
+
next-env.d.ts
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|
2
|
+
import { type NextRequest } from "next/server";
|
|
3
|
+
|
|
4
|
+
import { appRouter } from "@/server/api/root";
|
|
5
|
+
import { createTRPCContext } from "@/server/api/trpc";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
|
|
9
|
+
* handling a HTTP request (e.g. when you make requests from Client Components).
|
|
10
|
+
*/
|
|
11
|
+
const createContext = async (req: NextRequest) => {
|
|
12
|
+
return createTRPCContext({
|
|
13
|
+
headers: req.headers,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const handler = (req: NextRequest) =>
|
|
18
|
+
fetchRequestHandler({
|
|
19
|
+
endpoint: "/api/trpc",
|
|
20
|
+
req,
|
|
21
|
+
router: appRouter,
|
|
22
|
+
createContext: () => createContext(req),
|
|
23
|
+
onError:
|
|
24
|
+
process.env.NODE_ENV === "development"
|
|
25
|
+
? ({ path, error }) => {
|
|
26
|
+
console.error(
|
|
27
|
+
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
: undefined,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export { handler as GET, handler as POST };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
--background: #ffffff;
|
|
7
|
+
--foreground: #171717;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@media (prefers-color-scheme: dark) {
|
|
11
|
+
:root {
|
|
12
|
+
--background: #0a0a0a;
|
|
13
|
+
--foreground: #ededed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body {
|
|
18
|
+
color: var(--foreground);
|
|
19
|
+
background: var(--background);
|
|
20
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@layer utilities {
|
|
24
|
+
.text-balance {
|
|
25
|
+
text-wrap: balance;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { TRPCReactProvider } from "@/lib/api";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
|
|
5
|
+
export const metadata: Metadata = {
|
|
6
|
+
title: "Create T3 App",
|
|
7
|
+
description: "Generated by create-t3-app",
|
|
8
|
+
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function RootLayout({
|
|
12
|
+
children,
|
|
13
|
+
}: Readonly<{
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}>) {
|
|
16
|
+
return (
|
|
17
|
+
<html lang="en">
|
|
18
|
+
<body>
|
|
19
|
+
<TRPCReactProvider>{children}</TRPCReactProvider>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { api } from "@/lib/api";
|
|
4
|
+
|
|
5
|
+
export default function Home() {
|
|
6
|
+
const hello = api.example.hello.useQuery({ text: "from tRPC" });
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
|
|
10
|
+
<div className="container flex flex-col items-center justify-center gap-12 px-4 py-16">
|
|
11
|
+
<h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]">
|
|
12
|
+
Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
|
|
13
|
+
</h1>
|
|
14
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
|
|
15
|
+
<div className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20">
|
|
16
|
+
<h3 className="text-2xl font-bold">First Steps →</h3>
|
|
17
|
+
<div className="text-lg">
|
|
18
|
+
{hello.data ? hello.data.greeting : "Loading tRPC query..."}
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20">
|
|
22
|
+
<h3 className="text-2xl font-bold">Documentation →</h3>
|
|
23
|
+
<div className="text-lg">
|
|
24
|
+
Learn more about the T3 Stack and its components.
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</main>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
|
+
import { httpBatchLink, loggerLink } from "@trpc/client";
|
|
5
|
+
import { createTRPCReact } from "@trpc/react-query";
|
|
6
|
+
import { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import superjson from "superjson";
|
|
9
|
+
|
|
10
|
+
import type { AppRouter } from "@/server/api/root";
|
|
11
|
+
|
|
12
|
+
const createQueryClient = () => new QueryClient();
|
|
13
|
+
|
|
14
|
+
let clientQueryClientSingleton: QueryClient | undefined = undefined;
|
|
15
|
+
const getQueryClient = () => {
|
|
16
|
+
if (typeof window === "undefined") {
|
|
17
|
+
// Server: always make a new query client
|
|
18
|
+
return createQueryClient();
|
|
19
|
+
}
|
|
20
|
+
// Browser: use singleton pattern to keep the same query client
|
|
21
|
+
return (clientQueryClientSingleton ??= createQueryClient());
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const api = createTRPCReact<AppRouter>();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Inference helper for inputs.
|
|
28
|
+
*
|
|
29
|
+
* @example type HelloInput = RouterInputs['example']['hello']
|
|
30
|
+
*/
|
|
31
|
+
export type RouterInputs = inferRouterInputs<AppRouter>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Inference helper for outputs.
|
|
35
|
+
*
|
|
36
|
+
* @example type HelloOutput = RouterOutputs['example']['hello']
|
|
37
|
+
*/
|
|
38
|
+
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
|
39
|
+
|
|
40
|
+
export function TRPCReactProvider(props: { children: React.ReactNode }) {
|
|
41
|
+
const queryClient = getQueryClient();
|
|
42
|
+
|
|
43
|
+
const [trpcClient] = useState(() =>
|
|
44
|
+
api.createClient({
|
|
45
|
+
links: [
|
|
46
|
+
loggerLink({
|
|
47
|
+
enabled: (op) =>
|
|
48
|
+
process.env.NODE_ENV === "development" ||
|
|
49
|
+
(op.direction === "down" && op.result instanceof Error),
|
|
50
|
+
}),
|
|
51
|
+
httpBatchLink({
|
|
52
|
+
transformer: superjson,
|
|
53
|
+
url: getBaseUrl() + "/api/trpc",
|
|
54
|
+
headers: () => {
|
|
55
|
+
const headers = new Headers();
|
|
56
|
+
headers.set("x-trpc-source", "nextjs-react");
|
|
57
|
+
return headers;
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<QueryClientProvider client={queryClient}>
|
|
66
|
+
<api.Provider client={trpcClient} queryClient={queryClient}>
|
|
67
|
+
{props.children}
|
|
68
|
+
</api.Provider>
|
|
69
|
+
</QueryClientProvider>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getBaseUrl() {
|
|
74
|
+
if (typeof window !== "undefined") return window.location.origin;
|
|
75
|
+
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
|
|
76
|
+
return `http://localhost:${process.env.PORT ?? 3000}`;
|
|
77
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Utility function to merge Tailwind CSS classes
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* cn("px-2 py-1", "bg-blue-500") // "px-2 py-1 bg-blue-500"
|
|
9
|
+
* cn("px-2", condition && "py-1") // "px-2 py-1" or "px-2"
|
|
10
|
+
*/
|
|
11
|
+
export function cn(...inputs: ClassValue[]) {
|
|
12
|
+
return twMerge(clsx(inputs));
|
|
13
|
+
}
|