nene2-python 1.8.27__tar.gz → 1.8.28__tar.gz

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 (277) hide show
  1. {nene2_python-1.8.27 → nene2_python-1.8.28}/CHANGELOG.md +12 -0
  2. {nene2_python-1.8.27 → nene2_python-1.8.28}/PKG-INFO +1 -1
  3. nene2_python-1.8.28/docs/field-trials/2026-05-field-trial-82.md +198 -0
  4. nene2_python-1.8.28/docs/field-trials/2026-05-field-trial-83.md +223 -0
  5. {nene2_python-1.8.27 → nene2_python-1.8.28}/pyproject.toml +1 -1
  6. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/http/__init__.py +2 -1
  7. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/http/pagination.py +19 -1
  8. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/http/test_pagination.py +22 -1
  9. {nene2_python-1.8.27 → nene2_python-1.8.28}/uv.lock +1 -1
  10. {nene2_python-1.8.27 → nene2_python-1.8.28}/.env.example +0 -0
  11. {nene2_python-1.8.27 → nene2_python-1.8.28}/.github/workflows/ci.yml +0 -0
  12. {nene2_python-1.8.27 → nene2_python-1.8.28}/.github/workflows/docs.yml +0 -0
  13. {nene2_python-1.8.27 → nene2_python-1.8.28}/.github/workflows/publish.yml +0 -0
  14. {nene2_python-1.8.27 → nene2_python-1.8.28}/.gitignore +0 -0
  15. {nene2_python-1.8.27 → nene2_python-1.8.28}/.vitepress/config.mts +0 -0
  16. {nene2_python-1.8.27 → nene2_python-1.8.28}/.vitepress/theme/custom.css +0 -0
  17. {nene2_python-1.8.27 → nene2_python-1.8.28}/.vitepress/theme/index.ts +0 -0
  18. {nene2_python-1.8.27 → nene2_python-1.8.28}/AGENTS.md +0 -0
  19. {nene2_python-1.8.27 → nene2_python-1.8.28}/CLAUDE.md +0 -0
  20. {nene2_python-1.8.27 → nene2_python-1.8.28}/Dockerfile +0 -0
  21. {nene2_python-1.8.27 → nene2_python-1.8.28}/LICENSE +0 -0
  22. {nene2_python-1.8.27 → nene2_python-1.8.28}/README.md +0 -0
  23. {nene2_python-1.8.27 → nene2_python-1.8.28}/alembic/README +0 -0
  24. {nene2_python-1.8.27 → nene2_python-1.8.28}/alembic/env.py +0 -0
  25. {nene2_python-1.8.27 → nene2_python-1.8.28}/alembic/script.py.mako +0 -0
  26. {nene2_python-1.8.27 → nene2_python-1.8.28}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  27. {nene2_python-1.8.27 → nene2_python-1.8.28}/alembic.ini +0 -0
  28. {nene2_python-1.8.27 → nene2_python-1.8.28}/compose.yaml +0 -0
  29. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0001-toolchain.md +0 -0
  30. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0002-clean-architecture.md +0 -0
  31. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0003-security-first.md +0 -0
  32. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0004-ai-first-design.md +0 -0
  33. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0005-logging.md +0 -0
  34. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0006-rate-limiting.md +0 -0
  35. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0009-mcp-design.md +0 -0
  36. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0010-async-use-case.md +0 -0
  37. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  38. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/de/index.md +0 -0
  39. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/de/tutorials/getting-started.md +0 -0
  40. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/explanation/architecture.md +0 -0
  41. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/explanation/design-philosophy.md +0 -0
  42. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  43. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  44. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  45. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  46. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  47. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  48. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  49. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  50. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  51. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  52. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  53. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  54. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  55. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  56. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  57. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  58. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  59. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  60. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  61. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  62. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  63. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  64. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  65. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  66. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  67. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  68. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  69. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  70. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  71. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  72. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  73. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  74. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  75. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  76. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  77. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  78. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  79. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  80. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  81. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  82. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  83. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  84. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  85. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  86. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  87. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  88. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  89. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  90. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  91. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  92. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  93. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  94. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  95. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  96. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  97. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  98. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  99. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  100. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  101. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  102. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  103. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  104. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  105. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  106. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  107. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  108. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  109. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  110. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  111. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  112. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  113. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  114. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  115. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  116. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  117. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  118. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  119. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  120. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  121. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  122. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  123. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/fr/index.md +0 -0
  124. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/fr/tutorials/getting-started.md +0 -0
  125. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/add-new-domain.md +0 -0
  126. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/async-use-case.md +0 -0
  127. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/configure-auth.md +0 -0
  128. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/middleware-stack.md +0 -0
  129. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/new-project.md +0 -0
  130. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/problem-details.md +0 -0
  131. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/run-tests.md +0 -0
  132. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/sqlalchemy-repository.md +0 -0
  133. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/how-to/validation.md +0 -0
  134. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/howto/mcp-setup.md +0 -0
  135. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/index.md +0 -0
  136. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/explanation/architecture.md +0 -0
  137. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/explanation/design-philosophy.md +0 -0
  138. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/how-to/add-new-domain.md +0 -0
  139. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/how-to/configure-auth.md +0 -0
  140. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/how-to/new-project.md +0 -0
  141. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/how-to/run-tests.md +0 -0
  142. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  143. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/howto/mcp-setup.md +0 -0
  144. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/index.md +0 -0
  145. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/reference/api.md +0 -0
  146. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/reference/configuration.md +0 -0
  147. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/reference/framework-modules.md +0 -0
  148. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/tutorials/first-domain.md +0 -0
  149. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/ja/tutorials/getting-started.md +0 -0
  150. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/pt-br/index.md +0 -0
  151. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/pt-br/tutorials/getting-started.md +0 -0
  152. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/reference/api.md +0 -0
  153. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/reference/configuration.md +0 -0
  154. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/reference/framework-modules.md +0 -0
  155. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/roadmap.md +0 -0
  156. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/todo/current.md +0 -0
  157. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/tutorials/first-domain.md +0 -0
  158. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/tutorials/getting-started.md +0 -0
  159. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/zh/index.md +0 -0
  160. {nene2_python-1.8.27 → nene2_python-1.8.28}/docs/zh/tutorials/getting-started.md +0 -0
  161. {nene2_python-1.8.27 → nene2_python-1.8.28}/package-lock.json +0 -0
  162. {nene2_python-1.8.27 → nene2_python-1.8.28}/package.json +0 -0
  163. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/__init__.py +0 -0
  164. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/__main__.py +0 -0
  165. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/app.py +0 -0
  166. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/__init__.py +0 -0
  167. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/entity.py +0 -0
  168. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/exceptions.py +0 -0
  169. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/handler.py +0 -0
  170. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/repository.py +0 -0
  171. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/sqlalchemy_repository.py +0 -0
  172. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/comment/use_case.py +0 -0
  173. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/mcp.py +0 -0
  174. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/__init__.py +0 -0
  175. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/async_use_case.py +0 -0
  176. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/entity.py +0 -0
  177. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/exceptions.py +0 -0
  178. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/handler.py +0 -0
  179. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/repository.py +0 -0
  180. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/sqlalchemy_repository.py +0 -0
  181. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/note/use_case.py +0 -0
  182. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/schema.py +0 -0
  183. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/__init__.py +0 -0
  184. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/entity.py +0 -0
  185. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/exceptions.py +0 -0
  186. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/handler.py +0 -0
  187. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/repository.py +0 -0
  188. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/sqlalchemy_repository.py +0 -0
  189. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/example/tag/use_case.py +0 -0
  190. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/__init__.py +0 -0
  191. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/auth/__init__.py +0 -0
  192. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/auth/api_key.py +0 -0
  193. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/auth/bearer_token.py +0 -0
  194. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/auth/exceptions.py +0 -0
  195. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/auth/interfaces.py +0 -0
  196. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/auth/local_verifier.py +0 -0
  197. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/config/__init__.py +0 -0
  198. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/config/settings.py +0 -0
  199. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/database/__init__.py +0 -0
  200. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/database/exceptions.py +0 -0
  201. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/database/health.py +0 -0
  202. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/database/interfaces.py +0 -0
  203. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/database/sqlalchemy_executor.py +0 -0
  204. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/database/utils.py +0 -0
  205. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/http/health.py +0 -0
  206. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/http/problem_details.py +0 -0
  207. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/log/__init__.py +0 -0
  208. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/log/setup.py +0 -0
  209. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/mcp/__init__.py +0 -0
  210. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/mcp/http_client.py +0 -0
  211. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/mcp/server.py +0 -0
  212. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/__init__.py +0 -0
  213. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/domain_exception.py +0 -0
  214. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/error_handler.py +0 -0
  215. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/request_id.py +0 -0
  216. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/request_logging.py +0 -0
  217. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/request_size_limit.py +0 -0
  218. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/security_headers.py +0 -0
  219. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/setup.py +0 -0
  220. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/middleware/throttle.py +0 -0
  221. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/py.typed +0 -0
  222. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/use_case/__init__.py +0 -0
  223. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/use_case/protocols.py +0 -0
  224. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/validation/__init__.py +0 -0
  225. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/nene2/validation/exceptions.py +0 -0
  226. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/scripts/__init__.py +0 -0
  227. {nene2_python-1.8.27 → nene2_python-1.8.28}/src/scripts/export_openapi.py +0 -0
  228. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/__init__.py +0 -0
  229. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/__init__.py +0 -0
  230. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/comment/__init__.py +0 -0
  231. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/comment/test_comment_http.py +0 -0
  232. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/comment/test_comment_repository.py +0 -0
  233. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/comment/test_comment_use_case.py +0 -0
  234. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/conftest.py +0 -0
  235. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/note/__init__.py +0 -0
  236. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/note/test_async_note_use_case.py +0 -0
  237. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/note/test_list_notes.py +0 -0
  238. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/note/test_note_repository.py +0 -0
  239. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/tag/__init__.py +0 -0
  240. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/tag/test_tag_repository.py +0 -0
  241. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/tag/test_tags.py +0 -0
  242. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/test_cors.py +0 -0
  243. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/example/test_mcp.py +0 -0
  244. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/__init__.py +0 -0
  245. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/auth/__init__.py +0 -0
  246. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/auth/test_api_key.py +0 -0
  247. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/auth/test_bearer_token.py +0 -0
  248. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/auth/test_token_issuer.py +0 -0
  249. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/config/__init__.py +0 -0
  250. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/config/test_settings.py +0 -0
  251. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/database/__init__.py +0 -0
  252. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/database/test_transaction.py +0 -0
  253. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/database/test_utils.py +0 -0
  254. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/http/__init__.py +0 -0
  255. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/http/test_health.py +0 -0
  256. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/http/test_problem_details.py +0 -0
  257. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/log/__init__.py +0 -0
  258. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/log/test_setup.py +0 -0
  259. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/mcp/__init__.py +0 -0
  260. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/mcp/test_http_client.py +0 -0
  261. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/mcp/test_server.py +0 -0
  262. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/__init__.py +0 -0
  263. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_error_handler.py +0 -0
  264. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_request_id.py +0 -0
  265. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_request_logging.py +0 -0
  266. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  267. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_security_headers.py +0 -0
  268. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  269. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  270. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/middleware/test_throttle.py +0 -0
  271. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/use_case/__init__.py +0 -0
  272. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/use_case/test_protocols.py +0 -0
  273. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  274. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/validation/__init__.py +0 -0
  275. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/nene2/validation/test_exceptions.py +0 -0
  276. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/scripts/__init__.py +0 -0
  277. {nene2_python-1.8.27 → nene2_python-1.8.28}/tests/scripts/test_export_openapi.py +0 -0
