moai-adk 0.4.5__py3-none-any.whl → 0.20.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.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/__init__.py +1 -1
- moai_adk/__main__.py +74 -1
- moai_adk/cli/commands/__init__.py +1 -1
- moai_adk/cli/commands/analyze.py +119 -0
- moai_adk/cli/commands/backup.py +25 -1
- moai_adk/cli/commands/doctor.py +31 -5
- moai_adk/cli/commands/improve_user_experience.py +307 -0
- moai_adk/cli/commands/init.py +111 -10
- moai_adk/cli/commands/status.py +33 -3
- moai_adk/cli/commands/update.py +921 -130
- moai_adk/cli/commands/validate_links.py +120 -0
- moai_adk/cli/prompts/init_prompts.py +22 -87
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +388 -0
- moai_adk/core/analysis/tag_chain_analyzer.py +344 -0
- moai_adk/core/analysis/tag_chain_repair.py +879 -0
- moai_adk/core/config/__init__.py +19 -0
- moai_adk/core/config/migration.py +235 -0
- moai_adk/core/git/__init__.py +1 -1
- moai_adk/core/git/branch.py +1 -1
- moai_adk/core/git/commit.py +1 -1
- moai_adk/core/git/manager.py +1 -1
- moai_adk/core/issue_creator.py +313 -0
- moai_adk/core/mcp/setup.py +56 -0
- moai_adk/core/mcp/setup_old.py +296 -0
- moai_adk/core/project/backup_utils.py +1 -1
- moai_adk/core/project/checker.py +2 -2
- moai_adk/core/project/detector.py +211 -12
- moai_adk/core/project/initializer.py +85 -15
- moai_adk/core/project/phase_executor.py +76 -13
- moai_adk/core/project/validator.py +13 -13
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +1 -1
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/tags/__init__.py +86 -0
- moai_adk/core/tags/auto_corrector.py +693 -0
- moai_adk/core/tags/ci_validator.py +463 -0
- moai_adk/core/tags/cli.py +283 -0
- moai_adk/core/tags/generator.py +109 -0
- moai_adk/core/tags/inserter.py +99 -0
- moai_adk/core/tags/mapper.py +126 -0
- moai_adk/core/tags/parser.py +76 -0
- moai_adk/core/tags/policy_validator.py +580 -0
- moai_adk/core/tags/pre_commit_validator.py +421 -0
- moai_adk/core/tags/reporter.py +956 -0
- moai_adk/core/tags/rollback_manager.py +525 -0
- moai_adk/core/tags/tags.py +149 -0
- moai_adk/core/tags/validator.py +897 -0
- moai_adk/core/template/__init__.py +1 -1
- moai_adk/core/template/backup.py +1 -1
- moai_adk/core/template/merger.py +50 -1
- moai_adk/core/template/processor.py +119 -13
- moai_adk/core/template_engine.py +268 -0
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +348 -0
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +209 -944
- moai_adk/templates/.claude/agents/alfred/database-expert.md +352 -0
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +34 -5
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +38 -8
- moai_adk/templates/.claude/agents/alfred/format-expert.md +469 -0
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +128 -9
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +104 -6
- moai_adk/templates/.claude/agents/alfred/project-manager.md +88 -16
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +36 -9
- moai_adk/templates/.claude/agents/alfred/security-expert.md +270 -0
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +865 -0
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +214 -43
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +111 -9
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +309 -160
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +36 -7
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +605 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +393 -966
- moai_adk/templates/.claude/commands/alfred/1-plan.md +651 -367
- moai_adk/templates/.claude/commands/alfred/2-run.md +388 -241
- moai_adk/templates/.claude/commands/alfred/3-sync.md +1921 -410
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +153 -0
- moai_adk/templates/.claude/commands/alfred/release-new.md +3604 -0
- moai_adk/templates/.claude/hooks/alfred/core/project.py +484 -20
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
- moai_adk/templates/.claude/hooks/alfred/post_tool__enable_streaming_ui.py +50 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +93 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__tag_auto_corrector.py +407 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +99 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__realtime_tag_monitor.py +335 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__tag_policy_validator.py +325 -0
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +93 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__auto_cleanup.py +580 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +298 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +170 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +230 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/daily_analysis.py +351 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +174 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +87 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +61 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +111 -0
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/hook_config.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/output-styles/alfred/alfred-moai-adk-beginner.md +267 -0
- moai_adk/templates/.claude/output-styles/alfred/keating-personal-tutor.md +440 -0
- moai_adk/templates/.claude/output-styles/alfred/r2d2-agentic-coding.md +583 -0
- moai_adk/templates/.claude/settings.json +96 -14
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +242 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/SKILL.md +237 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/examples.md +871 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/reference.md +653 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/README.md +162 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/SKILL.md +227 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/examples.md +354 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/reference.md +158 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +179 -79
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/examples.md +117 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/scripts/pre-review-check.sh +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +132 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +229 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +87 -73
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/README.md +42 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/SKILL.md +429 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/examples.md +520 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +369 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +539 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +320 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +161 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +269 -0
- moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +32 -0
- moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +298 -0
- moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +26 -0
- moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +307 -0
- moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +21 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +252 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +19 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +19 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +24 -0
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +199 -0
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +39 -0
- moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +316 -0
- moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +18 -0
- moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +263 -0
- moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +30 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +482 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +303 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +524 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +477 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +429 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +391 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +431 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +577 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +466 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +583 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +526 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +608 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +328 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +312 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +245 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +285 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +278 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +303 -0
- moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +291 -0
- moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +15 -0
- moai_adk/templates/.claude/skills/moai-change-logger/SKILL.md +563 -0
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +234 -43
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +1633 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +660 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +97 -69
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +97 -72
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +102 -73
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +97 -67
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +97 -79
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -71
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +265 -64
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +1064 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +1047 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +87 -78
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +87 -70
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +87 -86
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +80 -62
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +207 -50
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +90 -71
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +78 -58
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +78 -51
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/.!11330!examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +253 -32
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +1099 -0
- moai_adk/templates/.claude/skills/moai-jit-docs-enhanced/SKILL.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +98 -76
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +2358 -70
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +2962 -68
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +1898 -70
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +1465 -68
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +2364 -66
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +32 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +1630 -69
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +89 -61
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +735 -66
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +624 -0
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +316 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +1834 -70
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +99 -74
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +1959 -69
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-template/SKILL.md +348 -0
- moai_adk/templates/.claude/skills/moai-lang-template/VARIABLES.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +1230 -66
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +34 -0
- moai_adk/templates/.claude/skills/moai-learning-optimizer/SKILL.md +575 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/README.md +50 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/SKILL.md +304 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/examples.md +417 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/README.md +87 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/SKILL.md +552 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/examples.md +1109 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/reference.md +514 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/validate.py +106 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/README.md +11 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/SKILL.md +622 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/examples.md +20 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/reference.md +12 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/README.md +152 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/SKILL.md +285 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/examples.md +333 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/reference.md +386 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/README.md +49 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/SKILL.md +319 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/examples.md +58 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/reference.md +123 -0
- moai_adk/templates/.claude/skills/moai-session-info/SKILL.md +314 -0
- moai_adk/templates/.claude/skills/moai-streaming-ui/SKILL.md +552 -0
- moai_adk/templates/.claude/skills/moai-tag-policy-validator/SKILL.md +570 -0
- moai_adk/templates/.git-hooks/pre-commit +66 -0
- moai_adk/templates/.git-hooks/pre-push +255 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
- moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +118 -0
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +338 -0
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.mcp.json +31 -0
- moai_adk/templates/.moai/config.json +80 -7
- moai_adk/templates/CLAUDE.md +562 -546
- moai_adk/utils/banner.py +5 -5
- moai_adk/utils/common.py +294 -0
- moai_adk/utils/link_validator.py +235 -0
- moai_adk/utils/logger.py +8 -8
- moai_adk/utils/user_experience.py +451 -0
- moai_adk-0.20.1.dist-info/METADATA +233 -0
- moai_adk-0.20.1.dist-info/RECORD +404 -0
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -156
- moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -85
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -92
- moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -70
- moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -41
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -636
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -692
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -470
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +0 -103
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -103
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -95
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +0 -105
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +0 -97
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -97
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -90
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -99
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +0 -87
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +0 -62
- moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +0 -94
- moai_adk/templates/.claude/skills/moai-claude-code/examples.md +0 -513
- moai_adk/templates/.claude/skills/moai-claude-code/reference.md +0 -433
- moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +0 -332
- moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +0 -384
- moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +0 -363
- moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +0 -595
- moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +0 -496
- moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +0 -100
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +0 -99
- moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +0 -100
- moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +0 -98
- moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +0 -98
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/development-guide.md +0 -344
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
- moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/__init__.py +0 -2
- moai_adk-0.4.5.dist-info/METADATA +0 -369
- moai_adk-0.4.5.dist-info/RECORD +0 -152
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,99 +1,1660 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
name: moai-lang-kotlin
|
|
4
|
-
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
created: 2025-11-06
|
|
5
|
+
updated: 2025-11-06
|
|
6
|
+
status: active
|
|
7
|
+
description: "Kotlin best practices with Android development, Spring Boot backend, and modern coroutines for 2025"
|
|
8
|
+
keywords: [kotlin, programming, android, backend, spring, coroutines, kmp, mobile]
|
|
5
9
|
allowed-tools:
|
|
6
10
|
- Read
|
|
11
|
+
- Write
|
|
12
|
+
- Edit
|
|
7
13
|
- Bash
|
|
14
|
+
- WebFetch
|
|
15
|
+
- WebSearch
|
|
8
16
|
---
|
|
9
17
|
|
|
10
|
-
# Kotlin
|
|
18
|
+
# Kotlin Development Mastery
|
|
19
|
+
|
|
20
|
+
**Modern Kotlin Development with 2025 Best Practices**
|
|
21
|
+
|
|
22
|
+
> Comprehensive Kotlin development guidance covering Android applications, Spring Boot backend services, KMP cross-platform development, and coroutine-based programming using the latest tools and frameworks.
|
|
23
|
+
|
|
24
|
+
## What It Does
|
|
25
|
+
|
|
26
|
+
### Android Development
|
|
27
|
+
- **Mobile App Development**: Jetpack Compose UI, MVVM architecture, modern Android patterns
|
|
28
|
+
- **Platform Integration**: Camera, location, notifications, background services
|
|
29
|
+
- **Performance Optimization**: Memory management, battery optimization, lifecycle awareness
|
|
30
|
+
- **Testing**: Unit tests, UI tests, integration tests with modern Android testing framework
|
|
31
|
+
|
|
32
|
+
### Backend Development
|
|
33
|
+
- **API Development**: Spring Boot with Kotlin, Ktor, Micronaut for microservices
|
|
34
|
+
- **Database Integration**: Exposed, jOOQ, Spring Data with coroutine support
|
|
35
|
+
- **Async Programming**: Coroutines, Flow, structured concurrency
|
|
36
|
+
- **Testing**: MockK, Kotest, TestContainers with coroutine support
|
|
37
|
+
|
|
38
|
+
### Cross-Platform Development
|
|
39
|
+
- **KMP Development**: Shared business logic across iOS, Android, Web, Desktop
|
|
40
|
+
- **Platform-Specific APIs**: Expect/actual implementations for platform differences
|
|
41
|
+
- **UI Development**: Compose Multiplatform for cross-platform UI
|
|
42
|
+
- **Testing**: Shared test suites across platforms
|
|
43
|
+
|
|
44
|
+
## When to Use
|
|
45
|
+
|
|
46
|
+
### Perfect Scenarios
|
|
47
|
+
- **Building Android applications with modern Jetpack Compose**
|
|
48
|
+
- **Developing backend microservices with Spring Boot and Ktor**
|
|
49
|
+
- **Creating cross-platform applications with Kotlin Multiplatform**
|
|
50
|
+
- **Implementing coroutine-based asynchronous programming**
|
|
51
|
+
- **Building reactive applications with Flow and structured concurrency**
|
|
52
|
+
- **Migrating Java codebases to Kotlin for modern development**
|
|
53
|
+
- **Developing Android libraries and SDKs**
|
|
54
|
+
|
|
55
|
+
### Common Triggers
|
|
56
|
+
- "Create Android app with Kotlin"
|
|
57
|
+
- "Build Kotlin backend API"
|
|
58
|
+
- "Set up KMP project"
|
|
59
|
+
- "Implement coroutines in Kotlin"
|
|
60
|
+
- "Optimize Kotlin performance"
|
|
61
|
+
- "Test Kotlin application"
|
|
62
|
+
- "Kotlin best practices"
|
|
63
|
+
|
|
64
|
+
## Tool Version Matrix (2025-11-06)
|
|
65
|
+
|
|
66
|
+
### Core Kotlin
|
|
67
|
+
- **Kotlin**: 2.0.20 (current) / 1.9.24 (LTS)
|
|
68
|
+
- **Kotlin Multiplatform**: 2.0.20
|
|
69
|
+
- **Package Managers**: Gradle 8.8 with Kotlin DSL, Maven
|
|
70
|
+
- **Runtime**: JVM 17+, Android API 21+, iOS 13+, WebAssembly
|
|
71
|
+
|
|
72
|
+
### Android Development
|
|
73
|
+
- **Android Studio**: Ladybug | 2024.2.1
|
|
74
|
+
- **Jetpack Compose**: 1.7.0
|
|
75
|
+
- **Compose BOM**: 2024.10.00
|
|
76
|
+
- **Android Gradle Plugin**: 8.7.0
|
|
77
|
+
- **KSP**: 2.0.20-1.0.25
|
|
78
|
+
|
|
79
|
+
### Backend Frameworks
|
|
80
|
+
- **Spring Boot**: 3.3.x with Kotlin support
|
|
81
|
+
- **Ktor**: 2.3.x - Asynchronous HTTP framework
|
|
82
|
+
- **Micronaut**: 4.7.x - Cloud-native framework
|
|
83
|
+
- **Exposed**: 0.53.x - SQL framework
|
|
84
|
+
- **jOOQ**: 3.19.x - Type-safe SQL
|
|
85
|
+
|
|
86
|
+
### Testing Tools
|
|
87
|
+
- **Kotest**: 5.9.x - Powerful testing framework
|
|
88
|
+
- **MockK**: 1.13.x - Mocking library for Kotlin
|
|
89
|
+
- **TestContainers**: 1.20.x - Integration testing
|
|
90
|
+
- **Espresso**: 3.6.x - Android UI testing
|
|
91
|
+
- **Compose Testing**: 1.7.0 - Compose UI testing
|
|
92
|
+
|
|
93
|
+
### Mobile Development
|
|
94
|
+
- **Compose Multiplatform**: 1.7.0
|
|
95
|
+
- **KMP libraries**: Ktor Client, SQLDelight, Voyager
|
|
96
|
+
|
|
97
|
+
## Ecosystem Overview
|
|
98
|
+
|
|
99
|
+
### Package Management
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Gradle Kotlin DSL project creation
|
|
103
|
+
gradle init --type kotlin-application --dsl kotlin --test-framework kotest --package com.example
|
|
104
|
+
|
|
105
|
+
# KMP project creation
|
|
106
|
+
curl -sSLO https://github.com/Kotlin/kmm-samples/archive/main.zip && unzip main.zip
|
|
107
|
+
|
|
108
|
+
# Dependency management with version catalogs
|
|
109
|
+
# gradle/libs.versions.toml
|
|
110
|
+
[versions]
|
|
111
|
+
kotlin = "2.0.20"
|
|
112
|
+
compose-bom = "2024.10.00"
|
|
113
|
+
ktor = "2.3.12"
|
|
114
|
+
|
|
115
|
+
[libraries]
|
|
116
|
+
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
|
117
|
+
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
|
118
|
+
|
|
119
|
+
[bundles]
|
|
120
|
+
ktor = ["ktor-client-core", "ktor-client-logging"]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Project Structure (2025 Best Practice)
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
kotlin-project/
|
|
127
|
+
├── gradle/
|
|
128
|
+
│ └── libs.versions.toml # Version catalog
|
|
129
|
+
├── build.gradle.kts # Root build configuration
|
|
130
|
+
├── settings.gradle.kts # Gradle settings
|
|
131
|
+
├── gradlew # Gradle wrapper
|
|
132
|
+
└── src/
|
|
133
|
+
├── commonMain/kotlin/ # Shared KMP code
|
|
134
|
+
│ └── com/example/
|
|
135
|
+
│ ├── data/ # Shared data models
|
|
136
|
+
│ ├── domain/ # Business logic
|
|
137
|
+
│ └── network/ # API clients
|
|
138
|
+
├── androidMain/kotlin/ # Android-specific code
|
|
139
|
+
│ └── com/example/
|
|
140
|
+
│ ├── ui/ # Android UI
|
|
141
|
+
│ ├── platform/ # Android implementations
|
|
142
|
+
│ └── MainActivity.kt
|
|
143
|
+
├── iosMain/kotlin/ # iOS-specific code
|
|
144
|
+
│ └── com/example/
|
|
145
|
+
│ └── platform/ # iOS implementations
|
|
146
|
+
├── jvmMain/kotlin/ # JVM/Backend code
|
|
147
|
+
│ └── com/example/
|
|
148
|
+
│ ├── api/ # REST controllers
|
|
149
|
+
│ ├── service/ # Business services
|
|
150
|
+
│ └── Application.kt
|
|
151
|
+
└── commonTest/kotlin/ # Shared tests
|
|
152
|
+
└── com/example/
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Modern Development Patterns
|
|
156
|
+
|
|
157
|
+
### Kotlin Language Features 2.0
|
|
158
|
+
|
|
159
|
+
```kotlin
|
|
160
|
+
import kotlinx.coroutines.*
|
|
161
|
+
import kotlinx.coroutines.flow.*
|
|
162
|
+
import kotlin.coroutines.*
|
|
163
|
+
import kotlin.time.Duration.Companion.seconds
|
|
164
|
+
|
|
165
|
+
// Context receivers (Kotlin 2.0)
|
|
166
|
+
context(Logging)
|
|
167
|
+
suspend fun processUserData(userId: String) {
|
|
168
|
+
log("Processing user: $userId")
|
|
169
|
+
// Processing logic
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Sealed interfaces for better hierarchies
|
|
173
|
+
sealed interface NetworkResult<out T> {
|
|
174
|
+
data class Success<T>(val data: T) : NetworkResult<T>
|
|
175
|
+
data class Error(val exception: Throwable) : NetworkResult<Nothing>
|
|
176
|
+
object Loading : NetworkResult<Nothing>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Extension functions for better APIs
|
|
180
|
+
inline fun <T, R> Result<T>.flatMap(transform: (value: T) -> Result<R>): Result<R> {
|
|
181
|
+
return fold(
|
|
182
|
+
onSuccess = { success -> transform(success) },
|
|
183
|
+
onFailure = { failure -> Result.failure(failure) }
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Higher-order functions for functional programming
|
|
188
|
+
inline fun <T> List<T>.filterAndMap(
|
|
189
|
+
crossinline predicate: (T) -> Boolean,
|
|
190
|
+
crossinline transform: (T) -> T
|
|
191
|
+
): List<T> = this
|
|
192
|
+
.filter { predicate(it) }
|
|
193
|
+
.map { transform(it) }
|
|
11
194
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
195
|
+
// Scope functions usage
|
|
196
|
+
class UserRepository {
|
|
197
|
+
private val users = mutableMapOf<String, User>()
|
|
198
|
+
|
|
199
|
+
fun createUser(name: String, email: String): User {
|
|
200
|
+
return User(name, email).also { user ->
|
|
201
|
+
users[user.id] = user
|
|
202
|
+
log("User created: ${user.id}")
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fun findUser(id: String): User? = users[id]?.takeIf { it.isActive }
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Coroutines and Flow Patterns
|
|
211
|
+
|
|
212
|
+
```kotlin
|
|
213
|
+
// Structured concurrency
|
|
214
|
+
class UserRepository(
|
|
215
|
+
private val api: UserApi,
|
|
216
|
+
private val database: UserDatabase
|
|
217
|
+
) {
|
|
218
|
+
suspend fun getUserWithCache(id: String): User = coroutineScope {
|
|
219
|
+
val cached = async(Dispatchers.IO) { database.getUser(id) }
|
|
220
|
+
val network = async(Dispatchers.IO) { api.getUser(id) }
|
|
221
|
+
|
|
222
|
+
when (val user = cached.await()) {
|
|
223
|
+
null -> {
|
|
224
|
+
val networkUser = network.await()
|
|
225
|
+
database.saveUser(networkUser)
|
|
226
|
+
networkUser
|
|
227
|
+
}
|
|
228
|
+
else -> {
|
|
229
|
+
// Refresh in background
|
|
230
|
+
launch { database.saveUser(network.await()) }
|
|
231
|
+
user
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Flow operators for reactive streams
|
|
238
|
+
class MessageRepository {
|
|
239
|
+
private val _messages = MutableSharedFlow<Message>()
|
|
240
|
+
val messages: SharedFlow<Message> = _messages.asSharedFlow()
|
|
241
|
+
|
|
242
|
+
fun getFilteredMessages(filter: MessageFilter): Flow<Message> {
|
|
243
|
+
return messages
|
|
244
|
+
.filter { filter.matches(it) }
|
|
245
|
+
.debounce(300.milliseconds)
|
|
246
|
+
.distinctUntilChanged()
|
|
247
|
+
.catch { error ->
|
|
248
|
+
log("Error in message flow: ${error.message}")
|
|
249
|
+
emit(Message.Error(error))
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Cold flow with state
|
|
254
|
+
fun getStateFlow(): StateFlow<UiState> = channelFlow {
|
|
255
|
+
send(UiState.Loading)
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
val data = fetchData()
|
|
259
|
+
send(UiState.Success(data))
|
|
260
|
+
|
|
261
|
+
// Continue listening for updates
|
|
262
|
+
dataUpdates.collect { update ->
|
|
263
|
+
send(UiState.Success(update))
|
|
264
|
+
}
|
|
265
|
+
} catch (e: Exception) {
|
|
266
|
+
send(UiState.Error(e))
|
|
267
|
+
}
|
|
268
|
+
}.stateIn(
|
|
269
|
+
scope = CoroutineScope(Dispatchers.Default + SupervisorJob()),
|
|
270
|
+
started = SharingStarted.WhileSubscribed(5000),
|
|
271
|
+
initialValue = UiState.Idle
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Exception handling with coroutines
|
|
276
|
+
suspend fun safeApiCall(apiCall: suspend () -> User): Result<User> {
|
|
277
|
+
return try {
|
|
278
|
+
Result.success(apiCall())
|
|
279
|
+
} catch (e: IOException) {
|
|
280
|
+
Result.failure(NetworkException("Network error", e))
|
|
281
|
+
} catch (e: HttpException) {
|
|
282
|
+
Result.failure(ServerException("Server error: ${e.code()}", e))
|
|
283
|
+
} catch (e: Exception) {
|
|
284
|
+
Result.failure(UnknownException("Unknown error", e))
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Timeout and retry patterns
|
|
289
|
+
suspend fun fetchWithRetryAndTimeout(
|
|
290
|
+
maxRetries: Int = 3,
|
|
291
|
+
timeout: Duration = 30.seconds
|
|
292
|
+
): Data {
|
|
293
|
+
return retry(maxRetries) { attempt ->
|
|
294
|
+
if (attempt > 0) delay(1000 * attempt) // Exponential backoff
|
|
295
|
+
|
|
296
|
+
withTimeout(timeout) {
|
|
297
|
+
fetchData()
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Coroutine scope management
|
|
303
|
+
class MyViewModel(
|
|
304
|
+
private val repository: UserRepository
|
|
305
|
+
) : ViewModel() {
|
|
306
|
+
|
|
307
|
+
private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
|
|
308
|
+
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
|
|
309
|
+
|
|
310
|
+
fun loadUser(userId: String) {
|
|
311
|
+
viewModelScope.launch {
|
|
312
|
+
_uiState.value = UiState.Loading
|
|
313
|
+
|
|
314
|
+
repository.getUser(userId)
|
|
315
|
+
.catch { error -> _uiState.value = UiState.Error(error) }
|
|
316
|
+
.collect { user -> _uiState.value = UiState.Success(user) }
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
override fun onCleared() {
|
|
321
|
+
super.onCleared()
|
|
322
|
+
viewModelScope.cancel() // Cancel all coroutines
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Android Jetpack Compose Patterns
|
|
328
|
+
|
|
329
|
+
```kotlin
|
|
330
|
+
@Composable
|
|
331
|
+
fun UserListScreen(
|
|
332
|
+
users: LazyPagingItems<User>,
|
|
333
|
+
onUserClick: (User) -> Unit,
|
|
334
|
+
modifier: Modifier = Modifier
|
|
335
|
+
) {
|
|
336
|
+
LazyColumn(
|
|
337
|
+
modifier = modifier.fillMaxSize(),
|
|
338
|
+
contentPadding = PaddingValues(16.dp),
|
|
339
|
+
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
340
|
+
) {
|
|
341
|
+
items(
|
|
342
|
+
count = users.itemCount,
|
|
343
|
+
key = users.itemKey { it.id }
|
|
344
|
+
) { index ->
|
|
345
|
+
val user = users[index]
|
|
346
|
+
|
|
347
|
+
if (user != null) {
|
|
348
|
+
UserItem(
|
|
349
|
+
user = user,
|
|
350
|
+
onClick = { onUserClick(user) }
|
|
351
|
+
)
|
|
352
|
+
} else {
|
|
353
|
+
UserItemPlaceholder()
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
when (users.loadState.append) {
|
|
358
|
+
is LoadState.Loading -> item {
|
|
359
|
+
LoadingIndicator()
|
|
360
|
+
}
|
|
361
|
+
is LoadState.Error -> item {
|
|
362
|
+
ErrorMessage(
|
|
363
|
+
message = "Failed to load users",
|
|
364
|
+
onRetry = { users.retry() }
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
else -> {}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
@Composable
|
|
373
|
+
private fun UserItem(
|
|
374
|
+
user: User,
|
|
375
|
+
onClick: () -> Unit,
|
|
376
|
+
modifier: Modifier = Modifier
|
|
377
|
+
) {
|
|
378
|
+
Card(
|
|
379
|
+
onClick = onClick,
|
|
380
|
+
modifier = modifier.fillMaxWidth(),
|
|
381
|
+
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
|
382
|
+
) {
|
|
383
|
+
Row(
|
|
384
|
+
modifier = Modifier
|
|
385
|
+
.padding(16.dp)
|
|
386
|
+
.fillMaxWidth(),
|
|
387
|
+
verticalAlignment = Alignment.CenterVertically
|
|
388
|
+
) {
|
|
389
|
+
AsyncImage(
|
|
390
|
+
model = user.avatarUrl,
|
|
391
|
+
contentDescription = "Avatar",
|
|
392
|
+
modifier = Modifier
|
|
393
|
+
.size(48.dp)
|
|
394
|
+
.clip(CircleShape),
|
|
395
|
+
contentScale = ContentScale.Crop
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
Spacer(modifier = Modifier.width(16.dp))
|
|
399
|
+
|
|
400
|
+
Column(modifier = Modifier.weight(1f)) {
|
|
401
|
+
Text(
|
|
402
|
+
text = user.name,
|
|
403
|
+
style = MaterialTheme.typography.titleMedium
|
|
404
|
+
)
|
|
405
|
+
Text(
|
|
406
|
+
text = user.email,
|
|
407
|
+
style = MaterialTheme.typography.bodyMedium,
|
|
408
|
+
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
Icon(
|
|
413
|
+
imageVector = Icons.Default.ChevronRight,
|
|
414
|
+
contentDescription = null,
|
|
415
|
+
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// State management with MVI
|
|
422
|
+
@Stable
|
|
423
|
+
sealed interface UserListUiState {
|
|
424
|
+
object Idle : UserListUiState
|
|
425
|
+
object Loading : UserListUiState
|
|
426
|
+
data class Success(val users: LazyPagingItems<User>) : UserListUiState
|
|
427
|
+
data class Error(val message: String) : UserListUiState
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
@Composable
|
|
431
|
+
fun rememberUserListUiState(
|
|
432
|
+
viewModel: UserListViewModel = hiltViewModel()
|
|
433
|
+
): UserListUiState {
|
|
434
|
+
val uiState by viewModel.uiState.collectAsState()
|
|
435
|
+
return uiState
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Custom composables
|
|
439
|
+
@Composable
|
|
440
|
+
fun LoadingIndicator(
|
|
441
|
+
modifier: Modifier = Modifier
|
|
442
|
+
) {
|
|
443
|
+
Box(
|
|
444
|
+
modifier = modifier.fillMaxWidth(),
|
|
445
|
+
contentAlignment = Alignment.Center
|
|
446
|
+
) {
|
|
447
|
+
CircularProgressIndicator()
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
@Composable
|
|
452
|
+
fun ErrorMessage(
|
|
453
|
+
message: String,
|
|
454
|
+
onRetry: () -> Unit,
|
|
455
|
+
modifier: Modifier = Modifier
|
|
456
|
+
) {
|
|
457
|
+
Column(
|
|
458
|
+
modifier = modifier
|
|
459
|
+
.fillMaxWidth()
|
|
460
|
+
.padding(16.dp),
|
|
461
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
462
|
+
) {
|
|
463
|
+
Text(
|
|
464
|
+
text = message,
|
|
465
|
+
style = MaterialTheme.typography.bodyMedium,
|
|
466
|
+
color = MaterialTheme.colorScheme.error,
|
|
467
|
+
textAlign = TextAlign.Center
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
Spacer(modifier = Modifier.height(8.dp))
|
|
471
|
+
|
|
472
|
+
Button(onClick = onRetry) {
|
|
473
|
+
Text("Retry")
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Spring Boot with Kotlin Patterns
|
|
480
|
+
|
|
481
|
+
```kotlin
|
|
482
|
+
@SpringBootApplication
|
|
483
|
+
@EnableTransactionManagement
|
|
484
|
+
class Application {
|
|
485
|
+
@Bean
|
|
486
|
+
fun objectMapper(): ObjectMapper = jacksonObjectMapper()
|
|
487
|
+
.registerModules(JavaTimeModule(), KotlinModule.Builder().build())
|
|
488
|
+
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
|
489
|
+
}
|
|
19
490
|
|
|
20
|
-
|
|
491
|
+
@Configuration
|
|
492
|
+
@EnableWebFluxSecurity
|
|
493
|
+
class SecurityConfig {
|
|
494
|
+
|
|
495
|
+
@Bean
|
|
496
|
+
fun securityWebFilterChain(
|
|
497
|
+
http: ServerHttpSecurity,
|
|
498
|
+
jwtDecoder: ReactiveJwtDecoder
|
|
499
|
+
): SecurityWebFilterChain {
|
|
500
|
+
return http
|
|
501
|
+
.csrf { it.disable() }
|
|
502
|
+
.authorizeExchange { exchanges ->
|
|
503
|
+
exchanges
|
|
504
|
+
.pathMatchers("/api/auth/**").permitAll()
|
|
505
|
+
.pathMatchers("/api/public/**").permitAll()
|
|
506
|
+
.pathMatchers("/actuator/health").permitAll()
|
|
507
|
+
.pathMatchers("/api/admin/**").hasRole("ADMIN")
|
|
508
|
+
.anyExchange().authenticated()
|
|
509
|
+
}
|
|
510
|
+
.oauth2ResourceServer { oauth2 ->
|
|
511
|
+
oauth2.jwt { jwt -> jwt.jwtDecoder(jwtDecoder) }
|
|
512
|
+
}
|
|
513
|
+
.build()
|
|
514
|
+
}
|
|
515
|
+
}
|
|
21
516
|
|
|
22
|
-
|
|
517
|
+
// Repository with Coroutines
|
|
518
|
+
@Repository
|
|
519
|
+
interface UserRepository : ReactiveCrudRepository<User, Long> {
|
|
520
|
+
suspend fun findByUsername(username: String): User?
|
|
521
|
+
fun findByEmailContaining(email: String): Flow<User>
|
|
522
|
+
fun findByActiveTrue(): Flow<User>
|
|
523
|
+
}
|
|
23
524
|
|
|
24
|
-
|
|
525
|
+
@Service
|
|
526
|
+
@Transactional
|
|
527
|
+
class UserService(
|
|
528
|
+
private val userRepository: UserRepository,
|
|
529
|
+
private val eventPublisher: ApplicationEventPublisher
|
|
530
|
+
) {
|
|
531
|
+
suspend fun createUser(request: CreateUserRequest): User {
|
|
532
|
+
return userEntity.toUser().also { user ->
|
|
533
|
+
userRepository.save(userEntity)
|
|
534
|
+
eventPublisher.publishEvent(UserCreatedEvent(user.id))
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
fun searchUsers(query: String): Flow<User> {
|
|
539
|
+
return userRepository.findByEmailContaining(query)
|
|
540
|
+
.flowOn(Dispatchers.IO)
|
|
541
|
+
.catch { error ->
|
|
542
|
+
log.error("Error searching users", error)
|
|
543
|
+
throw UserSearchException("Failed to search users", error)
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
25
547
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
548
|
+
// REST Controller with Coroutines
|
|
549
|
+
@RestController
|
|
550
|
+
@RequestMapping("/api/users")
|
|
551
|
+
@Validated
|
|
552
|
+
class UserController(
|
|
553
|
+
private val userService: UserService
|
|
554
|
+
) {
|
|
555
|
+
@GetMapping("/{id}")
|
|
556
|
+
suspend fun getUser(@PathVariable id: Long): ResponseEntity<UserDto> {
|
|
557
|
+
val user = userService.getUserById(id)
|
|
558
|
+
?: throw UserNotFoundException("User not found: $id")
|
|
559
|
+
|
|
560
|
+
return ResponseEntity.ok(user.toDto())
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
@GetMapping
|
|
564
|
+
fun searchUsers(
|
|
565
|
+
@RequestParam query: String?,
|
|
566
|
+
@RequestParam(defaultValue = "0") page: Int,
|
|
567
|
+
@RequestParam(defaultValue = "20") size: Int
|
|
568
|
+
): Flow<UserDto> {
|
|
569
|
+
return userService.searchUsers(query ?: "")
|
|
570
|
+
.map { it.toDto() }
|
|
571
|
+
.take(size.toLong())
|
|
572
|
+
.skip((page * size).toLong())
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
@PostMapping
|
|
576
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
577
|
+
suspend fun createUser(
|
|
578
|
+
@Valid @RequestBody request: CreateUserRequest
|
|
579
|
+
): UserDto {
|
|
580
|
+
val user = userService.createUser(request)
|
|
581
|
+
return user.toDto()
|
|
582
|
+
}
|
|
583
|
+
}
|
|
30
584
|
|
|
31
|
-
|
|
585
|
+
// Domain models with data classes
|
|
586
|
+
@Document
|
|
587
|
+
data class User(
|
|
588
|
+
@Id val id: String? = null,
|
|
589
|
+
val username: String,
|
|
590
|
+
val email: String,
|
|
591
|
+
val profile: UserProfile,
|
|
592
|
+
val preferences: UserPreferences,
|
|
593
|
+
val createdAt: Instant = Instant.now(),
|
|
594
|
+
val updatedAt: Instant = Instant.now(),
|
|
595
|
+
val active: Boolean = true
|
|
596
|
+
)
|
|
32
597
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
598
|
+
@Document
|
|
599
|
+
data class UserProfile(
|
|
600
|
+
val firstName: String,
|
|
601
|
+
val lastName: String,
|
|
602
|
+
val avatar: String? = null,
|
|
603
|
+
val bio: String? = null
|
|
604
|
+
)
|
|
38
605
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
606
|
+
// Extension functions for DTOs
|
|
607
|
+
fun User.toDto() = UserDto(
|
|
608
|
+
id = id!!,
|
|
609
|
+
username = username,
|
|
610
|
+
email = email,
|
|
611
|
+
firstName = profile.firstName,
|
|
612
|
+
lastName = profile.lastName,
|
|
613
|
+
avatar = profile.avatar,
|
|
614
|
+
bio = profile.bio,
|
|
615
|
+
active = active
|
|
616
|
+
)
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
## Performance Considerations
|
|
620
|
+
|
|
621
|
+
### Memory Optimization
|
|
622
|
+
|
|
623
|
+
```kotlin
|
|
624
|
+
// Efficient collections usage
|
|
625
|
+
class PerformanceOptimized {
|
|
626
|
+
|
|
627
|
+
// Use primitive collections for performance
|
|
628
|
+
private val userIdSet = Object2IntOpenHashMap<User>()
|
|
629
|
+
private val activeUsers = BooleanArrayList()
|
|
630
|
+
|
|
631
|
+
// Lazy initialization with delegation
|
|
632
|
+
private val expensiveResource: ExpensiveResource by lazy {
|
|
633
|
+
createExpensiveResource()
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Efficient string processing
|
|
637
|
+
fun processLargeText(text: String): List<String> {
|
|
638
|
+
return text.lineSequence()
|
|
639
|
+
.filter { it.isNotBlank() }
|
|
640
|
+
.map { it.trim() }
|
|
641
|
+
.toList()
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Efficient data processing with sequences
|
|
645
|
+
fun processUsers(users: List<User>): List<UserDto> {
|
|
646
|
+
return users.asSequence()
|
|
647
|
+
.filter { it.active }
|
|
648
|
+
.sortedBy { it.username }
|
|
649
|
+
.map { it.toDto() }
|
|
650
|
+
.toList()
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Coroutine performance patterns
|
|
655
|
+
class PerformanceService {
|
|
656
|
+
|
|
657
|
+
// Limit concurrency
|
|
658
|
+
private val dispatcher = Dispatchers.IO.limitedParallelism(10)
|
|
659
|
+
|
|
660
|
+
suspend fun processItems(items: List<Item>): List<Result> {
|
|
661
|
+
return coroutineScope {
|
|
662
|
+
items.map { item ->
|
|
663
|
+
async(dispatcher) {
|
|
664
|
+
processItem(item)
|
|
665
|
+
}
|
|
666
|
+
}.awaitAll()
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Efficient flow processing
|
|
671
|
+
fun processLargeStream(): Flow<Result> {
|
|
672
|
+
return generateSequence { generateNextItem() }
|
|
673
|
+
.asFlow()
|
|
674
|
+
.flowOn(Dispatchers.Default)
|
|
675
|
+
.buffer(Channel.CONFLATED) // Keep only latest
|
|
676
|
+
.conflate() // Skip intermediate values if consumer is slow
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Kotlin/Native and KMP Performance
|
|
682
|
+
|
|
683
|
+
```kotlin
|
|
684
|
+
// Expect/actual for platform-specific optimizations
|
|
685
|
+
expect class PlatformLogger() {
|
|
686
|
+
fun log(message: String)
|
|
687
|
+
fun logError(error: Throwable)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// JVM implementation
|
|
691
|
+
actual class PlatformLogger actual constructor() {
|
|
692
|
+
private val logger = LoggerFactory.getLogger(PlatformLogger::class.java)
|
|
693
|
+
|
|
694
|
+
actual fun log(message: String) {
|
|
695
|
+
logger.info(message)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
actual fun logError(error: Throwable) {
|
|
699
|
+
logger.error("Error occurred", error)
|
|
700
|
+
}
|
|
701
|
+
}
|
|
43
702
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
703
|
+
// Native implementation
|
|
704
|
+
actual class PlatformLogger actual constructor() {
|
|
705
|
+
actual fun log(message: String) {
|
|
706
|
+
println("[INFO] $message")
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
actual fun logError(error: Throwable) {
|
|
710
|
+
println("[ERROR] ${error.message}")
|
|
711
|
+
}
|
|
712
|
+
}
|
|
48
713
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
714
|
+
// Memory management in KMP
|
|
715
|
+
class SharedCache<K, V> {
|
|
716
|
+
private val cache = mutableMapOf<K, V>()
|
|
717
|
+
private val maxSize: Int
|
|
718
|
+
|
|
719
|
+
constructor(maxSize: Int = 100) {
|
|
720
|
+
this.maxSize = maxSize
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
fun get(key: K): V? = cache[key]
|
|
724
|
+
|
|
725
|
+
fun put(key: K, value: V) {
|
|
726
|
+
if (cache.size >= maxSize) {
|
|
727
|
+
val oldestKey = cache.keys.first()
|
|
728
|
+
cache.remove(oldestKey)
|
|
729
|
+
}
|
|
730
|
+
cache[key] = value
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
fun clear() {
|
|
734
|
+
cache.clear()
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
```
|
|
55
738
|
|
|
56
|
-
|
|
57
|
-
- **Jetpack Compose**: Declarative UI
|
|
58
|
-
- **ViewModel**: UI state management
|
|
59
|
-
- **Room**: Database abstraction
|
|
60
|
-
- **Retrofit**: Network requests
|
|
739
|
+
### Profiling and Monitoring
|
|
61
740
|
|
|
62
|
-
## Examples
|
|
63
741
|
```bash
|
|
64
|
-
|
|
742
|
+
# Kotlin coroutines debugging
|
|
743
|
+
# Add to build.gradle.kts
|
|
744
|
+
dependencies {
|
|
745
|
+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-debug")
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
# Enable coroutine debugging
|
|
749
|
+
# VM options: -Dkotlinx.coroutines.debug=on
|
|
750
|
+
|
|
751
|
+
# Android Studio profiler
|
|
752
|
+
# CPU Profiler, Memory Profiler, Network Profiler
|
|
753
|
+
|
|
754
|
+
# JVM debugging with jstat/jmap
|
|
755
|
+
jstat -gc <pid> 1000 10
|
|
756
|
+
jmap -dump:format=b,file=heap.hprof <pid>
|
|
757
|
+
|
|
758
|
+
# Gradle build performance
|
|
759
|
+
./gradlew build --scan
|
|
760
|
+
./gradlew build --profile
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
## Testing Strategy
|
|
764
|
+
|
|
765
|
+
### Kotest Configuration
|
|
766
|
+
|
|
767
|
+
```kotlin
|
|
768
|
+
// build.gradle.kts
|
|
769
|
+
tasks.test {
|
|
770
|
+
useTestNG() // or useJUnitPlatform()
|
|
771
|
+
|
|
772
|
+
// Configure test execution
|
|
773
|
+
testLogging {
|
|
774
|
+
events("passed", "skipped", "failed")
|
|
775
|
+
showExceptions = true
|
|
776
|
+
showCauses = true
|
|
777
|
+
showStackTraces = true
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// Parallel test execution
|
|
781
|
+
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Test configuration
|
|
785
|
+
@TestConfiguration
|
|
786
|
+
class TestConfig {
|
|
787
|
+
@Bean
|
|
788
|
+
@Primary
|
|
789
|
+
fun userRepository(): UserRepository = mockk()
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Kotest test classes
|
|
793
|
+
class UserRepositoryTest : FunSpec({
|
|
794
|
+
val repository = mockk<UserRepository>()
|
|
795
|
+
val service = UserService(repository)
|
|
796
|
+
|
|
797
|
+
test("should create user successfully") {
|
|
798
|
+
// Given
|
|
799
|
+
val request = CreateUserRequest("testuser", "test@example.com")
|
|
800
|
+
val expectedUser = User("1", "testuser", "test@example.com")
|
|
801
|
+
|
|
802
|
+
coEvery { repository.findByUsername("testuser") } returns null
|
|
803
|
+
coEvery { repository.save(any()) } returns expectedUser
|
|
804
|
+
|
|
805
|
+
// When
|
|
806
|
+
val result = service.createUser(request)
|
|
807
|
+
|
|
808
|
+
// Then
|
|
809
|
+
result shouldBe expectedUser
|
|
810
|
+
coVerify { repository.save(any()) }
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
context("user search") {
|
|
814
|
+
test("should return users matching query") {
|
|
815
|
+
// Given
|
|
816
|
+
val users = flowOf(
|
|
817
|
+
User("1", "john@example.com"),
|
|
818
|
+
User("2", "jane@example.com")
|
|
819
|
+
)
|
|
820
|
+
every { repository.findByEmailContaining("john") } returns users
|
|
821
|
+
|
|
822
|
+
// When
|
|
823
|
+
val results = service.searchUsers("john").toList()
|
|
824
|
+
|
|
825
|
+
// Then
|
|
826
|
+
results shouldHaveSize 1
|
|
827
|
+
results.first().email shouldBe "john@example.com"
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
})
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
### Android Testing with Compose
|
|
834
|
+
|
|
835
|
+
```kotlin
|
|
836
|
+
@RunWith(AndroidJUnit4::class)
|
|
837
|
+
class UserProfileScreenTest {
|
|
838
|
+
|
|
839
|
+
@get:Rule
|
|
840
|
+
val composeTestRule = createComposeRule()
|
|
841
|
+
|
|
842
|
+
@Test
|
|
843
|
+
fun userProfileScreen_displaysUserInformation() {
|
|
844
|
+
// Given
|
|
845
|
+
val user = User(
|
|
846
|
+
id = "1",
|
|
847
|
+
name = "John Doe",
|
|
848
|
+
email = "john@example.com",
|
|
849
|
+
avatar = "https://example.com/avatar.jpg"
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
// When
|
|
853
|
+
composeTestRule.setContent {
|
|
854
|
+
UserProfileScreen(
|
|
855
|
+
user = user,
|
|
856
|
+
onEditClick = {}
|
|
857
|
+
)
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// Then
|
|
861
|
+
composeTestRule
|
|
862
|
+
.onNodeWithText("John Doe")
|
|
863
|
+
.assertIsDisplayed()
|
|
864
|
+
|
|
865
|
+
composeTestRule
|
|
866
|
+
.onNodeWithText("john@example.com")
|
|
867
|
+
.assertIsDisplayed()
|
|
868
|
+
|
|
869
|
+
composeTestRule
|
|
870
|
+
.onNodeWithContentDescription("User avatar")
|
|
871
|
+
.assertIsDisplayed()
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
@Test
|
|
875
|
+
fun userProfileScreen_whenEditClicked_callsOnEditClick() {
|
|
876
|
+
// Given
|
|
877
|
+
var editClicked = false
|
|
878
|
+
val user = User("1", "John Doe", "john@example.com")
|
|
879
|
+
|
|
880
|
+
// When
|
|
881
|
+
composeTestRule.setContent {
|
|
882
|
+
UserProfileScreen(
|
|
883
|
+
user = user,
|
|
884
|
+
onEditClick = { editClicked = true }
|
|
885
|
+
)
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
composeTestRule
|
|
889
|
+
.onNodeWithText("Edit")
|
|
890
|
+
.performClick()
|
|
891
|
+
|
|
892
|
+
// Then
|
|
893
|
+
assertTrue(editClicked)
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// ViewModel testing
|
|
898
|
+
@ExtendWith(MockitoExtension::class)
|
|
899
|
+
class UserListViewModelTest {
|
|
900
|
+
|
|
901
|
+
@Mock
|
|
902
|
+
private lateinit var userRepository: UserRepository
|
|
903
|
+
|
|
904
|
+
private lateinit var viewModel: UserListViewModel
|
|
905
|
+
|
|
906
|
+
@Before
|
|
907
|
+
fun setup() {
|
|
908
|
+
viewModel = UserListViewModel(userRepository)
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
@Test
|
|
912
|
+
fun `loadUsers should update UI state with users`() = runTest {
|
|
913
|
+
// Given
|
|
914
|
+
val users = flowOf(
|
|
915
|
+
User("1", "User 1"),
|
|
916
|
+
User("2", "User 2")
|
|
917
|
+
)
|
|
918
|
+
whenever(userRepository.getUsers()).thenReturn(users)
|
|
919
|
+
|
|
920
|
+
// When
|
|
921
|
+
viewModel.loadUsers()
|
|
922
|
+
|
|
923
|
+
// Then
|
|
924
|
+
val state = viewModel.uiState.value
|
|
925
|
+
assertTrue(state is UserListUiState.Success)
|
|
926
|
+
assertEquals(2, (state as UserListUiState.Success).users.count())
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### Integration Testing
|
|
932
|
+
|
|
933
|
+
```kotlin
|
|
934
|
+
@SpringBootTest
|
|
935
|
+
@Testcontainers
|
|
936
|
+
@Transactional
|
|
937
|
+
class UserControllerIntegrationTest {
|
|
938
|
+
|
|
939
|
+
companion object {
|
|
940
|
+
@Container
|
|
941
|
+
@JvmStatic
|
|
942
|
+
val postgres = PostgreSQLContainer<Nothing>("postgres:16-alpine")
|
|
943
|
+
.apply {
|
|
944
|
+
withDatabaseName("testdb")
|
|
945
|
+
withUsername("test")
|
|
946
|
+
withPassword("test")
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
@JvmStatic
|
|
950
|
+
@DynamicPropertySource
|
|
951
|
+
fun postgresProperties(registry: DynamicPropertyRegistry) {
|
|
952
|
+
registry.add("spring.r2dbc.url") {
|
|
953
|
+
"r2dbc:postgresql://${postgres.host}:${postgres.firstMappedPort}/testdb"
|
|
954
|
+
}
|
|
955
|
+
registry.add("spring.r2dbc.username") { postgres.username }
|
|
956
|
+
registry.add("spring.r2dbc.password") { postgres.password }
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
@Autowired
|
|
961
|
+
private lateinit var webTestClient: WebTestClient
|
|
962
|
+
|
|
963
|
+
@Test
|
|
964
|
+
fun `should create and retrieve user`() {
|
|
965
|
+
// Given
|
|
966
|
+
val createUserRequest = CreateUserRequest(
|
|
967
|
+
username = "testuser",
|
|
968
|
+
email = "test@example.com"
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
// When
|
|
972
|
+
val createResponse = webTestClient.post()
|
|
973
|
+
.uri("/api/users")
|
|
974
|
+
.bodyValue(createUserRequest)
|
|
975
|
+
.exchange()
|
|
976
|
+
.expectStatus().isCreated
|
|
977
|
+
.expectBody(UserDto::class.java)
|
|
978
|
+
.returnResult()
|
|
979
|
+
.responseBody!!
|
|
980
|
+
|
|
981
|
+
// Then
|
|
982
|
+
val retrievedUser = webTestClient.get()
|
|
983
|
+
.uri("/api/users/${createResponse.id}")
|
|
984
|
+
.exchange()
|
|
985
|
+
.expectStatus().isOk
|
|
986
|
+
.expectBody(UserDto::class.java)
|
|
987
|
+
.returnResult()
|
|
988
|
+
.responseBody!!
|
|
989
|
+
|
|
990
|
+
assertEquals(createResponse.id, retrievedUser.id)
|
|
991
|
+
assertEquals(createResponse.username, retrievedUser.username)
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
## Security Best Practices
|
|
997
|
+
|
|
998
|
+
### Input Validation and Sanitization
|
|
999
|
+
|
|
1000
|
+
```kotlin
|
|
1001
|
+
// Data class with validation
|
|
1002
|
+
data class CreateUserRequest(
|
|
1003
|
+
@field:NotBlank(message = "Username is required")
|
|
1004
|
+
@field:Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
|
|
1005
|
+
@field:Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Username can only contain letters, numbers, and underscores")
|
|
1006
|
+
val username: String,
|
|
1007
|
+
|
|
1008
|
+
@field:NotBlank(message = "Email is required")
|
|
1009
|
+
@field:Email(message = "Invalid email format")
|
|
1010
|
+
@field:Size(max = 100, message = "Email must be less than 100 characters")
|
|
1011
|
+
val email: String,
|
|
1012
|
+
|
|
1013
|
+
@field:NotBlank(message = "Password is required")
|
|
1014
|
+
@field:Size(min = 8, max = 128, message = "Password must be between 8 and 128 characters")
|
|
1015
|
+
@field:Pattern(
|
|
1016
|
+
regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]",
|
|
1017
|
+
message = "Password must contain at least one lowercase letter, one uppercase letter, one digit, and one special character"
|
|
1018
|
+
)
|
|
1019
|
+
val password: String
|
|
1020
|
+
) {
|
|
1021
|
+
init {
|
|
1022
|
+
require(!email.lowercase().contains("@admin.com")) {
|
|
1023
|
+
"Admin email domains are not allowed"
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Security service
|
|
1029
|
+
@Service
|
|
1030
|
+
class SecurityService {
|
|
1031
|
+
private val passwordEncoder = BCryptPasswordEncoder(12)
|
|
1032
|
+
|
|
1033
|
+
fun encodePassword(rawPassword: String): String {
|
|
1034
|
+
return passwordEncoder.encode(rawPassword)
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
fun matches(rawPassword: String, encodedPassword: String): Boolean {
|
|
1038
|
+
return passwordEncoder.matches(rawPassword, encodedPassword)
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
fun validateInput(input: String): String {
|
|
1042
|
+
return input
|
|
1043
|
+
.replace(Regex("<script.*?>.*?</script>"), "")
|
|
1044
|
+
.replace(Regex("javascript:"), "")
|
|
1045
|
+
.trim()
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
### Authentication and Authorization
|
|
1051
|
+
|
|
1052
|
+
```kotlin
|
|
1053
|
+
@Configuration
|
|
1054
|
+
@EnableWebFluxSecurity
|
|
1055
|
+
class SecurityConfig {
|
|
1056
|
+
|
|
1057
|
+
@Bean
|
|
1058
|
+
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
1059
|
+
return http
|
|
1060
|
+
.csrf { it.disable() }
|
|
1061
|
+
.authorizeExchange { exchanges ->
|
|
1062
|
+
exchanges
|
|
1063
|
+
.pathMatchers("/api/auth/**").permitAll()
|
|
1064
|
+
.pathMatchers("/api/public/**").permitAll()
|
|
1065
|
+
.pathMatchers("/actuator/health").permitAll()
|
|
1066
|
+
.pathMatchers("/api/admin/**").hasRole("ADMIN")
|
|
1067
|
+
.anyExchange().authenticated()
|
|
1068
|
+
}
|
|
1069
|
+
.oauth2ResourceServer { oauth2 ->
|
|
1070
|
+
oauth2.jwt { jwt -> jwt.jwtDecoder(jwtDecoder()) }
|
|
1071
|
+
}
|
|
1072
|
+
.build()
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
@Bean
|
|
1076
|
+
fun reactiveAuthenticationManager(
|
|
1077
|
+
userRepository: UserRepository,
|
|
1078
|
+
passwordService: PasswordService
|
|
1079
|
+
): ReactiveAuthenticationManager {
|
|
1080
|
+
return UserDetailsRepositoryReactiveAuthenticationManager(
|
|
1081
|
+
userRepository.toUserDetailsService()
|
|
1082
|
+
).apply {
|
|
1083
|
+
setPasswordEncoder(passwordService.encoder)
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// Method-level security
|
|
1089
|
+
@Service
|
|
1090
|
+
class DocumentService {
|
|
1091
|
+
|
|
1092
|
+
@PreAuthorize("hasRole('USER') and @documentSecurity.canRead(#documentId, authentication.name)")
|
|
1093
|
+
suspend fun getDocument(documentId: Long): Document {
|
|
1094
|
+
return documentRepository.findById(documentId)
|
|
1095
|
+
?: throw DocumentNotFoundException("Document not found: $documentId")
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
@PreAuthorize("hasRole('ADMIN') or @documentSecurity.isOwner(#documentId, authentication.name)")
|
|
1099
|
+
suspend fun updateDocument(documentId: Long, updateDto: DocumentUpdateDto): Document {
|
|
1100
|
+
val document = getDocument(documentId)
|
|
1101
|
+
if (!canUpdate(document, SecurityContextHolder.getContext().authentication.name)) {
|
|
1102
|
+
throw AccessDeniedException("Not authorized to update document")
|
|
1103
|
+
}
|
|
1104
|
+
return documentRepository.save(document.update(updateDto))
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
## Integration Patterns
|
|
1110
|
+
|
|
1111
|
+
### Database Integration with Exposed
|
|
1112
|
+
|
|
1113
|
+
```kotlin
|
|
1114
|
+
object Users : Table("users") {
|
|
1115
|
+
val id = uuid("id").autoGenerate()
|
|
1116
|
+
val username = varchar("username", 50).uniqueIndex()
|
|
1117
|
+
val email = varchar("email", 100).uniqueIndex()
|
|
1118
|
+
val createdAt = datetime("created_at").defaultExpression(CurrentDateTime())
|
|
1119
|
+
val updatedAt = datetime("updated_at").defaultExpression(CurrentDateTime())
|
|
1120
|
+
val active = bool("active").default(true)
|
|
1121
|
+
|
|
1122
|
+
override val primaryKey = PrimaryKey(id)
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
@Entity
|
|
1126
|
+
data class User(
|
|
1127
|
+
val id: UUID = UUID.randomUUID(),
|
|
1128
|
+
val username: String,
|
|
1129
|
+
val email: String,
|
|
1130
|
+
val createdAt: Instant = Instant.now(),
|
|
1131
|
+
val updatedAt: Instant = Instant.now(),
|
|
1132
|
+
val active: Boolean = true
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
// Repository with Exposed
|
|
1136
|
+
class UserRepository(
|
|
1137
|
+
private val database: Database
|
|
1138
|
+
) {
|
|
1139
|
+
suspend fun findById(id: UUID): User? = withContext(Dispatchers.IO) {
|
|
1140
|
+
transaction(database) {
|
|
1141
|
+
Users.select { Users.id eq id }
|
|
1142
|
+
.map { it.toUser() }
|
|
1143
|
+
.singleOrNull()
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
suspend fun save(user: User): User = withContext(Dispatchers.IO) {
|
|
1148
|
+
transaction(database) {
|
|
1149
|
+
Users.upsert {
|
|
1150
|
+
it[id] = user.id
|
|
1151
|
+
it[username] = user.username
|
|
1152
|
+
it[email] = user.email
|
|
1153
|
+
it[createdAt] = user.createdAt
|
|
1154
|
+
it[updatedAt] = Instant.now()
|
|
1155
|
+
it[active] = user.active
|
|
1156
|
+
}
|
|
1157
|
+
user.copy(updatedAt = Instant.now())
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
fun findAllActive(): Flow<User> = flow {
|
|
1162
|
+
transaction(database) {
|
|
1163
|
+
Users.select { Users.active eq true }
|
|
1164
|
+
.map { it.toUser() }
|
|
1165
|
+
.forEach { user -> emit(user) }
|
|
1166
|
+
}
|
|
1167
|
+
}.flowOn(Dispatchers.IO)
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
private fun ResultRow.toUser() = User(
|
|
1171
|
+
id = this[Users.id],
|
|
1172
|
+
username = this[Users.username],
|
|
1173
|
+
email = this[Users.email],
|
|
1174
|
+
createdAt = this[Users.createdAt].toInstant(),
|
|
1175
|
+
updatedAt = this[Users.updatedAt].toInstant(),
|
|
1176
|
+
active = this[Users.active]
|
|
1177
|
+
)
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
### Ktor Client Integration
|
|
1181
|
+
|
|
1182
|
+
```kotlin
|
|
1183
|
+
// Ktor client configuration
|
|
1184
|
+
class ApiClient {
|
|
1185
|
+
private val httpClient = HttpClient(CIO) {
|
|
1186
|
+
install(ContentNegotiation) {
|
|
1187
|
+
json(
|
|
1188
|
+
Json {
|
|
1189
|
+
ignoreUnknownKeys = true
|
|
1190
|
+
isLenient = true
|
|
1191
|
+
}
|
|
1192
|
+
)
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
install(Auth) {
|
|
1196
|
+
bearer {
|
|
1197
|
+
loadTokens {
|
|
1198
|
+
BearerTokens(accessToken = getAccessToken(), refreshToken = getRefreshToken())
|
|
1199
|
+
}
|
|
1200
|
+
refreshTokens {
|
|
1201
|
+
val refreshedTokens = refreshAccessToken()
|
|
1202
|
+
BearerTokens(
|
|
1203
|
+
accessToken = refreshedTokens.accessToken,
|
|
1204
|
+
refreshToken = refreshedTokens.refreshToken
|
|
1205
|
+
)
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
defaultRequest {
|
|
1211
|
+
url("https://api.example.com/")
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
suspend fun getUsers(): List<User> = try {
|
|
1216
|
+
httpClient.get("users").body()
|
|
1217
|
+
} catch (e: ClientRequestException) {
|
|
1218
|
+
when (e.response.status) {
|
|
1219
|
+
HttpStatusCode.NotFound -> emptyList()
|
|
1220
|
+
HttpStatusCode.Unauthorized -> throw AuthenticationException()
|
|
1221
|
+
else -> throw ApiException("Failed to fetch users", e)
|
|
1222
|
+
}
|
|
1223
|
+
} catch (e: Exception) {
|
|
1224
|
+
throw NetworkException("Network error", e)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
suspend fun createUser(user: CreateUserRequest): User = httpClient.post("users") {
|
|
1228
|
+
contentType(ContentType.Application.Json)
|
|
1229
|
+
setBody(user)
|
|
1230
|
+
}.body()
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// Multiplatform API client
|
|
1234
|
+
class SharedApiClient(
|
|
1235
|
+
private val platform: Platform
|
|
1236
|
+
) {
|
|
1237
|
+
private val httpClient = HttpClient {
|
|
1238
|
+
if (platform.isDebug) {
|
|
1239
|
+
install(Logging) {
|
|
1240
|
+
logger = Logger.DEFAULT
|
|
1241
|
+
level = LogLevel.INFO
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
expectSuccess = true
|
|
1246
|
+
install(ContentNegotiation) {
|
|
1247
|
+
json(Json {
|
|
1248
|
+
ignoreUnknownKeys = true
|
|
1249
|
+
useAlternativeNames = false
|
|
1250
|
+
})
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
suspend fun fetchData(): ApiResponse = httpClient.get("data").body()
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// Platform-specific implementations
|
|
1258
|
+
// JVM
|
|
1259
|
+
class AndroidPlatform : Platform {
|
|
1260
|
+
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
|
1261
|
+
override val isDebug: Boolean = BuildConfig.DEBUG
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// iOS
|
|
1265
|
+
class IOSPlatform : Platform {
|
|
1266
|
+
override val name: String = UIDevice.currentDevice.systemName()
|
|
1267
|
+
override val isDebug: Boolean = Platform.isDebugBinary
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
actual fun getPlatform(): Platform =
|
|
1271
|
+
if (Platform.osFamily == OsFamily.IOS) IOSPlatform() else AndroidPlatform()
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
### Message Queue Integration
|
|
1275
|
+
|
|
1276
|
+
```kotlin
|
|
1277
|
+
// RabbitMQ with Spring Boot
|
|
1278
|
+
@Configuration
|
|
1279
|
+
class RabbitConfig {
|
|
1280
|
+
|
|
1281
|
+
@Bean
|
|
1282
|
+
fun userQueue(): Queue = QueueBuilder.durable("user.queue").build()
|
|
1283
|
+
|
|
1284
|
+
@Bean
|
|
1285
|
+
fun userExchange(): TopicExchange = TopicExchange("user.exchange")
|
|
1286
|
+
|
|
1287
|
+
@Bean
|
|
1288
|
+
fun userBinding(): Binding = BindingBuilder
|
|
1289
|
+
.bind(userQueue())
|
|
1290
|
+
.to(userExchange())
|
|
1291
|
+
.with("user.created")
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
@Service
|
|
1295
|
+
class UserEventPublisher(
|
|
1296
|
+
private val rabbitTemplate: RabbitTemplate
|
|
1297
|
+
) {
|
|
1298
|
+
fun publishUserCreated(user: User) {
|
|
1299
|
+
val event = UserCreatedEvent(
|
|
1300
|
+
userId = user.id,
|
|
1301
|
+
username = user.username,
|
|
1302
|
+
email = user.email,
|
|
1303
|
+
timestamp = Instant.now()
|
|
1304
|
+
)
|
|
1305
|
+
|
|
1306
|
+
rabbitTemplate.convertAndSend(
|
|
1307
|
+
"user.exchange",
|
|
1308
|
+
"user.created",
|
|
1309
|
+
event
|
|
1310
|
+
)
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
@Component
|
|
1315
|
+
class UserEventConsumer {
|
|
1316
|
+
|
|
1317
|
+
private val logger = LoggerFactory.getLogger(UserEventConsumer::class.java)
|
|
1318
|
+
|
|
1319
|
+
@RabbitListener(queues = ["user.queue"])
|
|
1320
|
+
fun handleUserCreated(event: UserCreatedEvent) {
|
|
1321
|
+
logger.info("Received user created event: $event")
|
|
1322
|
+
|
|
1323
|
+
// Process event
|
|
1324
|
+
sendWelcomeEmail(event.userId, event.email)
|
|
1325
|
+
createAuditRecord(event)
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
private fun sendWelcomeEmail(userId: UUID, email: String) {
|
|
1329
|
+
// Implementation
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
private fun createAuditRecord(event: UserCreatedEvent) {
|
|
1333
|
+
// Implementation
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
## Modern Development Workflow
|
|
1339
|
+
|
|
1340
|
+
### Gradle Configuration
|
|
1341
|
+
|
|
1342
|
+
```kotlin
|
|
1343
|
+
// build.gradle.kts
|
|
1344
|
+
plugins {
|
|
1345
|
+
kotlin("multiplatform") version "2.0.20"
|
|
1346
|
+
kotlin("plugin.serialization") version "2.0.20"
|
|
1347
|
+
application
|
|
1348
|
+
id("org.springframework.boot") version "3.3.0"
|
|
1349
|
+
id("io.spring.dependency-management") version "1.1.5"
|
|
1350
|
+
id("com.google.cloud.tools.jib") version "3.4.1"
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
kotlin {
|
|
1354
|
+
jvmToolchain(21)
|
|
1355
|
+
|
|
1356
|
+
jvm {
|
|
1357
|
+
withJava()
|
|
1358
|
+
testRuns.named("test") {
|
|
1359
|
+
executionTask.configure {
|
|
1360
|
+
useJUnitPlatform()
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
js(IR) {
|
|
1366
|
+
browser {
|
|
1367
|
+
commonWebpackConfig {
|
|
1368
|
+
cssSupport {
|
|
1369
|
+
enabled.set(true)
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
listOf(
|
|
1376
|
+
iosX64(),
|
|
1377
|
+
iosArm64(),
|
|
1378
|
+
iosSimulatorArm64()
|
|
1379
|
+
).forEach { iosTarget ->
|
|
1380
|
+
iosTarget.binaries.framework {
|
|
1381
|
+
baseName = "Shared"
|
|
1382
|
+
isStatic = true
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
sourceSets {
|
|
1387
|
+
commonMain.dependencies {
|
|
1388
|
+
implementation(kotlin("stdlib"))
|
|
1389
|
+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
|
|
1390
|
+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0")
|
|
1391
|
+
implementation("io.ktor:ktor-client-core:2.3.12")
|
|
1392
|
+
implementation("io.ktor:ktor-client-content-negotiation:2.3.12")
|
|
1393
|
+
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
commonTest.dependencies {
|
|
1397
|
+
implementation(kotlin("test"))
|
|
1398
|
+
implementation(kotlin("test-coroutines"))
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
jvmMain.dependencies {
|
|
1402
|
+
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
|
1403
|
+
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
|
|
1404
|
+
implementation("org.springframework.boot:spring-boot-starter-amqp")
|
|
1405
|
+
implementation("io.ktor:ktor-client-okhttp:2.3.12")
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
androidMain.dependencies {
|
|
1409
|
+
implementation("io.ktor:ktor-client-android:2.3.12")
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
iosMain.dependencies {
|
|
1413
|
+
implementation("io.ktor:ktor-client-darwin:2.3.12")
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
application {
|
|
1419
|
+
mainClass.set("com.example.ApplicationKt")
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
tasks.withType<KotlinCompile> {
|
|
1423
|
+
kotlinOptions {
|
|
1424
|
+
jvmTarget = "21"
|
|
1425
|
+
freeCompilerArgs += listOf(
|
|
1426
|
+
"-Xjsr305=strict",
|
|
1427
|
+
"-Xjvm-default=all",
|
|
1428
|
+
"-opt-in=kotlin.RequiresOptIn"
|
|
1429
|
+
)
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// Jib configuration for containerization
|
|
1434
|
+
jib {
|
|
1435
|
+
from {
|
|
1436
|
+
image = "eclipse-temurin:21-jre-alpine"
|
|
1437
|
+
}
|
|
1438
|
+
to {
|
|
1439
|
+
image = "my-registry.com/kotlin-app:latest"
|
|
1440
|
+
}
|
|
1441
|
+
container {
|
|
1442
|
+
jvmFlags = listOf("-Xms512m", "-Xmx2g")
|
|
1443
|
+
ports = listOf("8080")
|
|
1444
|
+
environment = mapOf(
|
|
1445
|
+
"SPRING_PROFILES_ACTIVE" to "prod"
|
|
1446
|
+
)
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
```
|
|
1450
|
+
|
|
1451
|
+
### Pre-commit Configuration
|
|
1452
|
+
|
|
1453
|
+
```yaml
|
|
1454
|
+
# .pre-commit-config.yaml
|
|
1455
|
+
repos:
|
|
1456
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
1457
|
+
rev: v5.0.0
|
|
1458
|
+
hooks:
|
|
1459
|
+
- id: trailing-whitespace
|
|
1460
|
+
- id: end-of-file-fixer
|
|
1461
|
+
- id: check-yaml
|
|
1462
|
+
- id: check-added-large-files
|
|
1463
|
+
- id: check-merge-conflict
|
|
1464
|
+
|
|
1465
|
+
- repo: https://github.com/pinterest/ktlint
|
|
1466
|
+
rev: 1.2.1
|
|
1467
|
+
hooks:
|
|
1468
|
+
- id: ktlint
|
|
1469
|
+
args: ["--android", "--color"]
|
|
1470
|
+
|
|
1471
|
+
- repo: local
|
|
1472
|
+
hooks:
|
|
1473
|
+
- id: gradle-detekt
|
|
1474
|
+
name: Static analysis with Detekt
|
|
1475
|
+
entry: ./gradlew detekt
|
|
1476
|
+
language: system
|
|
1477
|
+
files: '\.(kt|kts)$'
|
|
1478
|
+
pass_filenames: false
|
|
1479
|
+
|
|
1480
|
+
- id: gradle-test
|
|
1481
|
+
name: Run tests
|
|
1482
|
+
entry: ./gradlew test
|
|
1483
|
+
language: system
|
|
1484
|
+
pass_filenames: false
|
|
1485
|
+
always_run: true
|
|
1486
|
+
|
|
1487
|
+
- id: gradle-check
|
|
1488
|
+
name: Run all checks
|
|
1489
|
+
entry: ./gradlew check
|
|
1490
|
+
language: system
|
|
1491
|
+
pass_filenames: false
|
|
1492
|
+
always_run: true
|
|
1493
|
+
```
|
|
1494
|
+
|
|
1495
|
+
### Docker Configuration
|
|
1496
|
+
|
|
1497
|
+
```dockerfile
|
|
1498
|
+
# Multi-stage Docker build
|
|
1499
|
+
FROM eclipse-temurin:21-jdk-alpine AS builder
|
|
1500
|
+
|
|
1501
|
+
WORKDIR /app
|
|
1502
|
+
|
|
1503
|
+
# Copy gradle wrapper and cache dependencies
|
|
1504
|
+
COPY gradlew gradlew.bat gradle/ ./
|
|
1505
|
+
COPY build.gradle.kts settings.gradle.kts ./
|
|
1506
|
+
|
|
1507
|
+
RUN ./gradlew --no-daemon dependencies
|
|
1508
|
+
|
|
1509
|
+
# Copy source code
|
|
1510
|
+
COPY src/ ./src/
|
|
1511
|
+
|
|
1512
|
+
# Build application
|
|
1513
|
+
RUN ./gradlew --no-daemon bootJar -x test
|
|
1514
|
+
|
|
1515
|
+
# Runtime stage
|
|
1516
|
+
FROM eclipse-temurin:21-jre-alpine
|
|
1517
|
+
|
|
1518
|
+
RUN addgroup --system --gid 1001 appgroup && \
|
|
1519
|
+
adduser --system --uid 1001 --ingroup appgroup appuser && \
|
|
1520
|
+
mkdir -p /app && \
|
|
1521
|
+
chown -R appuser:appgroup /app
|
|
1522
|
+
|
|
1523
|
+
WORKDIR /app
|
|
1524
|
+
|
|
1525
|
+
COPY --from=builder /app/build/libs/*.jar app.jar
|
|
1526
|
+
|
|
1527
|
+
RUN chmod +x app.jar
|
|
1528
|
+
|
|
1529
|
+
USER appuser
|
|
1530
|
+
|
|
1531
|
+
EXPOSE 8080
|
|
1532
|
+
|
|
1533
|
+
ENV JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseG1GC -XX:+UseStringDeduplication"
|
|
1534
|
+
|
|
1535
|
+
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
|
65
1536
|
```
|
|
66
1537
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- Relevant test suites and sample data.
|
|
1538
|
+
## Cross-Platform Development Patterns
|
|
1539
|
+
|
|
1540
|
+
### KMP Shared Business Logic
|
|
71
1541
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1542
|
+
```kotlin
|
|
1543
|
+
// commonMain/kotlin/com/example/data/models/User.kt
|
|
1544
|
+
@Serializable
|
|
1545
|
+
data class User(
|
|
1546
|
+
val id: String,
|
|
1547
|
+
val username: String,
|
|
1548
|
+
val email: String,
|
|
1549
|
+
val avatar: String? = null,
|
|
1550
|
+
val createdAt: Instant = Clock.System.now()
|
|
1551
|
+
)
|
|
75
1552
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
1553
|
+
// commonMain/kotlin/com/example/data/repository/UserRepository.kt
|
|
1554
|
+
interface UserRepository {
|
|
1555
|
+
suspend fun getUsers(): Result<List<User>>
|
|
1556
|
+
suspend fun getUserById(id: String): Result<User>
|
|
1557
|
+
suspend fun createUser(user: User): Result<User>
|
|
1558
|
+
}
|
|
79
1559
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1560
|
+
// commonMain/kotlin/com/example/data/service/UserService.kt
|
|
1561
|
+
class UserService(
|
|
1562
|
+
private val repository: UserRepository,
|
|
1563
|
+
private val logger: Logger
|
|
1564
|
+
) {
|
|
1565
|
+
suspend fun getAllUsers(): Result<List<User>> {
|
|
1566
|
+
return try {
|
|
1567
|
+
val users = repository.getUsers().getOrElse { error ->
|
|
1568
|
+
logger.error("Failed to fetch users", error)
|
|
1569
|
+
return Result.failure(error)
|
|
1570
|
+
}
|
|
1571
|
+
Result.success(users)
|
|
1572
|
+
} catch (e: Exception) {
|
|
1573
|
+
logger.error("Unexpected error fetching users", e)
|
|
1574
|
+
Result.failure(e)
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
fun getUsersFlow(): Flow<List<User>> = flow {
|
|
1579
|
+
while (currentCoroutineContext()[Job]?.isActive == true) {
|
|
1580
|
+
val result = getAllUsers()
|
|
1581
|
+
if (result.isSuccess) {
|
|
1582
|
+
emit(result.getOrNull() ?: emptyList())
|
|
1583
|
+
}
|
|
1584
|
+
delay(30.seconds) // Refresh every 30 seconds
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
83
1588
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
1589
|
+
// Platform-specific implementations
|
|
1590
|
+
// androidMain/kotlin/com/example/data/repository/AndroidUserRepository.kt
|
|
1591
|
+
class AndroidUserRepository(
|
|
1592
|
+
private val context: Context
|
|
1593
|
+
) : UserRepository {
|
|
1594
|
+
|
|
1595
|
+
private val httpClient = HttpClient(OkHttp) {
|
|
1596
|
+
install(ContentNegotiation) {
|
|
1597
|
+
json(Json {
|
|
1598
|
+
ignoreUnknownKeys = true
|
|
1599
|
+
})
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
override suspend fun getUsers(): Result<List<User>> {
|
|
1604
|
+
return try {
|
|
1605
|
+
val response = httpClient.get("https://api.example.com/users")
|
|
1606
|
+
val users = response.body<List<User>>()
|
|
1607
|
+
Result.success(users)
|
|
1608
|
+
} catch (e: Exception) {
|
|
1609
|
+
Result.failure(e)
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
override suspend fun getUserById(id: String): Result<User> {
|
|
1614
|
+
// Implementation
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
override suspend fun createUser(user: User): Result<User> {
|
|
1618
|
+
// Implementation
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
87
1621
|
|
|
88
|
-
|
|
89
|
-
|
|
1622
|
+
// iosMain/kotlin/com/example/data/repository/IosUserRepository.kt
|
|
1623
|
+
class IosUserRepository : UserRepository {
|
|
1624
|
+
|
|
1625
|
+
private val httpClient = HttpClient(Darwin) {
|
|
1626
|
+
install(ContentNegotiation) {
|
|
1627
|
+
json(Json {
|
|
1628
|
+
ignoreUnknownKeys = true
|
|
1629
|
+
})
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
override suspend fun getUsers(): Result<List<User>> {
|
|
1634
|
+
return try {
|
|
1635
|
+
val response = httpClient.get("https://api.example.com/users")
|
|
1636
|
+
val users = response.body<List<User>>()
|
|
1637
|
+
Result.success(users)
|
|
1638
|
+
} catch (e: Exception) {
|
|
1639
|
+
Result.failure(e)
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
override suspend fun getUserById(id: String): Result<User> {
|
|
1644
|
+
// Implementation
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
override suspend fun createUser(user: User): Result<User> {
|
|
1648
|
+
// Implementation
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
```
|
|
90
1652
|
|
|
91
|
-
|
|
1653
|
+
---
|
|
92
1654
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
1655
|
+
**Created by**: MoAI Language Skill Factory
|
|
1656
|
+
**Last Updated**: 2025-11-06
|
|
1657
|
+
**Version**: 2.0.0
|
|
1658
|
+
**Kotlin Target**: 2.0.20 with modern coroutines, KMP, and Jetpack Compose
|
|
96
1659
|
|
|
97
|
-
|
|
98
|
-
- Enable automatic validation by matching your linter with the language's official style guide.
|
|
99
|
-
- Fix test/build pipelines with reproducible commands in CI.
|
|
1660
|
+
This skill provides comprehensive Kotlin development guidance with 2025 best practices, covering everything from Android development with Compose to backend services with Spring Boot and cross-platform KMP development.
|