rapidkit 0.41.0 → 0.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +88 -11
  2. package/contracts/extension-cli-compatibility.v1.json +1 -1
  3. package/contracts/runtime-command-surface.v1.json +3 -1
  4. package/contracts/workspace-intelligence/agent-action-outcome.v1.json +17 -1
  5. package/contracts/workspace-intelligence/studio-blocker-handoff.v1.json +24 -0
  6. package/contracts/workspace-intelligence/workspace-dependency-graph.v1.json +61 -1
  7. package/dist/analyze-QYHMGLSG.js +1 -0
  8. package/dist/autopilot-release-YDEUKRW6.js +1 -0
  9. package/dist/{chunk-WA6JYVJM.js → chunk-33LR2QEM.js} +1 -1
  10. package/dist/{chunk-DMUEGR36.js → chunk-3PTJID76.js} +1 -1
  11. package/dist/chunk-46AGNYI7.js +50 -0
  12. package/dist/chunk-AQ4XZZC6.js +1 -0
  13. package/dist/{chunk-MGUJWRZA.js → chunk-BFEBZABL.js} +3 -3
  14. package/dist/chunk-DWXRVGOY.js +2 -0
  15. package/dist/chunk-E5ZVQL3C.js +13 -0
  16. package/dist/chunk-ELU3G6DQ.js +9 -0
  17. package/dist/chunk-EN6YCX36.js +1 -0
  18. package/dist/chunk-FMBSON6H.js +33 -0
  19. package/dist/chunk-GBJBQ43T.js +1 -0
  20. package/dist/chunk-HYAT2EG7.js +1 -0
  21. package/dist/{chunk-FPUNOIAR.js → chunk-ITJ6RKUW.js} +3 -3
  22. package/dist/{chunk-Y2SCTWL4.js → chunk-JNXT6KJV.js} +2 -2
  23. package/dist/{chunk-XOVB2ZP5.js → chunk-JU3VNLTY.js} +1 -1
  24. package/dist/{chunk-44GSDNPQ.js → chunk-KIUSCFHF.js} +1 -1
  25. package/dist/{chunk-KTQZUWAM.js → chunk-RSYUNEH7.js} +13 -13
  26. package/dist/chunk-VDTAPIHB.js +2 -0
  27. package/dist/chunk-WCICO7ZB.js +13 -0
  28. package/dist/chunk-WRMCPKGA.js +1 -0
  29. package/dist/{chunk-73IS6RIM.js → chunk-ZPXMZCYG.js} +1 -1
  30. package/dist/{create-UGXMC4CT.js → create-RNP5ACQL.js} +1 -1
  31. package/dist/demo-kit-N5U3NGAE.js +149 -0
  32. package/dist/{doctor-LCKG5S76.js → doctor-XM6QDTDC.js} +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +118 -117
  35. package/dist/{pipeline-MKNYPNGD.js → pipeline-YD2DO7XY.js} +1 -1
  36. package/dist/platform-capabilities-TSLK667K.js +1 -0
  37. package/dist/{pythonRapidkitExec-MNWRC4F2.js → pythonRapidkitExec-SGKW76XM.js} +1 -1
  38. package/dist/{workspace-FDMJD5XI.js → workspace-E554C5SM.js} +1 -1
  39. package/dist/{workspace-agent-sync-WJIZCZX5.js → workspace-agent-sync-BHE2V4KS.js} +1 -1
  40. package/dist/{workspace-context-RYOQYGOP.js → workspace-context-VJTXW3K4.js} +1 -1
  41. package/dist/{workspace-contract-ITFCJCHI.js → workspace-contract-OO4GMENV.js} +1 -1
  42. package/dist/workspace-explain-XJ22ZXJY.js +1 -0
  43. package/dist/workspace-explain-contract-24RQ7KIW.js +1 -0
  44. package/dist/{workspace-foundation-SILFUKL5.js → workspace-foundation-LISDH53T.js} +1 -1
  45. package/dist/{workspace-intelligence-YOZQBAK5.js → workspace-intelligence-EC63CRVF.js} +1 -1
  46. package/dist/workspace-mcp-serve-KOM2V35Q.js +3 -0
  47. package/dist/{workspace-model-VMMLHJWI.js → workspace-model-YL7W3573.js} +1 -1
  48. package/dist/workspace-registry-summary-X5WRUU3T.js +1 -0
  49. package/dist/workspace-run-F5FADRY5.js +1 -0
  50. package/dist/workspace-verify-IAUHP6Y6.js +1 -0
  51. package/dist/{workspace-watch-3MEZRSEE.js → workspace-watch-H2AETGFI.js} +1 -1
  52. package/docs/DEVELOPMENT.md +1 -1
  53. package/docs/OPEN_SOURCE_USER_SCENARIOS.md +1 -1
  54. package/docs/README.md +1 -1
  55. package/docs/commands-reference.md +10 -2
  56. package/docs/contracts/ARTIFACT_CATALOG.md +1 -1
  57. package/docs/contracts/README.md +11 -0
  58. package/package.json +7 -3
  59. package/scripts/enterprise-package-smoke.mjs +433 -0
  60. package/scripts/prepack-enterprise.mjs +40 -0
  61. package/templates/generator.js +175 -0
  62. package/templates/kits/fastapi-ddd/README.md.j2 +122 -0
  63. package/templates/kits/fastapi-ddd/common/env.example.j2 +10 -0
  64. package/templates/kits/fastapi-ddd/env.example.j2 +1 -0
  65. package/templates/kits/fastapi-ddd/pyproject.toml.j2 +64 -0
  66. package/templates/kits/fastapi-ddd/src/__init__.py.j2 +3 -0
  67. package/templates/kits/fastapi-ddd/src/app/__init__.py.j2 +11 -0
  68. package/templates/kits/fastapi-ddd/src/app/application/__init__.py.j2 +5 -0
  69. package/templates/kits/fastapi-ddd/src/app/application/interfaces.py.j2 +43 -0
  70. package/templates/kits/fastapi-ddd/src/app/application/use_cases/__init__.py.j2 +6 -0
  71. package/templates/kits/fastapi-ddd/src/app/application/use_cases/health.py.j2 +14 -0
  72. package/templates/kits/fastapi-ddd/src/app/application/use_cases/notes.py.j2 +24 -0
  73. package/templates/kits/fastapi-ddd/src/app/config/__init__.py.j2 +16 -0
  74. package/templates/kits/fastapi-ddd/src/app/domain/__init__.py.j2 +3 -0
  75. package/templates/kits/fastapi-ddd/src/app/domain/models/__init__.py.j2 +6 -0
  76. package/templates/kits/fastapi-ddd/src/app/domain/models/health.py.j2 +16 -0
  77. package/templates/kits/fastapi-ddd/src/app/domain/models/note.py.j2 +27 -0
  78. package/templates/kits/fastapi-ddd/src/app/infrastructure/__init__.py.j2 +5 -0
  79. package/templates/kits/fastapi-ddd/src/app/infrastructure/repositories/__init__.py.j2 +6 -0
  80. package/templates/kits/fastapi-ddd/src/app/infrastructure/repositories/health.py.j2 +17 -0
  81. package/templates/kits/fastapi-ddd/src/app/infrastructure/repositories/notes.py.j2 +28 -0
  82. package/templates/kits/fastapi-ddd/src/app/main.py.j2 +61 -0
  83. package/templates/kits/fastapi-ddd/src/app/presentation/__init__.py.j2 +3 -0
  84. package/templates/kits/fastapi-ddd/src/app/presentation/api/__init__.py.j2 +5 -0
  85. package/templates/kits/fastapi-ddd/src/app/presentation/api/dependencies/__init__.py.j2 +19 -0
  86. package/templates/kits/fastapi-ddd/src/app/presentation/api/router.py.j2 +10 -0
  87. package/templates/kits/fastapi-ddd/src/app/presentation/api/routes/__init__.py.j2 +5 -0
  88. package/templates/kits/fastapi-ddd/src/app/presentation/api/routes/health.py.j2 +27 -0
  89. package/templates/kits/fastapi-ddd/src/app/presentation/api/routes/notes.py.j2 +50 -0
  90. package/templates/kits/fastapi-ddd/src/app/shared/__init__.py.j2 +5 -0
  91. package/templates/kits/fastapi-ddd/src/app/shared/result.py.j2 +28 -0
  92. package/templates/kits/fastapi-ddd/src/cli.py.j2 +167 -0
  93. package/templates/kits/fastapi-ddd/src/main.py.j2 +35 -0
  94. package/templates/kits/fastapi-ddd/src/modules/__init__.py.j2 +3 -0
  95. package/templates/kits/fastapi-ddd/src/routing/__init__.py.j2 +13 -0
  96. package/templates/kits/fastapi-ddd/src/routing/health.py.j2 +7 -0
  97. package/templates/kits/fastapi-ddd/src/routing/notes.py.j2 +7 -0
  98. package/templates/kits/fastapi-ddd/tests/__init__.py.j2 +1 -0
  99. package/templates/kits/fastapi-ddd/tests/test_app_factory.py.j2 +22 -0
  100. package/templates/kits/fastapi-ddd/tests/test_health.py.j2 +17 -0
  101. package/templates/kits/fastapi-ddd/tests/test_notes.py.j2 +27 -0
  102. package/templates/kits/fastapi-standard/README.md.j2 +145 -0
  103. package/templates/kits/fastapi-standard/common/env.example.j2 +10 -0
  104. package/templates/kits/fastapi-standard/env.example.j2 +1 -0
  105. package/templates/kits/fastapi-standard/pyproject.toml.j2 +64 -0
  106. package/templates/kits/fastapi-standard/src/__init__.py.j2 +3 -0
  107. package/templates/kits/fastapi-standard/src/cli.py.j2 +168 -0
  108. package/templates/kits/fastapi-standard/src/main.py.j2 +66 -0
  109. package/templates/kits/fastapi-standard/src/modules/__init__.py.j2 +3 -0
  110. package/templates/kits/fastapi-standard/src/routing/__init__.py.j2 +16 -0
  111. package/templates/kits/fastapi-standard/src/routing/examples.py.j2 +71 -0
  112. package/templates/kits/fastapi-standard/src/routing/health.py.j2 +22 -0
  113. package/templates/kits/fastapi-standard/tests/__init__.py.j2 +1 -0
  114. package/templates/kits/fastapi-standard/tests/test_examples.py.j2 +29 -0
  115. package/templates/kits/fastapi-standard/tests/test_health.py.j2 +17 -0
  116. package/templates/kits/nestjs-standard/Dockerfile.j2 +41 -0
  117. package/templates/kits/nestjs-standard/README.md.j2 +139 -0
  118. package/templates/kits/nestjs-standard/docker-compose.yml.j2 +94 -0
  119. package/templates/kits/nestjs-standard/docs/README.md.j2 +15 -0
  120. package/templates/kits/nestjs-standard/env.example.j2 +18 -0
  121. package/templates/kits/nestjs-standard/eslint.config.cjs.j2 +9 -0
  122. package/templates/kits/nestjs-standard/jest.config.ts.j2 +22 -0
  123. package/templates/kits/nestjs-standard/nest-cli.json.j2 +10 -0
  124. package/templates/kits/nestjs-standard/package.json.j2 +101 -0
  125. package/templates/kits/nestjs-standard/src/app.controller.ts.j2 +14 -0
  126. package/templates/kits/nestjs-standard/src/app.module.ts.j2 +26 -0
  127. package/templates/kits/nestjs-standard/src/app.service.ts.j2 +16 -0
  128. package/templates/kits/nestjs-standard/src/auth/auth.controller.ts.j2 +20 -0
  129. package/templates/kits/nestjs-standard/src/auth/auth.module.ts.j2 +13 -0
  130. package/templates/kits/nestjs-standard/src/auth/auth.service.ts.j2 +6 -0
  131. package/templates/kits/nestjs-standard/src/auth/entities/token.entity.ts.j2 +3 -0
  132. package/templates/kits/nestjs-standard/src/auth/entities/user.entity.ts.j2 +3 -0
  133. package/templates/kits/nestjs-standard/src/auth/entities/webauthn.entity.ts.j2 +3 -0
  134. package/templates/kits/nestjs-standard/src/config/configuration.ts.j2 +85 -0
  135. package/templates/kits/nestjs-standard/src/config/index.ts.j2 +2 -0
  136. package/templates/kits/nestjs-standard/src/config/validation.ts.j2 +21 -0
  137. package/templates/kits/nestjs-standard/src/examples/dto/create-note.dto.ts.j2 +11 -0
  138. package/templates/kits/nestjs-standard/src/examples/examples.controller.ts.j2 +24 -0
  139. package/templates/kits/nestjs-standard/src/examples/examples.module.ts.j2 +10 -0
  140. package/templates/kits/nestjs-standard/src/examples/examples.service.ts.j2 +33 -0
  141. package/templates/kits/nestjs-standard/src/main.ts.j2 +53 -0
  142. package/templates/kits/nestjs-standard/src/modules/index.ts.j2 +25 -0
  143. package/templates/kits/nestjs-standard/test/app.controller.spec.ts.j2 +24 -0
  144. package/templates/kits/nestjs-standard/test/app.e2e-spec.ts.j2 +60 -0
  145. package/templates/kits/nestjs-standard/test/examples.controller.spec.ts.j2 +28 -0
  146. package/templates/kits/nestjs-standard/test/jest-e2e.json.j2 +15 -0
  147. package/templates/kits/nestjs-standard/tsconfig.build.json.j2 +12 -0
  148. package/templates/kits/nestjs-standard/tsconfig.json.j2 +26 -0
  149. package/dist/analyze-JVMUCQ22.js +0 -1
  150. package/dist/autopilot-release-GM5ALPWO.js +0 -1
  151. package/dist/chunk-424B73UF.js +0 -1
  152. package/dist/chunk-56RL5OB6.js +0 -2
  153. package/dist/chunk-AO6PG3K2.js +0 -9
  154. package/dist/chunk-AT3EQ2S7.js +0 -2
  155. package/dist/chunk-FVCZGUVX.js +0 -1
  156. package/dist/chunk-GX7UU7LL.js +0 -33
  157. package/dist/chunk-P5ODFWB2.js +0 -13
  158. package/dist/chunk-QN2LPLHO.js +0 -1
  159. package/dist/chunk-RIZCWYRR.js +0 -1
  160. package/dist/chunk-YOQ2546V.js +0 -50
  161. package/dist/chunk-Z5LKRG57.js +0 -1
  162. package/dist/chunk-ZQRFVFKK.js +0 -13
  163. package/dist/demo-kit-2VI4H6OJ.js +0 -141
  164. package/dist/workspace-explain-VKSUKP3O.js +0 -1
  165. package/dist/workspace-explain-contract-CLHQ3XEH.js +0 -1
  166. package/dist/workspace-mcp-serve-OOLITFCK.js +0 -3
  167. package/dist/workspace-registry-summary-ZXGKL2NT.js +0 -1
  168. package/dist/workspace-run-IHB2TPMD.js +0 -1
  169. package/dist/workspace-verify-3CAKAZIL.js +0 -1
@@ -0,0 +1,122 @@
1
+ # {{ project_name }}
2
+
3
+ Domain-driven FastAPI service generated with [RapidKit](https://github.com/rapidkitlabs/rapidkit-core).
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ # Load the project-aware RapidKit CLI (adds .rapidkit/rapidkit to PATH)
9
+ source .rapidkit/activate
10
+
11
+ # Bootstrap dependencies (creates .venv + installs Poetry deps)
12
+ rapidkit init # use make init if you prefer a Make target
13
+
14
+ # Copy env templates and install hooks/tooling
15
+ ./bootstrap.sh
16
+
17
+ # Type-check, lint, and test
18
+ make lint
19
+ make typecheck
20
+ make test
21
+
22
+ # Run the service
23
+ make dev
24
+
25
+ # Use the RapidKit CLI helpers when you prefer auto-detected environments
26
+ rapidkit dev
27
+ rapidkit lint
28
+ rapidkit test
29
+
30
+ # Run supply-chain and dependency audits
31
+ make audit
32
+ ```
33
+
34
+ > Re-source `.rapidkit/activate` whenever you open a new shell so the project-local `rapidkit` launcher is always on your PATH.
35
+
36
+ > Re-run `rapidkit init` (or `make init`) when dependencies change. If you only need to refresh tooling/hooks, run `SKIP_INIT=1 make install` or rely on `./bootstrap.sh`.
37
+
38
+ > Lockfiles are generated automatically during scaffolding. Set `RAPIDKIT_SKIP_LOCKS=1` (or `RAPIDKIT_GENERATE_LOCKS=0`) before running `rapidkit create` if you want to skip the lockfile attempt.
39
+
40
+ > Need to see every RapidKit CLI command? Run `rapidkit --help` or check the CLI reference in the docs for the full catalog.
41
+
42
+ ## Project structure
43
+
44
+ ```
45
+ src/
46
+ app/
47
+ application/ # Use cases and service contracts
48
+ domain/ # Aggregates, entities, and value objects
49
+ infrastructure/ # Adapters and repositories
50
+ presentation/ # API routers and dependencies
51
+ shared/ # Cross-cutting primitives (Result, types, etc.)
52
+ routing/ # Glue for module-provided routers
53
+ modules/ # RapidKit module bootstrapping
54
+ ```
55
+
56
+ ## Layer overview
57
+
58
+ - **Domain** – Pure business rules (`src/app/domain`).
59
+ - **Application** – Orchestrates use-cases and enforces boundaries (`src/app/application`).
60
+ - **Infrastructure** – Implements gateways and repositories (`src/app/infrastructure`).
61
+ - **Presentation** – Exposes the API using FastAPI (`src/app/presentation`).
62
+
63
+ Modules added via `rapidkit add module ...` integrate automatically using the preserved
64
+ injection anchors inside `src/main.py` and `src/routing/__init__.py`.
65
+
66
+ ## Example feature
67
+
68
+ An in-memory notes module is included under `/api/examples/notes` to demonstrate how the domain, application, infrastructure, and presentation layers collaborate:
69
+
70
+ ```bash
71
+ # Create a note
72
+ curl -s -X POST http://localhost:8000/api/examples/notes \
73
+ -H "Content-Type: application/json" \
74
+ -d '{"title":"design","body":"Describe your bounded context here."}'
75
+
76
+ # Retrieve the newly created note
77
+ curl -s http://localhost:8000/api/examples/notes/1 | jq
78
+ ```
79
+
80
+ Swap the in-memory repository with a RapidKit database module when you are ready for persistence.
81
+
82
+ ## Deployment
83
+
84
+ - `Dockerfile` packages the service for container hosting with production-ready defaults.
85
+ - `docker-compose.yml` provisions the FastAPI app plus dependencies (for example PostgreSQL) for local production parity.
86
+ - Run `docker compose up --build` to start the stack; copy `.env.example` to `.env` to configure credentials before deploying.
87
+ - CI/CD workflows under `.github/workflows/` demonstrate automated deployment pipelines.
88
+
89
+ ## Testing
90
+
91
+ ```bash
92
+ rapidkit test
93
+ ```
94
+
95
+ The scaffold ships with a smoke test (`tests/test_app_factory.py`) that validates the
96
+ health endpoint and the application factory.
97
+
98
+ ---
99
+
100
+ ## Local development
101
+
102
+ - `rapidkit init` bootstraps dependencies via the project-local CLI (run it after sourcing `.rapidkit/activate`).
103
+ - `make init` is an optional alias for `rapidkit init` when you prefer Make targets.
104
+ - `make install` re-runs `rapidkit init` unless `SKIP_INIT=1` is set and refreshes developer tooling such as pre-commit hooks.
105
+ - `./bootstrap.sh` copies `.env.example` to `.env` (when missing) and runs `SKIP_INIT=1 make install` for you.
106
+ - `make dev` or `rapidkit dev` starts the FastAPI server with hot-reload and ensures environment variables from `.env` are loaded.
107
+ - `make lint`, `make typecheck`, and `make test` (or `rapidkit lint` / `rapidkit test`) are thin wrappers around Ruff, mypy, and pytest for consistent CI parity.
108
+ - `make audit` runs `pip-audit` and `npm audit` to surface vulnerable dependencies early.
109
+
110
+ Pre-commit is configured with Ruff, Black, mypy, and hygiene hooks; `./bootstrap.sh` installs them automatically (or run `SKIP_INIT=1 make install`). Use the RapidKit CLI variants when you need it to auto-discover the right virtual environment inside automation scripts.
111
+
112
+ <!-- <<<inject:module-snippet>>> -->
113
+
114
+ ## 📄 License
115
+
116
+ This project is licensed under the **{{ license }}** License - see the `LICENSE` file included at the project root for details.
117
+
118
+ ## 🔒 Security & secrets
119
+
120
+ - Copy `.env.example` to `.env` and populate secrets (`SECRET_KEY`, DB credentials, etc.) before deploying.
121
+ - **Do not** commit real secrets or `.env` to git; `.gitignore` already contains `.env` to protect accidental commits.
122
+ - For production, tighten CORS and allowed hosts rather than using wildcard settings provided for convenience in dev.
@@ -0,0 +1,10 @@
1
+ # RapidKit environment defaults
2
+ PROJECT_NAME="{{ project_name | default('rapidkit-app') }}"
3
+ ENV="development"
4
+ DEBUG="true"
5
+ APP_HOST="0.0.0.0"
6
+ APP_PORT="8000"
7
+ LOG_LEVEL="info"
8
+
9
+ # Module-specific environment variables
10
+ # <<<inject:module-env>>>
@@ -0,0 +1 @@
1
+ {% include 'common/env.example.j2' %}
@@ -0,0 +1,64 @@
1
+ [tool.poetry]
2
+ name = "{{ project_name }}"
3
+ version = "{{ app_version }}"
4
+ description = "{{ description }}"
5
+ authors = ["{{ author }} <you@example.com>"]
6
+ license = "{{ license }}"
7
+ packages = [{ include = "src" }]
8
+
9
+ [tool.poetry.scripts]
10
+ dev = "src.cli:dev"
11
+ start = "src.cli:start"
12
+ build = "src.cli:build"
13
+ test = "src.cli:test"
14
+ lint = "src.cli:lint"
15
+ format = "src.cli:format"
16
+ help = "src.cli:help_cmd"
17
+
18
+ [tool.poetry.dependencies]
19
+ python = "^{{ python_version }}"
20
+ fastapi = "^0.128.0"
21
+ uvicorn = { extras = ["standard"], version = "^0.40.0" }
22
+ pydantic = "^2.12.5"
23
+ {% if has_settings -%}
24
+ pydantic-settings = { version = "^2.12.0", extras = ["yaml"] }
25
+ {% endif %}
26
+ # <<<inject:poetry-dependencies>>>
27
+
28
+ [tool.poetry.group.dev.dependencies]
29
+ pytest = "^9.0.2"
30
+ pytest-asyncio = "^1.3.0"
31
+ pytest-cov = "^7.0.0"
32
+ httpx = "^0.28.1"
33
+ ruff = "^0.14.10"
34
+ black = "^25.12.0"
35
+ isort = "^7.0.0"
36
+ mypy = "^1.19.1"
37
+ build = "^1.3.0"
38
+
39
+ # <<<inject:poetry-dev-dependencies>>>
40
+
41
+ [build-system]
42
+ requires = ["poetry-core"]
43
+ build-backend = "poetry.core.masonry.api"
44
+
45
+ [tool.pytest.ini_options]
46
+ markers = [
47
+ "integration: mark tests as integration tests",
48
+ "template_integration: mark template integration tests",
49
+ ]
50
+
51
+ [tool.ruff]
52
+ line-length = 100
53
+ exclude = [".rapidkit/vendor"]
54
+
55
+ [tool.ruff.lint]
56
+ ignore = ["E501"]
57
+
58
+ [tool.mypy]
59
+ python_version = "{{ python_version }}"
60
+ ignore_missing_imports = true
61
+ warn_unused_configs = false
62
+ show_error_codes = true
63
+ # Ignore vendor snapshots and generated health shims from strict static checking
64
+ exclude = "(^\\.rapidkit/vendor/|src/health/|src/main.py$|src/app/)"
@@ -0,0 +1,3 @@
1
+ """{{ project_name }} package."""
2
+
3
+ __all__ = ["app", "main"]
@@ -0,0 +1,11 @@
1
+ """Domain-driven application layers for {{ project_name }}."""
2
+
3
+ __all__ = [
4
+ "main",
5
+ "config",
6
+ "application",
7
+ "domain",
8
+ "infrastructure",
9
+ "presentation",
10
+ "shared",
11
+ ]
@@ -0,0 +1,5 @@
1
+ """Application layer orchestrating use cases and service contracts."""
2
+
3
+ from .interfaces import HealthStatusProvider, NoteRepository, ServiceContext
4
+
5
+ __all__ = ["HealthStatusProvider", "NoteRepository", "ServiceContext"]
@@ -0,0 +1,43 @@
1
+ """Contracts between the application layer and infrastructure adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Protocol
7
+
8
+ from src.app.domain.models import HealthStatus, Note, NoteDraft
9
+
10
+
11
+ class HealthStatusProvider(Protocol):
12
+ """Protocol describing how to obtain the current health status."""
13
+
14
+ def get_status(self) -> HealthStatus:
15
+ """Return the latest domain health status snapshot."""
16
+ ...
17
+
18
+
19
+ class NoteRepository(Protocol):
20
+ """Protocol for managing example notes."""
21
+
22
+ def create(self, draft: NoteDraft) -> Note:
23
+ """Persist a note draft and return the resulting entity."""
24
+ ...
25
+
26
+ def list(self) -> list[Note]:
27
+ """Return all available notes."""
28
+ ...
29
+
30
+ def get(self, note_id: int) -> Note | None:
31
+ """Return a single note or None if it does not exist."""
32
+ ...
33
+
34
+
35
+ @dataclass(slots=True)
36
+ class ServiceContext:
37
+ """Aggregates infrastructure adapters used by application use-cases."""
38
+
39
+ health_provider: HealthStatusProvider
40
+ note_repository: NoteRepository
41
+
42
+
43
+ __all__ = ["HealthStatusProvider", "NoteRepository", "ServiceContext"]
@@ -0,0 +1,6 @@
1
+ """Application use-cases exposed to delivery mechanisms."""
2
+
3
+ from .health import get_service_health
4
+ from .notes import create_note, get_note, list_notes
5
+
6
+ __all__ = ["get_service_health", "create_note", "get_note", "list_notes"]
@@ -0,0 +1,14 @@
1
+ """Example use-case exposing service health information."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from src.app.application.interfaces import ServiceContext
6
+ from src.app.domain.models import HealthStatus
7
+
8
+
9
+ def get_service_health(context: ServiceContext) -> dict[str, str]:
10
+ """Retrieve an immutable health snapshot for presentation layers."""
11
+ status = context.health_provider.get_status()
12
+ if isinstance(status, HealthStatus):
13
+ return status.to_dict()
14
+ return {"status": str(status)}
@@ -0,0 +1,24 @@
1
+ """Application use-cases orchestrating the example notes feature."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from src.app.application.interfaces import ServiceContext
6
+ from src.app.domain.models import Note, NoteDraft
7
+
8
+
9
+ def list_notes(context: ServiceContext) -> list[Note]:
10
+ """Return every note currently stored."""
11
+
12
+ return context.note_repository.list()
13
+
14
+
15
+ def create_note(context: ServiceContext, draft: NoteDraft) -> Note:
16
+ """Create a new note."""
17
+
18
+ return context.note_repository.create(draft)
19
+
20
+
21
+ def get_note(context: ServiceContext, note_id: int) -> Note | None:
22
+ """Retrieve a single note by identifier."""
23
+
24
+ return context.note_repository.get(note_id)
@@ -0,0 +1,16 @@
1
+ """Configuration primitives for the application layer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class AppConfig:
10
+ """Application-level configuration aggregated from RapidKit settings."""
11
+
12
+ environment: str = "development"
13
+ service_name: str = "{{ project_name }}"
14
+
15
+
16
+ __all__ = ["AppConfig"]
@@ -0,0 +1,3 @@
1
+ """Domain layer primitives for {{ project_name }}."""
2
+
3
+ __all__ = ["models"]
@@ -0,0 +1,6 @@
1
+ """Domain models and value objects."""
2
+
3
+ from .health import HealthStatus
4
+ from .note import Note, NoteDraft
5
+
6
+ __all__ = ["HealthStatus", "Note", "NoteDraft"]
@@ -0,0 +1,16 @@
1
+ """Domain entity representing system health."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class HealthStatus:
10
+ """Immutable representation of the service health state."""
11
+
12
+ status: str = "ok"
13
+
14
+ def to_dict(self) -> dict[str, str]:
15
+ """Serialize the status for transport layers."""
16
+ return {"status": self.status}
@@ -0,0 +1,27 @@
1
+ """Domain entity and value object for example notes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class NoteDraft:
10
+ """Value object describing the data needed to create a note."""
11
+
12
+ title: str
13
+ body: str
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class Note:
18
+ """Domain entity representing an immutable note."""
19
+
20
+ id: int
21
+ title: str
22
+ body: str
23
+
24
+ def to_dict(self) -> dict[str, str | int]:
25
+ """Serialize the note for presentation layers."""
26
+
27
+ return {"id": self.id, "title": self.title, "body": self.body}
@@ -0,0 +1,5 @@
1
+ """Infrastructure adapters for the application layer."""
2
+
3
+ from .repositories import StaticHealthRepository
4
+
5
+ __all__ = ["StaticHealthRepository"]
@@ -0,0 +1,6 @@
1
+ """Repository implementations bridging the domain and infrastructure."""
2
+
3
+ from .health import StaticHealthRepository
4
+ from .notes import InMemoryNoteRepository
5
+
6
+ __all__ = ["StaticHealthRepository", "InMemoryNoteRepository"]
@@ -0,0 +1,17 @@
1
+ """Infrastructure repository providing health status data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from src.app.domain.models import HealthStatus
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class StaticHealthRepository:
12
+ """Simple repository returning a constant health status."""
13
+
14
+ status: str = "ok"
15
+
16
+ def get_status(self) -> HealthStatus:
17
+ return HealthStatus(status=self.status)
@@ -0,0 +1,28 @@
1
+ """In-memory repository backing the example notes use-case."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Dict, List
7
+
8
+ from src.app.domain.models import Note, NoteDraft
9
+
10
+
11
+ @dataclass(slots=True)
12
+ class InMemoryNoteRepository:
13
+ """Very small repository storing notes in process memory."""
14
+
15
+ _store: Dict[int, Note] = field(default_factory=dict)
16
+ _next_id: int = 1
17
+
18
+ def create(self, draft: NoteDraft) -> Note:
19
+ note = Note(id=self._next_id, title=draft.title, body=draft.body)
20
+ self._store[self._next_id] = note
21
+ self._next_id += 1
22
+ return note
23
+
24
+ def list(self) -> List[Note]:
25
+ return list(self._store.values())
26
+
27
+ def get(self, note_id: int) -> Note | None:
28
+ return self._store.get(note_id)
@@ -0,0 +1,61 @@
1
+ """Application factory wiring the FastAPI instance for DDD layers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator, Callable
6
+
7
+ from fastapi import FastAPI
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+
10
+ from src.routing import api_router as root_api_router
11
+
12
+ try:
13
+ # canonical health package
14
+ from src.health import register_health_routes as _core_register_health_routes
15
+ except ImportError: # pragma: no cover - health package not generated yet
16
+
17
+ def _register_health_routes(_: FastAPI) -> None:
18
+ return None
19
+
20
+ else:
21
+
22
+ def _register_health_routes(app: FastAPI) -> None:
23
+ try:
24
+ _core_register_health_routes(app)
25
+ except Exception: # pragma: no cover - defensive best-effort registration
26
+ return None
27
+
28
+
29
+ LifespanHandler = Callable[[FastAPI], AsyncIterator[None]]
30
+
31
+
32
+ def create_app(
33
+ *,
34
+ title: str,
35
+ description: str,
36
+ version: str,
37
+ lifespan: LifespanHandler | None = None,
38
+ ) -> FastAPI:
39
+ """Build the FastAPI application configured with shared middlewares and routers."""
40
+
41
+ app = FastAPI(
42
+ title=title,
43
+ description=description,
44
+ version=version,
45
+ docs_url="/docs",
46
+ redoc_url="/redoc",
47
+ lifespan=lifespan,
48
+ )
49
+
50
+ app.add_middleware(
51
+ CORSMiddleware,
52
+ allow_origins=["*"],
53
+ allow_credentials=True,
54
+ allow_methods=["*"],
55
+ allow_headers=["*"],
56
+ )
57
+
58
+ app.include_router(root_api_router, prefix="/api")
59
+ _register_health_routes(app)
60
+
61
+ return app
@@ -0,0 +1,3 @@
1
+ """Presentation layer packages (HTTP, CLI, messaging, ...)."""
2
+
3
+ __all__ = ["api"]
@@ -0,0 +1,5 @@
1
+ """HTTP-facing presentation layer."""
2
+
3
+ from .router import get_api_router
4
+
5
+ __all__ = ["get_api_router"]
@@ -0,0 +1,19 @@
1
+ """FastAPI dependencies that wire infrastructure to application use-cases."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from functools import lru_cache
6
+
7
+ from src.app.application.interfaces import ServiceContext
8
+ from src.app.infrastructure.repositories import InMemoryNoteRepository, StaticHealthRepository
9
+
10
+
11
+ @lru_cache(maxsize=1)
12
+ def get_service_context() -> ServiceContext:
13
+ """Return a cached service context for request handlers."""
14
+ health_repository = StaticHealthRepository()
15
+ note_repository = InMemoryNoteRepository()
16
+ return ServiceContext(health_provider=health_repository, note_repository=note_repository)
17
+
18
+
19
+ __all__ = ["get_service_context"]
@@ -0,0 +1,10 @@
1
+ """Factory for the root API router."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter
6
+
7
+
8
+ def get_api_router() -> APIRouter:
9
+ """Return an API router ready for versioned sub-routers."""
10
+ return APIRouter()
@@ -0,0 +1,5 @@
1
+ """HTTP routes exposed by the presentation layer."""
2
+
3
+ from . import health, notes
4
+
5
+ __all__ = ["health", "notes"]
@@ -0,0 +1,27 @@
1
+ """Health endpoints bridging the DDD layers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from time import monotonic
6
+
7
+ from fastapi import APIRouter, Depends
8
+
9
+ from src.app.application.interfaces import ServiceContext
10
+ from src.app.application.use_cases import get_service_health
11
+ from src.app.presentation.api.dependencies import get_service_context
12
+
13
+ router = APIRouter(tags=["health"])
14
+
15
+ _START_TIME = monotonic()
16
+
17
+
18
+ @router.get("/", summary="Health check")
19
+ async def heartbeat(
20
+ context: ServiceContext = Depends(get_service_context),
21
+ ) -> dict[str, object]:
22
+ """Return the current service health decal."""
23
+ payload: dict[str, object] = dict(get_service_health(context))
24
+ payload.setdefault("version", "{{ app_version }}")
25
+ payload.setdefault("uptime", monotonic() - _START_TIME)
26
+ payload.setdefault("module", "service")
27
+ return payload
@@ -0,0 +1,50 @@
1
+ """HTTP handlers for the example notes feature."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter, Depends, HTTPException, status
6
+ from pydantic import BaseModel, Field
7
+
8
+ from src.app.application.interfaces import ServiceContext
9
+ from src.app.application.use_cases import create_note, get_note, list_notes
10
+ from src.app.domain.models import NoteDraft
11
+ from src.app.presentation.api.dependencies import get_service_context
12
+
13
+ router = APIRouter(tags=["examples"])
14
+
15
+
16
+ class NotePayload(BaseModel):
17
+ title: str = Field(..., max_length=80)
18
+ body: str = Field(..., max_length=500)
19
+
20
+
21
+ class NoteResponse(NotePayload):
22
+ id: int
23
+
24
+
25
+ @router.post("/notes", response_model=NoteResponse, status_code=status.HTTP_201_CREATED)
26
+ async def create_example_note(
27
+ payload: NotePayload,
28
+ context: ServiceContext = Depends(get_service_context),
29
+ ) -> NoteResponse:
30
+ note = create_note(context, NoteDraft(title=payload.title, body=payload.body))
31
+ return NoteResponse(**note.to_dict())
32
+
33
+
34
+ @router.get("/notes", response_model=list[NoteResponse])
35
+ async def list_example_notes(
36
+ context: ServiceContext = Depends(get_service_context),
37
+ ) -> list[NoteResponse]:
38
+ notes = list_notes(context)
39
+ return [NoteResponse(**note.to_dict()) for note in notes]
40
+
41
+
42
+ @router.get("/notes/{note_id}", response_model=NoteResponse)
43
+ async def get_example_note(
44
+ note_id: int,
45
+ context: ServiceContext = Depends(get_service_context),
46
+ ) -> NoteResponse:
47
+ note = get_note(context, note_id)
48
+ if not note:
49
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found")
50
+ return NoteResponse(**note.to_dict())
@@ -0,0 +1,5 @@
1
+ """Shared utilities across layers."""
2
+
3
+ from .result import Result
4
+
5
+ __all__ = ["Result"]