javi-forge 0.1.0
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/.gitignore.template +105 -0
- package/.releaserc +44 -0
- package/README.md +45 -0
- package/ai-config/.skillignore +15 -0
- package/ai-config/AUTO_INVOKE.md +300 -0
- package/ai-config/agents/_TEMPLATE.md +93 -0
- package/ai-config/agents/business/api-designer.md +1657 -0
- package/ai-config/agents/business/business-analyst.md +1331 -0
- package/ai-config/agents/business/product-strategist.md +206 -0
- package/ai-config/agents/business/project-manager.md +178 -0
- package/ai-config/agents/business/requirements-analyst.md +1277 -0
- package/ai-config/agents/business/technical-writer.md +1679 -0
- package/ai-config/agents/creative/ux-designer.md +205 -0
- package/ai-config/agents/data-ai/ai-engineer.md +487 -0
- package/ai-config/agents/data-ai/analytics-engineer.md +953 -0
- package/ai-config/agents/data-ai/data-engineer.md +173 -0
- package/ai-config/agents/data-ai/data-scientist.md +672 -0
- package/ai-config/agents/data-ai/mlops-engineer.md +814 -0
- package/ai-config/agents/data-ai/prompt-engineer.md +772 -0
- package/ai-config/agents/development/angular-expert.md +620 -0
- package/ai-config/agents/development/backend-architect.md +795 -0
- package/ai-config/agents/development/database-specialist.md +212 -0
- package/ai-config/agents/development/frontend-specialist.md +686 -0
- package/ai-config/agents/development/fullstack-engineer.md +668 -0
- package/ai-config/agents/development/golang-pro.md +338 -0
- package/ai-config/agents/development/java-enterprise.md +400 -0
- package/ai-config/agents/development/javascript-pro.md +422 -0
- package/ai-config/agents/development/nextjs-pro.md +474 -0
- package/ai-config/agents/development/python-pro.md +570 -0
- package/ai-config/agents/development/react-pro.md +487 -0
- package/ai-config/agents/development/rust-pro.md +246 -0
- package/ai-config/agents/development/spring-boot-4-expert.md +326 -0
- package/ai-config/agents/development/typescript-pro.md +336 -0
- package/ai-config/agents/development/vue-specialist.md +605 -0
- package/ai-config/agents/infrastructure/cloud-architect.md +472 -0
- package/ai-config/agents/infrastructure/deployment-manager.md +358 -0
- package/ai-config/agents/infrastructure/devops-engineer.md +455 -0
- package/ai-config/agents/infrastructure/incident-responder.md +519 -0
- package/ai-config/agents/infrastructure/kubernetes-expert.md +705 -0
- package/ai-config/agents/infrastructure/monitoring-specialist.md +674 -0
- package/ai-config/agents/infrastructure/performance-engineer.md +658 -0
- package/ai-config/agents/orchestrator.md +241 -0
- package/ai-config/agents/quality/accessibility-auditor.md +1204 -0
- package/ai-config/agents/quality/code-reviewer-compact.md +123 -0
- package/ai-config/agents/quality/code-reviewer.md +363 -0
- package/ai-config/agents/quality/dependency-manager.md +743 -0
- package/ai-config/agents/quality/e2e-test-specialist.md +1005 -0
- package/ai-config/agents/quality/performance-tester.md +1086 -0
- package/ai-config/agents/quality/security-auditor.md +133 -0
- package/ai-config/agents/quality/test-engineer.md +453 -0
- package/ai-config/agents/specialists/api-designer.md +87 -0
- package/ai-config/agents/specialists/backend-architect.md +73 -0
- package/ai-config/agents/specialists/code-reviewer.md +77 -0
- package/ai-config/agents/specialists/db-optimizer.md +75 -0
- package/ai-config/agents/specialists/devops-engineer.md +83 -0
- package/ai-config/agents/specialists/documentation-writer.md +78 -0
- package/ai-config/agents/specialists/frontend-developer.md +75 -0
- package/ai-config/agents/specialists/performance-analyst.md +82 -0
- package/ai-config/agents/specialists/refactor-specialist.md +74 -0
- package/ai-config/agents/specialists/security-auditor.md +74 -0
- package/ai-config/agents/specialists/test-engineer.md +81 -0
- package/ai-config/agents/specialists/ux-consultant.md +76 -0
- package/ai-config/agents/specialized/agent-generator.md +1190 -0
- package/ai-config/agents/specialized/blockchain-developer.md +149 -0
- package/ai-config/agents/specialized/code-migrator.md +892 -0
- package/ai-config/agents/specialized/context-manager.md +978 -0
- package/ai-config/agents/specialized/documentation-writer.md +1078 -0
- package/ai-config/agents/specialized/ecommerce-expert.md +1756 -0
- package/ai-config/agents/specialized/embedded-engineer.md +1714 -0
- package/ai-config/agents/specialized/error-detective.md +1034 -0
- package/ai-config/agents/specialized/fintech-specialist.md +1659 -0
- package/ai-config/agents/specialized/freelance-project-planner-v2.md +1988 -0
- package/ai-config/agents/specialized/freelance-project-planner-v3.md +2136 -0
- package/ai-config/agents/specialized/freelance-project-planner-v4.md +4503 -0
- package/ai-config/agents/specialized/freelance-project-planner.md +722 -0
- package/ai-config/agents/specialized/game-developer.md +1963 -0
- package/ai-config/agents/specialized/healthcare-dev.md +1620 -0
- package/ai-config/agents/specialized/mobile-developer.md +188 -0
- package/ai-config/agents/specialized/parallel-plan-executor.md +506 -0
- package/ai-config/agents/specialized/plan-executor.md +485 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +485 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +3493 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +778 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +918 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +1537 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +2633 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +5610 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +335 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +215 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +260 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +379 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +355 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +279 -0
- package/ai-config/agents/specialized/template-writer.md +347 -0
- package/ai-config/agents/specialized/test-runner.md +99 -0
- package/ai-config/agents/specialized/vibekanban-smart-worker.md +244 -0
- package/ai-config/agents/specialized/wave-executor.md +138 -0
- package/ai-config/agents/specialized/workflow-optimizer.md +1114 -0
- package/ai-config/commands/git/changelog.md +32 -0
- package/ai-config/commands/git/ci-local.md +70 -0
- package/ai-config/commands/git/commit.md +35 -0
- package/ai-config/commands/git/fix-issue.md +23 -0
- package/ai-config/commands/git/pr-create.md +42 -0
- package/ai-config/commands/git/pr-review.md +50 -0
- package/ai-config/commands/git/worktree.md +39 -0
- package/ai-config/commands/refactoring/cleanup.md +24 -0
- package/ai-config/commands/refactoring/dead-code.md +40 -0
- package/ai-config/commands/refactoring/extract.md +31 -0
- package/ai-config/commands/testing/e2e.md +30 -0
- package/ai-config/commands/testing/tdd.md +36 -0
- package/ai-config/commands/testing/test-coverage.md +30 -0
- package/ai-config/commands/testing/test-fix.md +24 -0
- package/ai-config/commands/workflow/generate-agents-md.md +85 -0
- package/ai-config/commands/workflow/planning.md +47 -0
- package/ai-config/commands/workflows/compound.md +89 -0
- package/ai-config/commands/workflows/plan.md +77 -0
- package/ai-config/commands/workflows/review.md +78 -0
- package/ai-config/commands/workflows/work.md +75 -0
- package/ai-config/config.yaml +18 -0
- package/ai-config/hooks/_TEMPLATE.md +96 -0
- package/ai-config/hooks/block-dangerous-commands.md +75 -0
- package/ai-config/hooks/commit-guard.md +90 -0
- package/ai-config/hooks/context-loader.md +73 -0
- package/ai-config/hooks/improve-prompt.md +91 -0
- package/ai-config/hooks/learning-log.md +72 -0
- package/ai-config/hooks/model-router.md +86 -0
- package/ai-config/hooks/secret-scanner.md +64 -0
- package/ai-config/hooks/skill-validator.md +102 -0
- package/ai-config/hooks/task-artifact.md +114 -0
- package/ai-config/hooks/validate-workflow.md +100 -0
- package/ai-config/prompts/base.md +71 -0
- package/ai-config/prompts/modes/debug.md +34 -0
- package/ai-config/prompts/modes/deploy.md +40 -0
- package/ai-config/prompts/modes/research.md +32 -0
- package/ai-config/prompts/modes/review.md +33 -0
- package/ai-config/prompts/review-policy.md +79 -0
- package/ai-config/skills/_TEMPLATE.md +157 -0
- package/ai-config/skills/backend/api-gateway/SKILL.md +254 -0
- package/ai-config/skills/backend/bff-concepts/SKILL.md +239 -0
- package/ai-config/skills/backend/bff-spring/SKILL.md +364 -0
- package/ai-config/skills/backend/chi-router/SKILL.md +396 -0
- package/ai-config/skills/backend/error-handling/SKILL.md +255 -0
- package/ai-config/skills/backend/exceptions-spring/SKILL.md +323 -0
- package/ai-config/skills/backend/fastapi/SKILL.md +302 -0
- package/ai-config/skills/backend/gateway-spring/SKILL.md +390 -0
- package/ai-config/skills/backend/go-backend/SKILL.md +457 -0
- package/ai-config/skills/backend/gradle-multimodule/SKILL.md +274 -0
- package/ai-config/skills/backend/graphql-concepts/SKILL.md +352 -0
- package/ai-config/skills/backend/graphql-spring/SKILL.md +398 -0
- package/ai-config/skills/backend/grpc-concepts/SKILL.md +283 -0
- package/ai-config/skills/backend/grpc-spring/SKILL.md +445 -0
- package/ai-config/skills/backend/jwt-auth/SKILL.md +412 -0
- package/ai-config/skills/backend/notifications-concepts/SKILL.md +259 -0
- package/ai-config/skills/backend/recommendations-concepts/SKILL.md +261 -0
- package/ai-config/skills/backend/search-concepts/SKILL.md +263 -0
- package/ai-config/skills/backend/search-spring/SKILL.md +375 -0
- package/ai-config/skills/backend/spring-boot-4/SKILL.md +172 -0
- package/ai-config/skills/backend/websockets/SKILL.md +532 -0
- package/ai-config/skills/data-ai/ai-ml/SKILL.md +423 -0
- package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +195 -0
- package/ai-config/skills/data-ai/analytics-spring/SKILL.md +340 -0
- package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +440 -0
- package/ai-config/skills/data-ai/langchain/SKILL.md +238 -0
- package/ai-config/skills/data-ai/mlflow/SKILL.md +302 -0
- package/ai-config/skills/data-ai/onnx-inference/SKILL.md +290 -0
- package/ai-config/skills/data-ai/powerbi/SKILL.md +352 -0
- package/ai-config/skills/data-ai/pytorch/SKILL.md +274 -0
- package/ai-config/skills/data-ai/scikit-learn/SKILL.md +321 -0
- package/ai-config/skills/data-ai/vector-db/SKILL.md +301 -0
- package/ai-config/skills/database/graph-databases/SKILL.md +218 -0
- package/ai-config/skills/database/graph-spring/SKILL.md +361 -0
- package/ai-config/skills/database/pgx-postgres/SKILL.md +512 -0
- package/ai-config/skills/database/redis-cache/SKILL.md +343 -0
- package/ai-config/skills/database/sqlite-embedded/SKILL.md +388 -0
- package/ai-config/skills/database/timescaledb/SKILL.md +320 -0
- package/ai-config/skills/docs/api-documentation/SKILL.md +293 -0
- package/ai-config/skills/docs/docs-spring/SKILL.md +377 -0
- package/ai-config/skills/docs/mustache-templates/SKILL.md +190 -0
- package/ai-config/skills/docs/technical-docs/SKILL.md +447 -0
- package/ai-config/skills/frontend/astro-ssr/SKILL.md +441 -0
- package/ai-config/skills/frontend/frontend-design/SKILL.md +54 -0
- package/ai-config/skills/frontend/frontend-web/SKILL.md +368 -0
- package/ai-config/skills/frontend/mantine-ui/SKILL.md +396 -0
- package/ai-config/skills/frontend/tanstack-query/SKILL.md +439 -0
- package/ai-config/skills/frontend/zod-validation/SKILL.md +417 -0
- package/ai-config/skills/frontend/zustand-state/SKILL.md +350 -0
- package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +244 -0
- package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +378 -0
- package/ai-config/skills/infrastructure/devops-infra/SKILL.md +435 -0
- package/ai-config/skills/infrastructure/docker-containers/SKILL.md +420 -0
- package/ai-config/skills/infrastructure/kubernetes/SKILL.md +456 -0
- package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +546 -0
- package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +474 -0
- package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +315 -0
- package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +504 -0
- package/ai-config/skills/mobile/mobile-ionic/SKILL.md +448 -0
- package/ai-config/skills/prompt-improver/SKILL.md +125 -0
- package/ai-config/skills/quality/ghagga-review/SKILL.md +216 -0
- package/ai-config/skills/references/hooks-patterns/SKILL.md +238 -0
- package/ai-config/skills/references/mcp-servers/SKILL.md +275 -0
- package/ai-config/skills/references/plugins-reference/SKILL.md +110 -0
- package/ai-config/skills/references/skills-reference/SKILL.md +420 -0
- package/ai-config/skills/references/subagent-templates/SKILL.md +193 -0
- package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +410 -0
- package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +408 -0
- package/ai-config/skills/systems-iot/rust-systems/SKILL.md +386 -0
- package/ai-config/skills/systems-iot/tokio-async/SKILL.md +324 -0
- package/ai-config/skills/testing/playwright-e2e/SKILL.md +289 -0
- package/ai-config/skills/testing/testcontainers/SKILL.md +299 -0
- package/ai-config/skills/testing/vitest-testing/SKILL.md +381 -0
- package/ai-config/skills/workflow/ci-local-guide/SKILL.md +118 -0
- package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +299 -0
- package/ai-config/skills/workflow/claude-md-improver/SKILL.md +158 -0
- package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +117 -0
- package/ai-config/skills/workflow/git-github/SKILL.md +334 -0
- package/ai-config/skills/workflow/git-github/references/examples.md +160 -0
- package/ai-config/skills/workflow/git-workflow/SKILL.md +214 -0
- package/ai-config/skills/workflow/ide-plugins/SKILL.md +277 -0
- package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +401 -0
- package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +199 -0
- package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +100 -0
- package/ai-config/skills/workflow/verification-before-completion/SKILL.md +73 -0
- package/ai-config/skills/workflow/wave-workflow/SKILL.md +178 -0
- package/ci-local/README.md +170 -0
- package/ci-local/ci-local.sh +297 -0
- package/ci-local/hooks/commit-msg +74 -0
- package/ci-local/hooks/pre-commit +162 -0
- package/ci-local/hooks/pre-push +41 -0
- package/ci-local/install.sh +49 -0
- package/ci-local/semgrep.yml +214 -0
- package/dist/commands/analyze.d.ts +9 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +55 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/analyze.test.d.ts +2 -0
- package/dist/commands/analyze.test.d.ts.map +1 -0
- package/dist/commands/analyze.test.js +145 -0
- package/dist/commands/analyze.test.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +158 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +2 -0
- package/dist/commands/doctor.test.d.ts.map +1 -0
- package/dist/commands/doctor.test.js +200 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +283 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +271 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +201 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +57 -0
- package/dist/constants.js.map +1 -0
- package/dist/e2e/aggressive.e2e.test.d.ts +2 -0
- package/dist/e2e/aggressive.e2e.test.d.ts.map +1 -0
- package/dist/e2e/aggressive.e2e.test.js +350 -0
- package/dist/e2e/aggressive.e2e.test.js.map +1 -0
- package/dist/e2e/commands.e2e.test.d.ts +2 -0
- package/dist/e2e/commands.e2e.test.d.ts.map +1 -0
- package/dist/e2e/commands.e2e.test.js +213 -0
- package/dist/e2e/commands.e2e.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/common.d.ts +17 -0
- package/dist/lib/common.d.ts.map +1 -0
- package/dist/lib/common.js +111 -0
- package/dist/lib/common.js.map +1 -0
- package/dist/lib/common.test.d.ts +2 -0
- package/dist/lib/common.test.d.ts.map +1 -0
- package/dist/lib/common.test.js +316 -0
- package/dist/lib/common.test.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +18 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js +61 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/frontmatter.test.d.ts +2 -0
- package/dist/lib/frontmatter.test.d.ts.map +1 -0
- package/dist/lib/frontmatter.test.js +257 -0
- package/dist/lib/frontmatter.test.js.map +1 -0
- package/dist/lib/template.d.ts +24 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +78 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/lib/template.test.d.ts +2 -0
- package/dist/lib/template.test.d.ts.map +1 -0
- package/dist/lib/template.test.js +201 -0
- package/dist/lib/template.test.js.map +1 -0
- package/dist/types/index.d.ts +48 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/AnalyzeUI.d.ts +7 -0
- package/dist/ui/AnalyzeUI.d.ts.map +1 -0
- package/dist/ui/AnalyzeUI.js +100 -0
- package/dist/ui/AnalyzeUI.js.map +1 -0
- package/dist/ui/App.d.ts +13 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +100 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/CIContext.d.ts +9 -0
- package/dist/ui/CIContext.d.ts.map +1 -0
- package/dist/ui/CIContext.js +9 -0
- package/dist/ui/CIContext.js.map +1 -0
- package/dist/ui/CISelector.d.ts +8 -0
- package/dist/ui/CISelector.d.ts.map +1 -0
- package/dist/ui/CISelector.js +45 -0
- package/dist/ui/CISelector.js.map +1 -0
- package/dist/ui/Doctor.d.ts +3 -0
- package/dist/ui/Doctor.d.ts.map +1 -0
- package/dist/ui/Doctor.js +89 -0
- package/dist/ui/Doctor.js.map +1 -0
- package/dist/ui/Header.d.ts +8 -0
- package/dist/ui/Header.d.ts.map +1 -0
- package/dist/ui/Header.js +30 -0
- package/dist/ui/Header.js.map +1 -0
- package/dist/ui/MemorySelector.d.ts +8 -0
- package/dist/ui/MemorySelector.d.ts.map +1 -0
- package/dist/ui/MemorySelector.js +46 -0
- package/dist/ui/MemorySelector.js.map +1 -0
- package/dist/ui/NameInput.d.ts +8 -0
- package/dist/ui/NameInput.d.ts.map +1 -0
- package/dist/ui/NameInput.js +69 -0
- package/dist/ui/NameInput.js.map +1 -0
- package/dist/ui/OptionSelector.d.ts +12 -0
- package/dist/ui/OptionSelector.d.ts.map +1 -0
- package/dist/ui/OptionSelector.js +69 -0
- package/dist/ui/OptionSelector.js.map +1 -0
- package/dist/ui/Progress.d.ts +11 -0
- package/dist/ui/Progress.d.ts.map +1 -0
- package/dist/ui/Progress.js +58 -0
- package/dist/ui/Progress.js.map +1 -0
- package/dist/ui/StackSelector.d.ts +9 -0
- package/dist/ui/StackSelector.d.ts.map +1 -0
- package/dist/ui/StackSelector.js +65 -0
- package/dist/ui/StackSelector.js.map +1 -0
- package/dist/ui/Summary.d.ts +12 -0
- package/dist/ui/Summary.d.ts.map +1 -0
- package/dist/ui/Summary.js +114 -0
- package/dist/ui/Summary.js.map +1 -0
- package/dist/ui/SyncUI.d.ts +10 -0
- package/dist/ui/SyncUI.d.ts.map +1 -0
- package/dist/ui/SyncUI.js +64 -0
- package/dist/ui/SyncUI.js.map +1 -0
- package/dist/ui/Welcome.d.ts +7 -0
- package/dist/ui/Welcome.d.ts.map +1 -0
- package/dist/ui/Welcome.js +45 -0
- package/dist/ui/Welcome.js.map +1 -0
- package/dist/ui/theme.d.ts +10 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +9 -0
- package/dist/ui/theme.js.map +1 -0
- package/modules/engram/.gitignore-snippet.txt +6 -0
- package/modules/engram/.mcp-config-snippet.json +11 -0
- package/modules/engram/README.md +146 -0
- package/modules/engram/install-engram.sh +216 -0
- package/modules/ghagga/.env.example +43 -0
- package/modules/ghagga/README.md +153 -0
- package/modules/ghagga/docker-compose.yml +80 -0
- package/modules/ghagga/setup-ghagga.sh +139 -0
- package/modules/memory-simple/.project/NOTES.md +22 -0
- package/modules/memory-simple/README.md +23 -0
- package/modules/obsidian-brain/.obsidian/app.json +23 -0
- package/modules/obsidian-brain/.obsidian/appearance.json +5 -0
- package/modules/obsidian-brain/.obsidian/bookmarks.json +34 -0
- package/modules/obsidian-brain/.obsidian/community-plugins.json +1 -0
- package/modules/obsidian-brain/.obsidian/core-plugins-migration.json +21 -0
- package/modules/obsidian-brain/.obsidian/core-plugins.json +18 -0
- package/modules/obsidian-brain/.obsidian/daily-notes.json +5 -0
- package/modules/obsidian-brain/.obsidian/graph.json +37 -0
- package/modules/obsidian-brain/.obsidian/hotkeys.json +14 -0
- package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +25 -0
- package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +29 -0
- package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +18 -0
- package/modules/obsidian-brain/.obsidian/snippets/project-memory.css +71 -0
- package/modules/obsidian-brain/.obsidian-gitignore-snippet.txt +8 -0
- package/modules/obsidian-brain/.project/Attachments/.gitkeep +0 -0
- package/modules/obsidian-brain/.project/Memory/BLOCKERS.md +78 -0
- package/modules/obsidian-brain/.project/Memory/CONTEXT.md +102 -0
- package/modules/obsidian-brain/.project/Memory/DASHBOARD.md +73 -0
- package/modules/obsidian-brain/.project/Memory/DECISIONS.md +87 -0
- package/modules/obsidian-brain/.project/Memory/KANBAN.md +15 -0
- package/modules/obsidian-brain/.project/Memory/README.md +61 -0
- package/modules/obsidian-brain/.project/Memory/WAVES.md +78 -0
- package/modules/obsidian-brain/.project/Sessions/TEMPLATE.md +99 -0
- package/modules/obsidian-brain/.project/Templates/ADR.md +33 -0
- package/modules/obsidian-brain/.project/Templates/Blocker.md +21 -0
- package/modules/obsidian-brain/.project/Templates/Session.md +88 -0
- package/modules/obsidian-brain/README.md +268 -0
- package/modules/obsidian-brain/new-wave.sh +182 -0
- package/package.json +51 -0
- package/schemas/agent.schema.json +34 -0
- package/schemas/ai-config.schema.json +28 -0
- package/schemas/skill.schema.json +44 -0
- package/src/commands/analyze.test.ts +145 -0
- package/src/commands/analyze.ts +69 -0
- package/src/commands/doctor.test.ts +208 -0
- package/src/commands/doctor.ts +163 -0
- package/src/commands/init.test.ts +298 -0
- package/src/commands/init.ts +285 -0
- package/src/constants.ts +69 -0
- package/src/e2e/aggressive.e2e.test.ts +557 -0
- package/src/e2e/commands.e2e.test.ts +298 -0
- package/src/index.tsx +106 -0
- package/src/lib/common.test.ts +318 -0
- package/src/lib/common.ts +127 -0
- package/src/lib/frontmatter.test.ts +291 -0
- package/src/lib/frontmatter.ts +77 -0
- package/src/lib/template.test.ts +226 -0
- package/src/lib/template.ts +99 -0
- package/src/types/index.ts +53 -0
- package/src/ui/AnalyzeUI.tsx +133 -0
- package/src/ui/App.tsx +175 -0
- package/src/ui/CIContext.tsx +25 -0
- package/src/ui/CISelector.tsx +72 -0
- package/src/ui/Doctor.tsx +122 -0
- package/src/ui/Header.tsx +48 -0
- package/src/ui/MemorySelector.tsx +73 -0
- package/src/ui/NameInput.tsx +82 -0
- package/src/ui/OptionSelector.tsx +100 -0
- package/src/ui/Progress.tsx +88 -0
- package/src/ui/StackSelector.tsx +101 -0
- package/src/ui/Summary.tsx +134 -0
- package/src/ui/Welcome.tsx +54 -0
- package/src/ui/theme.ts +10 -0
- package/stryker.config.json +19 -0
- package/tasks/_TEMPLATE/files-edited.md +3 -0
- package/tasks/_TEMPLATE/plan.md +3 -0
- package/tasks/_TEMPLATE/research.md +3 -0
- package/tasks/_TEMPLATE/verification.md +5 -0
- package/templates/common/dependabot/cargo.yml +11 -0
- package/templates/common/dependabot/github-actions.yml +16 -0
- package/templates/common/dependabot/gomod.yml +15 -0
- package/templates/common/dependabot/gradle.yml +15 -0
- package/templates/common/dependabot/header.yml +3 -0
- package/templates/common/dependabot/maven.yml +15 -0
- package/templates/common/dependabot/npm.yml +20 -0
- package/templates/common/dependabot/pip.yml +11 -0
- package/templates/dependabot.yml +162 -0
- package/templates/github/ci-go.yml +41 -0
- package/templates/github/ci-java.yml +45 -0
- package/templates/github/ci-monorepo.yml +150 -0
- package/templates/github/ci-node.yml +42 -0
- package/templates/github/ci-python.yml +42 -0
- package/templates/github/ci-rust.yml +42 -0
- package/templates/github/dependabot-automerge.yml +40 -0
- package/templates/gitlab/gitlab-ci-go.yml +88 -0
- package/templates/gitlab/gitlab-ci-java.yml +79 -0
- package/templates/gitlab/gitlab-ci-monorepo.yml +126 -0
- package/templates/gitlab/gitlab-ci-node.yml +63 -0
- package/templates/gitlab/gitlab-ci-python.yml +147 -0
- package/templates/gitlab/gitlab-ci-rust.yml +67 -0
- package/templates/global/claude-settings.json +98 -0
- package/templates/global/codex-config.toml +8 -0
- package/templates/global/copilot-instructions/base-rules.instructions.md +13 -0
- package/templates/global/copilot-instructions/sdd-orchestrator.instructions.md +37 -0
- package/templates/global/gemini-commands/cleanup.toml +20 -0
- package/templates/global/gemini-commands/commit.toml +15 -0
- package/templates/global/gemini-commands/dead-code.toml +22 -0
- package/templates/global/gemini-commands/plan.toml +30 -0
- package/templates/global/gemini-commands/review.toml +17 -0
- package/templates/global/gemini-commands/sdd-apply.toml +22 -0
- package/templates/global/gemini-commands/sdd-ff.toml +14 -0
- package/templates/global/gemini-commands/sdd-new.toml +21 -0
- package/templates/global/gemini-commands/sdd-verify.toml +21 -0
- package/templates/global/gemini-commands/tdd.toml +26 -0
- package/templates/global/gemini-settings.json +8 -0
- package/templates/global/opencode-config.json +44 -0
- package/templates/global/sdd-instructions.md +47 -0
- package/templates/global/sdd-orchestrator-claude.md +46 -0
- package/templates/global/sdd-orchestrator-copilot.md +34 -0
- package/templates/renovate.json +69 -0
- package/templates/woodpecker/monorepo/backend.yml +34 -0
- package/templates/woodpecker/monorepo/frontend.yml +34 -0
- package/templates/woodpecker/monorepo/summary.yml +25 -0
- package/templates/woodpecker/woodpecker-go.yml +51 -0
- package/templates/woodpecker/woodpecker-java.yml +67 -0
- package/templates/woodpecker/woodpecker-node.yml +47 -0
- package/templates/woodpecker/woodpecker-python.yml +108 -0
- package/templates/woodpecker/woodpecker-rust.yml +57 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +16 -0
- package/workflows/reusable-build-go.yml +111 -0
- package/workflows/reusable-build-java.yml +120 -0
- package/workflows/reusable-build-node.yml +145 -0
- package/workflows/reusable-build-python.yml +159 -0
- package/workflows/reusable-build-rust.yml +135 -0
- package/workflows/reusable-docker.yml +120 -0
- package/workflows/reusable-ghagga-review.yml +165 -0
- package/workflows/reusable-release.yml +91 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bff-concepts
|
|
3
|
+
description: >
|
|
4
|
+
Backend for Frontend pattern. Client-specific APIs, response tailoring, aggregation.
|
|
5
|
+
Trigger: BFF, backend for frontend, aggregation, client-specific, mobile API
|
|
6
|
+
tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Edit
|
|
10
|
+
- Grep
|
|
11
|
+
metadata:
|
|
12
|
+
author: apigen-team
|
|
13
|
+
version: "1.0"
|
|
14
|
+
tags: [bff, architecture, api-design, microservices]
|
|
15
|
+
scope: ["**/bff/**"]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Backend for Frontend (BFF) Pattern
|
|
19
|
+
|
|
20
|
+
## Core Concept
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Traditional API:
|
|
24
|
+
┌─────────────────────────────────────────┐
|
|
25
|
+
│ Single Generic API │
|
|
26
|
+
│ (serves Web, Mobile, TV, Partners) │
|
|
27
|
+
└─────────────────────────────────────────┘
|
|
28
|
+
↓ ↓ ↓
|
|
29
|
+
[Web] [Mobile] [TV]
|
|
30
|
+
(over-fetching + under-fetching)
|
|
31
|
+
|
|
32
|
+
BFF Pattern:
|
|
33
|
+
┌───────────┐ ┌───────────┐ ┌───────────┐
|
|
34
|
+
│ Web BFF │ │Mobile BFF │ │ TV BFF │
|
|
35
|
+
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
|
|
36
|
+
↓ ↓ ↓
|
|
37
|
+
┌─────────────────────────────────────────┐
|
|
38
|
+
│ Backend Microservices │
|
|
39
|
+
└─────────────────────────────────────────┘
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Client Types
|
|
43
|
+
|
|
44
|
+
### Web BFF
|
|
45
|
+
```
|
|
46
|
+
Characteristics:
|
|
47
|
+
- Full data payloads (fast connections)
|
|
48
|
+
- Rich interaction capabilities
|
|
49
|
+
- SEO requirements (SSR data)
|
|
50
|
+
- Complex filtering/sorting UI
|
|
51
|
+
|
|
52
|
+
Typical operations:
|
|
53
|
+
- Large paginated lists
|
|
54
|
+
- Real-time updates (WebSocket)
|
|
55
|
+
- Complex search queries
|
|
56
|
+
- File uploads
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Mobile BFF
|
|
60
|
+
```
|
|
61
|
+
Characteristics:
|
|
62
|
+
- Bandwidth optimization
|
|
63
|
+
- Battery conservation
|
|
64
|
+
- Offline support (sync)
|
|
65
|
+
- Push notifications
|
|
66
|
+
|
|
67
|
+
Optimizations:
|
|
68
|
+
- Compressed responses
|
|
69
|
+
- Delta sync (only changes)
|
|
70
|
+
- Image CDN URLs (different sizes)
|
|
71
|
+
- Aggressive caching headers
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### IoT / Embedded BFF
|
|
75
|
+
```
|
|
76
|
+
Characteristics:
|
|
77
|
+
- Minimal payloads
|
|
78
|
+
- Low memory footprint
|
|
79
|
+
- Binary protocols (gRPC/MQTT)
|
|
80
|
+
- Intermittent connectivity
|
|
81
|
+
|
|
82
|
+
Patterns:
|
|
83
|
+
- Batch operations
|
|
84
|
+
- Store-and-forward
|
|
85
|
+
- Heartbeat polling
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Response Tailoring
|
|
89
|
+
|
|
90
|
+
### Field Selection
|
|
91
|
+
```json
|
|
92
|
+
// Web: Full response
|
|
93
|
+
{
|
|
94
|
+
"user": {
|
|
95
|
+
"id": "123",
|
|
96
|
+
"name": "John Doe",
|
|
97
|
+
"email": "john@example.com",
|
|
98
|
+
"avatar": "https://...",
|
|
99
|
+
"bio": "...",
|
|
100
|
+
"preferences": {...},
|
|
101
|
+
"stats": {...}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Mobile: Tailored response
|
|
106
|
+
{
|
|
107
|
+
"user": {
|
|
108
|
+
"id": "123",
|
|
109
|
+
"name": "John Doe",
|
|
110
|
+
"avatar_thumb": "https://.../48x48"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Aggregation Strategies
|
|
116
|
+
```
|
|
117
|
+
Sequential:
|
|
118
|
+
1. Get user profile
|
|
119
|
+
2. Get user's orders
|
|
120
|
+
3. Get recommendations
|
|
121
|
+
4. Combine and return
|
|
122
|
+
|
|
123
|
+
Parallel:
|
|
124
|
+
1. Fork: [profile, orders, recommendations]
|
|
125
|
+
2. Join: Combine results
|
|
126
|
+
3. Return aggregated response
|
|
127
|
+
|
|
128
|
+
Conditional:
|
|
129
|
+
1. Get user profile
|
|
130
|
+
2. IF premium user THEN get recommendations
|
|
131
|
+
3. Return appropriate response
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Rate Limiting per Client
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Web clients:
|
|
138
|
+
- 1000 req/min (authenticated)
|
|
139
|
+
- 100 req/min (anonymous)
|
|
140
|
+
|
|
141
|
+
Mobile clients:
|
|
142
|
+
- 500 req/min (aggressive caching expected)
|
|
143
|
+
|
|
144
|
+
Partner APIs:
|
|
145
|
+
- Per-contract limits
|
|
146
|
+
- Separate quotas per partner
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Query Composition
|
|
150
|
+
|
|
151
|
+
### GraphQL-like Behavior
|
|
152
|
+
```
|
|
153
|
+
Request:
|
|
154
|
+
GET /bff/dashboard?include=profile,notifications,orders
|
|
155
|
+
|
|
156
|
+
Response:
|
|
157
|
+
{
|
|
158
|
+
"profile": {...},
|
|
159
|
+
"notifications": [...],
|
|
160
|
+
"orders": [...]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Backend calls:
|
|
164
|
+
- GET /users/me → profile
|
|
165
|
+
- GET /notifications?limit=5 → notifications
|
|
166
|
+
- GET /orders?status=pending → orders
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Partial Failure Handling
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"profile": {...},
|
|
173
|
+
"notifications": {
|
|
174
|
+
"_error": "Service temporarily unavailable",
|
|
175
|
+
"_fallback": []
|
|
176
|
+
},
|
|
177
|
+
"orders": [...]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Caching Strategies
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
Per-client-type:
|
|
185
|
+
- Web: 5 min cache, ETags
|
|
186
|
+
- Mobile: 30 min cache, stale-while-revalidate
|
|
187
|
+
- IoT: 1 hour cache
|
|
188
|
+
|
|
189
|
+
Per-resource:
|
|
190
|
+
- User profile: 5 min
|
|
191
|
+
- Product catalog: 1 hour
|
|
192
|
+
- Recommendations: 15 min
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Implementation Patterns
|
|
196
|
+
|
|
197
|
+
### Request/Response DTOs
|
|
198
|
+
```
|
|
199
|
+
// Client-specific DTOs
|
|
200
|
+
WebUserResponse:
|
|
201
|
+
- All fields
|
|
202
|
+
- Nested objects
|
|
203
|
+
- Full links
|
|
204
|
+
|
|
205
|
+
MobileUserResponse:
|
|
206
|
+
- Essential fields only
|
|
207
|
+
- IDs instead of nested objects
|
|
208
|
+
- Thumbnail URLs
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Service Orchestration
|
|
212
|
+
```
|
|
213
|
+
BFF Controller:
|
|
214
|
+
1. Parse client context (User-Agent, Accept)
|
|
215
|
+
2. Determine required data
|
|
216
|
+
3. Call services (parallel when possible)
|
|
217
|
+
4. Apply transformations
|
|
218
|
+
5. Return tailored response
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Anti-patterns to Avoid
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
❌ Business logic in BFF
|
|
225
|
+
→ BFF should only orchestrate and transform
|
|
226
|
+
|
|
227
|
+
❌ Direct database access
|
|
228
|
+
→ Always go through services
|
|
229
|
+
|
|
230
|
+
❌ Shared BFF for different clients
|
|
231
|
+
→ Each client type needs its own BFF
|
|
232
|
+
|
|
233
|
+
❌ Duplicating validation
|
|
234
|
+
→ Let backend services handle validation
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Related Skills
|
|
238
|
+
|
|
239
|
+
- `bff-spring`: Spring Boot BFF implementation
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bff-spring
|
|
3
|
+
description: >
|
|
4
|
+
Spring Boot BFF implementation. Client detection, response tailoring, service composition.
|
|
5
|
+
Trigger: apigen-bff, BaseBffController, ClientType, TailorForClient, aggregation
|
|
6
|
+
tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Edit
|
|
10
|
+
- Bash
|
|
11
|
+
- Grep
|
|
12
|
+
metadata:
|
|
13
|
+
author: apigen-team
|
|
14
|
+
version: "1.0"
|
|
15
|
+
tags: [bff, spring-boot, aggregation, java]
|
|
16
|
+
scope: ["apigen-bff/**"]
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# BFF Spring Boot (apigen-bff)
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
apigen:
|
|
25
|
+
bff:
|
|
26
|
+
enabled: true
|
|
27
|
+
|
|
28
|
+
client-detection:
|
|
29
|
+
header: X-Client-Type
|
|
30
|
+
fallback: WEB
|
|
31
|
+
|
|
32
|
+
rate-limits:
|
|
33
|
+
web:
|
|
34
|
+
requests-per-minute: 1000
|
|
35
|
+
mobile:
|
|
36
|
+
requests-per-minute: 500
|
|
37
|
+
iot:
|
|
38
|
+
requests-per-minute: 100
|
|
39
|
+
|
|
40
|
+
response-tailoring:
|
|
41
|
+
enabled: true
|
|
42
|
+
compress-mobile: true
|
|
43
|
+
|
|
44
|
+
composition:
|
|
45
|
+
timeout: 5s
|
|
46
|
+
parallel-enabled: true
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Client Type Detection
|
|
50
|
+
|
|
51
|
+
```java
|
|
52
|
+
public enum ClientType {
|
|
53
|
+
WEB("web"),
|
|
54
|
+
MOBILE_IOS("ios"),
|
|
55
|
+
MOBILE_ANDROID("android"),
|
|
56
|
+
TABLET("tablet"),
|
|
57
|
+
TV("tv"),
|
|
58
|
+
IOT("iot"),
|
|
59
|
+
PARTNER("partner");
|
|
60
|
+
|
|
61
|
+
public boolean isMobile() {
|
|
62
|
+
return this == MOBILE_IOS || this == MOBILE_ANDROID;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public boolean requiresCompression() {
|
|
66
|
+
return isMobile() || this == IOT;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Annotations
|
|
72
|
+
|
|
73
|
+
```java
|
|
74
|
+
@Target(ElementType.METHOD)
|
|
75
|
+
@Retention(RetentionPolicy.RUNTIME)
|
|
76
|
+
public @interface BffEndpoint {
|
|
77
|
+
ClientType[] clients() default {};
|
|
78
|
+
String[] include() default {};
|
|
79
|
+
boolean aggregate() default false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@Target(ElementType.METHOD)
|
|
83
|
+
@Retention(RetentionPolicy.RUNTIME)
|
|
84
|
+
public @interface TailorForClient {
|
|
85
|
+
ClientType value();
|
|
86
|
+
String[] fields() default {};
|
|
87
|
+
boolean compress() default false;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Base BFF Controller
|
|
92
|
+
|
|
93
|
+
```java
|
|
94
|
+
@RestController
|
|
95
|
+
@RequestMapping("/bff")
|
|
96
|
+
public abstract class BaseBffController {
|
|
97
|
+
|
|
98
|
+
protected final QueryCompositionService compositionService;
|
|
99
|
+
protected final ResponseTailoringService tailoringService;
|
|
100
|
+
protected final CombinedRateLimitService rateLimitService;
|
|
101
|
+
|
|
102
|
+
protected ClientType detectClient(HttpServletRequest request) {
|
|
103
|
+
String clientHeader = request.getHeader("X-Client-Type");
|
|
104
|
+
if (clientHeader != null) {
|
|
105
|
+
return ClientType.fromValue(clientHeader);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
String userAgent = request.getHeader("User-Agent");
|
|
109
|
+
return ClientTypeDetector.fromUserAgent(userAgent);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected <T> T tailorResponse(T response, ClientType clientType) {
|
|
113
|
+
return tailoringService.tailor(response, clientType);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Query Composition Service
|
|
119
|
+
|
|
120
|
+
```java
|
|
121
|
+
@Service
|
|
122
|
+
public class QueryCompositionServiceImpl implements QueryCompositionService {
|
|
123
|
+
|
|
124
|
+
private final List<ServiceRequest> serviceRequests;
|
|
125
|
+
private final ExecutorService executor;
|
|
126
|
+
|
|
127
|
+
@Override
|
|
128
|
+
public AggregatedResponse compose(List<String> includes,
|
|
129
|
+
Map<String, Object> params) {
|
|
130
|
+
|
|
131
|
+
List<CompletableFuture<ServiceResult>> futures = includes.stream()
|
|
132
|
+
.map(include -> findServiceRequest(include))
|
|
133
|
+
.map(sr -> CompletableFuture.supplyAsync(
|
|
134
|
+
() -> executeService(sr, params), executor)
|
|
135
|
+
.exceptionally(this::handleFailure))
|
|
136
|
+
.toList();
|
|
137
|
+
|
|
138
|
+
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
|
139
|
+
.orTimeout(5, TimeUnit.SECONDS)
|
|
140
|
+
.join();
|
|
141
|
+
|
|
142
|
+
return aggregateResults(futures);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private ServiceResult handleFailure(Throwable ex) {
|
|
146
|
+
log.warn("Service call failed: {}", ex.getMessage());
|
|
147
|
+
return ServiceResult.failure(ex.getMessage());
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Response Tailoring Service
|
|
153
|
+
|
|
154
|
+
```java
|
|
155
|
+
@Service
|
|
156
|
+
public class ResponseTailoringServiceImpl implements ResponseTailoringService {
|
|
157
|
+
|
|
158
|
+
private final ObjectMapper objectMapper;
|
|
159
|
+
private final Map<ClientType, Set<String>> fieldMappings;
|
|
160
|
+
|
|
161
|
+
@Override
|
|
162
|
+
@SuppressWarnings("unchecked")
|
|
163
|
+
public <T> T tailor(T response, ClientType clientType) {
|
|
164
|
+
if (response == null) return null;
|
|
165
|
+
|
|
166
|
+
Set<String> allowedFields = fieldMappings.get(clientType);
|
|
167
|
+
if (allowedFields == null || allowedFields.isEmpty()) {
|
|
168
|
+
return response;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Convert to map, filter fields, convert back
|
|
172
|
+
Map<String, Object> map = objectMapper.convertValue(response, Map.class);
|
|
173
|
+
Map<String, Object> filtered = filterFields(map, allowedFields);
|
|
174
|
+
|
|
175
|
+
return (T) objectMapper.convertValue(filtered, response.getClass());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private Map<String, Object> filterFields(Map<String, Object> source,
|
|
179
|
+
Set<String> allowed) {
|
|
180
|
+
return source.entrySet().stream()
|
|
181
|
+
.filter(e -> allowed.contains(e.getKey()))
|
|
182
|
+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Aggregated Response Model
|
|
188
|
+
|
|
189
|
+
```java
|
|
190
|
+
public record AggregatedResponse(
|
|
191
|
+
Map<String, Object> data,
|
|
192
|
+
Map<String, ErrorInfo> errors,
|
|
193
|
+
ResponseMetadata metadata
|
|
194
|
+
) {
|
|
195
|
+
public boolean hasErrors() {
|
|
196
|
+
return errors != null && !errors.isEmpty();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public static Builder builder() {
|
|
200
|
+
return new Builder();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public static class Builder {
|
|
204
|
+
private final Map<String, Object> data = new HashMap<>();
|
|
205
|
+
private final Map<String, ErrorInfo> errors = new HashMap<>();
|
|
206
|
+
|
|
207
|
+
public Builder addData(String key, Object value) {
|
|
208
|
+
data.put(key, value);
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public Builder addError(String key, String message) {
|
|
213
|
+
errors.put(key, new ErrorInfo(message, null));
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public AggregatedResponse build() {
|
|
218
|
+
return new AggregatedResponse(data, errors,
|
|
219
|
+
new ResponseMetadata(Instant.now()));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## BFF Endpoint Aspect
|
|
226
|
+
|
|
227
|
+
```java
|
|
228
|
+
@Aspect
|
|
229
|
+
@Component
|
|
230
|
+
public class BffEndpointAspect {
|
|
231
|
+
|
|
232
|
+
private final CombinedRateLimitService rateLimitService;
|
|
233
|
+
|
|
234
|
+
@Around("@annotation(bffEndpoint)")
|
|
235
|
+
public Object handleBffEndpoint(ProceedingJoinPoint joinPoint,
|
|
236
|
+
BffEndpoint bffEndpoint) throws Throwable {
|
|
237
|
+
|
|
238
|
+
HttpServletRequest request = getCurrentRequest();
|
|
239
|
+
ClientType clientType = detectClient(request);
|
|
240
|
+
|
|
241
|
+
// Check client type restriction
|
|
242
|
+
if (bffEndpoint.clients().length > 0 &&
|
|
243
|
+
!Arrays.asList(bffEndpoint.clients()).contains(clientType)) {
|
|
244
|
+
throw new ClientTypeNotAllowedException(clientType);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Apply rate limiting
|
|
248
|
+
rateLimitService.checkLimit(clientType, request);
|
|
249
|
+
|
|
250
|
+
return joinPoint.proceed();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Example BFF Controller
|
|
256
|
+
|
|
257
|
+
```java
|
|
258
|
+
@RestController
|
|
259
|
+
@RequestMapping("/bff/v1")
|
|
260
|
+
public class DashboardBffController extends BaseBffController {
|
|
261
|
+
|
|
262
|
+
private final UserService userService;
|
|
263
|
+
private final OrderService orderService;
|
|
264
|
+
private final NotificationService notificationService;
|
|
265
|
+
|
|
266
|
+
@GetMapping("/dashboard")
|
|
267
|
+
@BffEndpoint(aggregate = true)
|
|
268
|
+
public AggregatedResponse getDashboard(
|
|
269
|
+
@RequestParam(defaultValue = "profile,notifications") String include,
|
|
270
|
+
HttpServletRequest request) {
|
|
271
|
+
|
|
272
|
+
ClientType client = detectClient(request);
|
|
273
|
+
List<String> includes = Arrays.asList(include.split(","));
|
|
274
|
+
|
|
275
|
+
AggregatedResponse.Builder builder = AggregatedResponse.builder();
|
|
276
|
+
|
|
277
|
+
if (includes.contains("profile")) {
|
|
278
|
+
UserDTO profile = userService.getCurrentUser();
|
|
279
|
+
builder.addData("profile", tailorResponse(profile, client));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (includes.contains("notifications")) {
|
|
283
|
+
int limit = client.isMobile() ? 5 : 20;
|
|
284
|
+
List<NotificationDTO> notifications =
|
|
285
|
+
notificationService.getRecent(limit);
|
|
286
|
+
builder.addData("notifications", notifications);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (includes.contains("orders")) {
|
|
290
|
+
List<OrderDTO> orders = orderService.getRecentOrders();
|
|
291
|
+
builder.addData("orders", tailorResponse(orders, client));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return builder.build();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@GetMapping("/dashboard")
|
|
298
|
+
@TailorForClient(value = ClientType.MOBILE_IOS,
|
|
299
|
+
fields = {"id", "name", "avatarThumb"})
|
|
300
|
+
public MobileDashboardResponse getMobileDashboard() {
|
|
301
|
+
// iOS-specific optimized response
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Rate Limiting Service
|
|
307
|
+
|
|
308
|
+
```java
|
|
309
|
+
@Service
|
|
310
|
+
public class CombinedRateLimitServiceImpl implements CombinedRateLimitService {
|
|
311
|
+
|
|
312
|
+
private final Map<ClientType, Bucket> buckets = new ConcurrentHashMap<>();
|
|
313
|
+
private final BffProperties properties;
|
|
314
|
+
|
|
315
|
+
@Override
|
|
316
|
+
public void checkLimit(ClientType clientType, HttpServletRequest request) {
|
|
317
|
+
Bucket bucket = buckets.computeIfAbsent(clientType, this::createBucket);
|
|
318
|
+
|
|
319
|
+
if (!bucket.tryConsume(1)) {
|
|
320
|
+
throw new RateLimitExceededException(
|
|
321
|
+
"Rate limit exceeded for client type: " + clientType);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private Bucket createBucket(ClientType clientType) {
|
|
326
|
+
int rpm = properties.getRateLimits()
|
|
327
|
+
.getOrDefault(clientType, 100);
|
|
328
|
+
|
|
329
|
+
return Bucket.builder()
|
|
330
|
+
.addLimit(Bandwidth.simple(rpm, Duration.ofMinutes(1)))
|
|
331
|
+
.build();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Testing
|
|
337
|
+
|
|
338
|
+
```java
|
|
339
|
+
@WebMvcTest(DashboardBffController.class)
|
|
340
|
+
class DashboardBffControllerTest {
|
|
341
|
+
|
|
342
|
+
@Autowired
|
|
343
|
+
private MockMvc mockMvc;
|
|
344
|
+
|
|
345
|
+
@MockBean
|
|
346
|
+
private UserService userService;
|
|
347
|
+
|
|
348
|
+
@Test
|
|
349
|
+
void shouldTailorResponseForMobile() throws Exception {
|
|
350
|
+
when(userService.getCurrentUser()).thenReturn(fullUserDto());
|
|
351
|
+
|
|
352
|
+
mockMvc.perform(get("/bff/v1/dashboard")
|
|
353
|
+
.header("X-Client-Type", "ios"))
|
|
354
|
+
.andExpect(status().isOk())
|
|
355
|
+
.andExpect(jsonPath("$.data.profile.email").doesNotExist())
|
|
356
|
+
.andExpect(jsonPath("$.data.profile.id").exists());
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Related Skills
|
|
362
|
+
|
|
363
|
+
- `bff-concepts`: BFF pattern concepts
|
|
364
|
+
- `spring-boot-4`: Spring Boot 4.0 patterns
|