@@ -5,6 +5,18 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [1.8.27] — 2026-05-20
9
+
10
+ FT81 フィールドトライアル — CORS 設定パターン検証と setup_middlewares() への CORS 統合。
11
+
12
+ ### Added
13
+ - `setup_middlewares()` に `cors_allowed_origins` / `cors_allow_credentials` / `cors_allow_methods` / `cors_allow_headers` パラメーターを追加 (#348) (FT81)
14
+ — `CORSMiddleware` を最外側に自動配置し、OPTIONS プリフライトが確実に処理される
15
+ — `cors_allowed_origins=["*"]` を渡すと `ValueError` を raise(wildcard 禁止ポリシーの実装強制)
16
+ - Field trial report: `docs/field-trials/2026-05-field-trial-81.md` (FT81)
17
+
18
+ ---
19
+
8
20
  ## [1.8.26] — 2026-05-20
9
21
 
10
22
  FT80 フィールドトライアル — LocalMcpServer + HttpxMcpClient MCP E2E 検証と list_tools() 追加。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.27
3
+ Version: 1.8.28
4
4
  Summary: NENE2 Python — minimal API framework following NENE2's design philosophy
5
5
  Project-URL: Homepage, https://github.com/hideyukiMORI/nene2-python
6
6
  Project-URL: Repository, https://github.com/hideyukiMORI/nene2-python
@@ -0,0 +1,198 @@
1
+ # FT82: Background Tasks — FastAPI BackgroundTasks と nene2 の組み合わせ
2
+
3
+ **日付**: 2026-05-20
4
+ **テーマ**: FastAPI BackgroundTasks を nene2 アーキテクチャと組み合わせた際の動作とパターン検証
5
+ **バージョン**: v1.8.27
6
+ **FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft82-background-tasks/`
7
+
8
+ ---
9
+
10
+ ## 概要
11
+
12
+ FastAPI の `BackgroundTasks` は「レスポンス返却後に処理を実行する」仕組みで、
13
+ メール送信・在庫更新・監査ログなどのユースケースに使う。
14
+ nene2 のミドルウェアスタック(`setup_middlewares()`)との共存、
15
+ 例外ハンドリングの限界、UseCase への注入パターンを検証した。
16
+
17
+ ---
18
+
19
+ ## 実装パターン
20
+
21
+ ### 基本パターン(ハンドラーで BackgroundTasks を受け取る)
22
+
23
+ ```python
24
+ from fastapi import BackgroundTasks
25
+
26
+ @app.post("/orders")
27
+ def create_order(body: OrderBody, background_tasks: BackgroundTasks) -> JSONResponse:
28
+ output = use_case.execute(CreateOrderInput(item=body.item, quantity=body.quantity))
29
+ # レスポンス送信後にバックグラウンドで実行される
30
+ background_tasks.add_task(send_order_confirmation, output.order_id, output.item)
31
+ background_tasks.add_task(update_inventory, output.item, output.quantity)
32
+ return JSONResponse({...}, status_code=201)
33
+ ```
34
+
35
+ ### UseCase を注入可能にするパターン(テスト用ファクトリー)
36
+
37
+ ```python
38
+ def create_app_with_use_case(
39
+ create_order_fn: Callable[[CreateOrderInput], CreateOrderOutput]
40
+ ) -> FastAPI:
41
+ factory_app = FastAPI()
42
+ setup_middlewares(factory_app)
43
+
44
+ @factory_app.post("/orders")
45
+ def _create_order(body: OrderBody, background_tasks: BackgroundTasks) -> JSONResponse:
46
+ output = create_order_fn(...)
47
+ background_tasks.add_task(send_order_confirmation, output.order_id, output.item)
48
+ return JSONResponse({...}, status_code=201)
49
+
50
+ return factory_app
51
+
52
+ # テストでの使用
53
+ injected_app = create_app_with_use_case(lambda _: mock_output)
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 発見した問題
59
+
60
+ ### 問題1: バックグラウンドタスクの例外がクライアントに見えない
61
+
62
+ ```python
63
+ # バックグラウンドタスクが例外を投げても...
64
+ def send_email(order_id: int) -> None:
65
+ raise SMTPConnectionError("Mail server down")
66
+
67
+ @app.post("/orders")
68
+ def create_order(body: OrderBody, background_tasks: BackgroundTasks) -> JSONResponse:
69
+ output = use_case.execute(...)
70
+ background_tasks.add_task(send_email, output.order_id)
71
+ return JSONResponse({...}, status_code=201) # ← 201 が返る
72
+
73
+ # クライアントには 201 が届く(メール失敗は見えない)
74
+ ```
75
+
76
+ FastAPI の設計上、レスポンスはすでに送信済みのため
77
+ バックグラウンドタスクのエラーを HTTP レスポンスコードで伝えられない。
78
+ エラーはサーバーログに記録されるが、クライアントには `201 Created` が届く。
79
+
80
+ ### 問題2: nene2 に BackgroundTasks 推奨パターンがない
81
+
82
+ ```python
83
+ # UseCase にどうやって BackgroundTasks を渡すか、ドキュメントがない
84
+
85
+ # ❌ UseCase の中で直接 BackgroundTasks を使う(アーキテクチャ違反)
86
+ class CreateOrderUseCase:
87
+ def execute(self, input_: CreateOrderInput, bg: BackgroundTasks) -> CreateOrderOutput:
88
+ ...
89
+ bg.add_task(send_email, ...) # UseCase が HTTP 境界に依存してしまう
90
+
91
+ # ✅ ハンドラー層で BackgroundTasks を使い、UseCase は純粋に保つ
92
+ @app.post("/orders")
93
+ def create_order(body: OrderBody, background_tasks: BackgroundTasks) -> JSONResponse:
94
+ output = use_case.execute(CreateOrderInput(...)) # UseCase はクリーン
95
+ background_tasks.add_task(send_email, output.order_id) # ハンドラー層で副作用
96
+ return JSONResponse({...})
97
+ ```
98
+
99
+ 正しいパターン(UseCase を純粋に保ちハンドラー層で BackgroundTasks を使う)が
100
+ nene2 のドキュメントに記載されていない。
101
+
102
+ ### 問題3: バックグラウンドタスクの実行確認ができない
103
+
104
+ ```python
105
+ # 「タスクをキューした」ことと「タスクが成功した」ことを HTTP で区別できない
106
+ r = client.post("/orders/1/alert")
107
+ # レスポンスは {"queued": True} — 成功したかどうかはわからない
108
+ ```
109
+
110
+ 本番運用では Celery・ARQ・FastAPI Scheduler などの外部ジョブキューが必要。
111
+ 「BackgroundTasks は軽量な one-shot 処理向け」という点がドキュメントに不足。
112
+
113
+ ---
114
+
115
+ ## テスト結果(全16件パス)
116
+
117
+ ```
118
+ test_create_order_returns_201 PASSED
119
+ test_get_order_returns_200 PASSED
120
+ test_get_nonexistent_order_returns_404 PASSED
121
+ test_background_tasks_execute_after_response PASSED # TestClient は同期実行
122
+ test_multiple_background_tasks_all_execute PASSED
123
+ test_background_task_with_alert PASSED
124
+ test_background_task_not_queued_on_404 PASSED # 404 ではタスク未実行
125
+ test_failing_background_task_does_not_affect_response PASSED # 例外 → 200 のまま
126
+ test_failing_background_task_started PASSED
127
+ test_request_id_present_with_background_tasks PASSED # nene2 と共存
128
+ test_security_headers_present_with_background_tasks PASSED # nene2 と共存
129
+ test_validation_error_does_not_queue_background_tasks PASSED # 422 では未実行
130
+ test_injectable_use_case_pattern PASSED # DI パターン動作
131
+ test_friction_background_exception_not_visible_to_client PASSED # 摩擦記録
132
+ test_friction_no_nene2_background_task_pattern PASSED # 摩擦記録
133
+ test_friction_background_task_cannot_return_result PASSED # 摩擦記録
134
+ ```
135
+
136
+ ---
137
+
138
+ ## 重要な発見: TestClient はバックグラウンドタスクを同期実行する
139
+
140
+ ```python
141
+ client = TestClient(app)
142
+ r = client.post("/orders", json={"item": "Widget", "quantity": 1})
143
+ # TestClient はレスポンスを返す前にバックグラウンドタスクも実行する
144
+ assert "confirmation:order_1:Widget" in notification_log # ✅ すでに実行済み
145
+ ```
146
+
147
+ `TestClient` はバックグラウンドタスクをリクエスト完了前に同期的に実行するため、
148
+ テストでは `background_tasks.add_task()` で追加した処理を即座に検証できる。
149
+
150
+ ---
151
+
152
+ ## 摩擦ポイント一覧
153
+
154
+ | ID | 内容 | 深刻度 |
155
+ |---|---|---|
156
+ | F82-1 | バックグラウンドタスクの例外がクライアントに見えない(FastAPI 設計上の制約) | 中 |
157
+ | F82-2 | nene2 に BackgroundTasks 推奨パターン(UseCase との分離)のドキュメントがない | 低 |
158
+ | F82-3 | バックグラウンドタスクは result を返せない(外部キューが必要) | 低 |
159
+
160
+ ---
161
+
162
+ ## 使用感(主観評価)
163
+
164
+ ### 直感性 ★★★★☆
165
+
166
+ `background_tasks.add_task(fn, arg1, arg2)` は非常にシンプルで直感的。
167
+ FastAPI のドキュメントを読めば即座に使える。
168
+ nene2 のミドルウェアスタックとの共存も問題なし。
169
+
170
+ ### 実害の深刻さ ★★☆☆☆
171
+
172
+ バックグラウンド例外がクライアントに見えない問題は、
173
+ 軽量な通知タスク(メール送信)であれば許容範囲。
174
+ ただし「注文確定」のような業務的に重要な処理を
175
+ バックグラウンドタスクに入れるのは設計ミス — そこは UseCase に残すべき。
176
+
177
+ ### 修正のしやすさ ★★★★★
178
+
179
+ 必要なのはドキュメントのみ:
180
+ - UseCase はクリーンに保つ(BackgroundTasks を引数に取らない)
181
+ - ハンドラー層でのみ `background_tasks.add_task()` を呼ぶ
182
+ - 失敗して困る処理はバックグラウンドタスクに入れない
183
+ - TestClient はバックグラウンドタスクを同期実行する(テスト性良好)
184
+
185
+ ### 総合コメント
186
+
187
+ FastAPI の `BackgroundTasks` は nene2 と相性が良く、
188
+ `setup_middlewares()` のミドルウェアスタックとも完全に共存する。
189
+ X-Request-Id・セキュリティヘッダーも正常に付与される。
190
+ 摩擦の大部分は「どう使うべきか」のドキュメント不足であり、
191
+ フレームワークの改修は不要。
192
+
193
+ ---
194
+
195
+ ## 推奨アクション
196
+
197
+ 1. **docs**: BackgroundTasks パターン(UseCase との分離、TestClient の同期実行)を how-to ガイドに追加
198
+ 2. **minor**: BackgroundTasks は「失敗しても OK な副作用のみ」というガイドラインの明記
@@ -0,0 +1,223 @@
1
+ # FT83: Depends() DI — FastAPI Depends を nene2 アーキテクチャで活用するパターン検証
2
+
3
+ **日付**: 2026-05-20
4
+ **テーマ**: UseCase・認証・Pagination の Depends 組み合わせパターンと dependency_overrides
5
+ **バージョン**: v1.8.27
6
+ **FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft83-depends-injection/`
7
+
8
+ ---
9
+
10
+ ## 概要
11
+
12
+ FastAPI の `Depends()` を nene2 の UseCase + Repository パターンと組み合わせた。
13
+ 依存チェーン(Repository → UseCase → Handler)、認証 Depends、
14
+ `PaginationQueryParser` の統合、テスト時の `dependency_overrides` による差し替えを検証した。
15
+ `PaginationResponse.model_dump()` が存在しない(`to_dict()` が正しい)という
16
+ Pydantic ユーザーがつまずく摩擦点を発見した。
17
+
18
+ ---
19
+
20
+ ## 実装パターン
21
+
22
+ ### Repository → UseCase の Depends チェーン
23
+
24
+ ```python
25
+ from typing import Annotated
26
+ from fastapi import Depends
27
+
28
+ def get_repo() -> InMemoryProductRepository:
29
+ return _repo # シングルトン
30
+
31
+ def get_list_use_case(
32
+ repo: Annotated[InMemoryProductRepository, Depends(get_repo)],
33
+ ) -> ListProductsUseCase:
34
+ return ListProductsUseCase(repo)
35
+
36
+ @app.get("/products")
37
+ def list_products(
38
+ use_case: Annotated[ListProductsUseCase, Depends(get_list_use_case)],
39
+ ) -> JSONResponse:
40
+ products, total = use_case.execute(...)
41
+ return JSONResponse(...)
42
+ ```
43
+
44
+ ### PaginationQueryParser + UseCase の両方を Depends
45
+
46
+ ```python
47
+ @app.get("/products")
48
+ def list_products(
49
+ pagination: Annotated[PaginationQueryParser, Depends(PaginationQueryParser)],
50
+ use_case: Annotated[ListProductsUseCase, Depends(get_list_use_case)],
51
+ ) -> JSONResponse:
52
+ products, total = use_case.execute(
53
+ limit=pagination.limit, offset=pagination.offset
54
+ )
55
+ return JSONResponse(
56
+ PaginationResponse(
57
+ items=[...], total=total, limit=pagination.limit, offset=pagination.offset
58
+ ).to_dict() # ← model_dump() ではなく to_dict()
59
+ )
60
+ ```
61
+
62
+ ### 認証 Depends(Bearer Token)
63
+
64
+ ```python
65
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
66
+
67
+ security = HTTPBearer(auto_error=False)
68
+
69
+ def get_current_user(
70
+ credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(security)],
71
+ ) -> str | None:
72
+ if credentials is None:
73
+ return None
74
+ return verify_token(credentials.credentials)
75
+
76
+ def require_auth(user: Annotated[str | None, Depends(get_current_user)]) -> str:
77
+ if user is None:
78
+ raise HTTPException(status_code=401)
79
+ return user
80
+
81
+ @app.post("/products")
82
+ def create_product(
83
+ body: ProductBody,
84
+ use_case: Annotated[CreateProductUseCase, Depends(get_create_use_case)],
85
+ current_user: Annotated[str, Depends(require_auth)],
86
+ ) -> JSONResponse: ...
87
+ ```
88
+
89
+ ### テスト時の dependency_overrides
90
+
91
+ ```python
92
+ custom_repo = InMemoryProductRepository()
93
+ custom_repo.create("Test Product", 100)
94
+
95
+ app.dependency_overrides[get_repo] = lambda: custom_repo
96
+ # テスト実行
97
+ del app.dependency_overrides[get_repo] # クリーンアップ
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 発見した問題
103
+
104
+ ### 問題1: PaginationResponse.model_dump() が存在しない
105
+
106
+ ```python
107
+ # ❌ Pydantic ユーザーは model_dump() を期待してしまう
108
+ PaginationResponse(...).model_dump() # AttributeError!
109
+
110
+ # ✅ 正しいメソッド
111
+ PaginationResponse(...).to_dict()
112
+ ```
113
+
114
+ `PaginationResponse` は dataclass だが、Pydantic v2 に慣れているユーザーは
115
+ `model_dump()` を期待して AttributeError に直面する。
116
+ FastAPI の OpenAPI ドキュメントや response_model を使うには
117
+ Pydantic BaseModel にするか、FastAPI の Response 型として登録する必要がある。
118
+
119
+ ### 問題2: Annotated[T, Depends(fn)] の記述が冗長
120
+
121
+ ```python
122
+ # 現在の書き方(型情報が Annotated の外側と内側に重複)
123
+ def list_products(
124
+ pagination: Annotated[PaginationQueryParser, Depends(PaginationQueryParser)],
125
+ use_case: Annotated[ListProductsUseCase, Depends(get_list_use_case)],
126
+ ) -> JSONResponse: ...
127
+
128
+ # 理想(nene2 が型エイリアスを提供)
129
+ type PaginationDep = Annotated[PaginationQueryParser, Depends(PaginationQueryParser)]
130
+
131
+ @app.get("/products")
132
+ def list_products(pagination: PaginationDep, ...) -> JSONResponse: ...
133
+ ```
134
+
135
+ `PaginationQueryParser` の `Annotated` エイリアスを nene2 が提供すれば
136
+ 毎回書く冗長さが解消される。
137
+
138
+ ### 問題3: nene2 の認証 Depends ユーティリティがない
139
+
140
+ ```python
141
+ # nene2.auth に Depends ユーティリティがない
142
+ # ユーザーは HTTPBearer + LocalTokenVerifier を手動で組み合わせる必要がある
143
+
144
+ # 理想:
145
+ from nene2.auth.deps import CurrentUser, require_auth
146
+ # これが存在しない
147
+ ```
148
+
149
+ `LocalTokenVerifier.from_env()` は実装されているが、
150
+ FastAPI の Depends パターンに接続する `CurrentUser` 型や
151
+ `require_auth` Depends がない。
152
+
153
+ ---
154
+
155
+ ## テスト結果(全16件パス)
156
+
157
+ ```
158
+ test_list_products_empty PASSED
159
+ test_create_product_returns_201 PASSED
160
+ test_get_product_returns_200 PASSED
161
+ test_get_nonexistent_returns_404 PASSED
162
+ test_create_product_requires_auth PASSED
163
+ test_create_product_with_auth_succeeds PASSED
164
+ test_pagination_with_depends PASSED # PaginationQueryParser Depends 動作
165
+ test_pagination_second_page PASSED
166
+ test_override_repo_with_custom_data PASSED # dependency_overrides 動作
167
+ test_override_repo_empty PASSED
168
+ test_request_id_with_depends PASSED # nene2 ミドルウェアと共存
169
+ test_security_headers_with_depends PASSED
170
+ test_validation_error_returns_422 PASSED
171
+ test_friction_annotated_syntax_verbosity PASSED # 摩擦記録
172
+ test_friction_use_case_chains_in_depends PASSED # 摩擦記録
173
+ test_friction_no_nene2_depends_utilities PASSED # 摩擦記録
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 摩擦ポイント一覧
179
+
180
+ | ID | 内容 | 深刻度 |
181
+ |---|---|---|
182
+ | F83-1 | `PaginationResponse.model_dump()` が存在しない(`to_dict()` が正しい)— Pydantic ユーザー混乱 | 中 |
183
+ | F83-2 | `Annotated[PaginationQueryParser, Depends(PaginationQueryParser)]` の冗長記述 | 低 |
184
+ | F83-3 | nene2 認証の Depends ユーティリティ (`CurrentUser`, `require_auth`) がない | 低 |
185
+
186
+ ---
187
+
188
+ ## 使用感(主観評価)
189
+
190
+ ### 直感性 ★★★☆☆
191
+
192
+ `Depends()` のパターン自体は FastAPI の機能なので明確。
193
+ ただし Repository → UseCase → Handler の3層チェーンは
194
+ Spring/NestJS の DI に慣れたユーザーには違和感がある
195
+ (Constructor Injection ではなく Function Injection)。
196
+ `dependency_overrides` によるテスト差し替えは非常に優れた機能。
197
+
198
+ ### 実害の深刻さ ★★☆☆☆
199
+
200
+ `PaginationResponse.to_dict()` を知らなければ AttributeError で即座に詰まる。
201
+ nene2 ドキュメントに `PaginationResponse` の使い方が記載されているが、
202
+ `model_dump()` と混同するユーザーが続出する可能性がある。
203
+
204
+ ### 修正のしやすさ ★★★★★
205
+
206
+ - `PaginationResponse` に `model_dump()` の alias を追加(または別名で明記)
207
+ - `Annotated[PaginationQueryParser, Depends(PaginationQueryParser)]` を `PaginationDep` 型エイリアスとして公開
208
+ - `nene2.auth.deps` モジュールに `CurrentUser` 型と `require_auth` Depends を追加
209
+
210
+ ### 総合コメント
211
+
212
+ FastAPI の `Depends()` は nene2 のアーキテクチャと非常に相性がよく、
213
+ Repository の差し替えも `dependency_overrides` で簡単にできる。
214
+ 主な摩擦は `PaginationResponse.to_dict()` の名前(Pydantic との不整合)と
215
+ 認証 Depends ユーティリティの欠如。どちらも小さな追加で解決できる。
216
+
217
+ ---
218
+
219
+ ## 推奨アクション
220
+
221
+ 1. **Issue**: `PaginationResponse` に `model_dump()` エイリアスを追加(Pydantic ユーザー向け)
222
+ 2. **Issue**: `nene2.http` に `PaginationDep` 型エイリアスを追加(`PaginationQueryParser` の Depends)
223
+ 3. **Issue**: `nene2.auth.deps` モジュールに認証 Depends ユーティリティを追加
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.27"
3
+ version = "1.8.28"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -7,7 +7,7 @@ from .health import (
7
7
  HealthCheckProtocol,
8
8
  HealthStatus,
9
9
  )
10
- from .pagination import PaginationQuery, PaginationQueryParser, PaginationResponse
10
+ from .pagination import PaginationDep, PaginationQuery, PaginationQueryParser, PaginationResponse
11
11
  from .problem_details import (
12
12
  PROBLEM_DETAILS_BASE_URL,
13
13
  configure_problem_details,
@@ -21,6 +21,7 @@ __all__ = [
21
21
  "CompositeHealthCheck",
22
22
  "HealthCheckProtocol",
23
23
  "HealthStatus",
24
+ "PaginationDep",
24
25
  "PaginationQuery",
25
26
  "PaginationQueryParser",
26
27
  "PaginationResponse",
@@ -7,7 +7,7 @@ import dataclasses
7
7
  from dataclasses import dataclass, field
8
8
  from typing import Annotated, Any
9
9
 
10
- from fastapi import Query, Request
10
+ from fastapi import Depends, Query, Request
11
11
 
12
12
  from nene2.validation.exceptions import ValidationError, ValidationException
13
13
 
@@ -128,3 +128,21 @@ class PaginationResponse:
128
128
  if self.total is not None:
129
129
  data["total"] = self.total
130
130
  return data
131
+
132
+ def model_dump(self) -> dict[str, Any]:
133
+ """Alias for :meth:`to_dict` — Pydantic-compatible name for familiarity."""
134
+ return self.to_dict()
135
+
136
+
137
+ type PaginationDep = Annotated[PaginationQueryParser, Depends(PaginationQueryParser)]
138
+ """Type alias for injecting :class:`PaginationQueryParser` via ``Depends``.
139
+
140
+ Usage::
141
+
142
+ from nene2.http import PaginationDep
143
+
144
+ @app.get("/items")
145
+ def list_items(pagination: PaginationDep) -> JSONResponse:
146
+ items, total = use_case.execute(pagination.limit, pagination.offset)
147
+ ...
148
+ """
@@ -9,7 +9,7 @@ from fastapi import Depends, FastAPI, Request
9
9
  from fastapi.responses import JSONResponse
10
10
  from fastapi.testclient import TestClient
11
11
 
12
- from nene2.http import PaginationQueryParser, PaginationResponse
12
+ from nene2.http import PaginationDep, PaginationQueryParser, PaginationResponse
13
13
  from nene2.validation.exceptions import ValidationException
14
14
 
15
15
 
@@ -125,3 +125,24 @@ def test_pagination_response_to_dict_serializes_dataclass_items() -> None:
125
125
  def test_pagination_response_to_dict_passes_through_dict_items() -> None:
126
126
  r = PaginationResponse(items=[{"id": 1}, {"id": 2}], limit=20, offset=0, total=2)
127
127
  assert r.to_dict()["items"] == [{"id": 1}, {"id": 2}]
128
+
129
+
130
+ def test_pagination_response_model_dump_is_alias_for_to_dict() -> None:
131
+ """model_dump() は to_dict() の Pydantic 互換エイリアス。"""
132
+ r = PaginationResponse(items=[{"id": 1}], limit=10, offset=0, total=1)
133
+ assert r.model_dump() == r.to_dict()
134
+
135
+
136
+ def test_pagination_dep_type_alias_usable_in_handler() -> None:
137
+ """PaginationDep 型エイリアスで Depends を省略して記述できる。"""
138
+ test_app = FastAPI()
139
+
140
+ @test_app.get("/things")
141
+ def list_things(pagination: PaginationDep) -> JSONResponse:
142
+ return JSONResponse({"limit": pagination.limit, "offset": pagination.offset})
143
+
144
+ test_client = TestClient(test_app)
145
+ r = test_client.get("/things?limit=5&offset=10")
146
+ assert r.status_code == 200
147
+ assert r.json()["limit"] == 5
148
+ assert r.json()["offset"] == 10
@@ -925,7 +925,7 @@ wheels = [
925
925
 
926
926
  [[package]]
927
927
  name = "nene2-python"
928
- version = "1.8.27"
928
+ version = "1.8.28"
929
929
  source = { editable = "." }
930
930
  dependencies = [
931
931
  { name = "alembic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes