nene2-python 1.8.66__tar.gz → 1.8.163__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.
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.env.example +5 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.github/workflows/ci.yml +28 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/AGENTS.md +18 -2
- {nene2_python-1.8.66 → nene2_python-1.8.163}/CHANGELOG.md +30 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/CLAUDE.md +25 -12
- {nene2_python-1.8.66 → nene2_python-1.8.163}/PKG-INFO +63 -31
- {nene2_python-1.8.66 → nene2_python-1.8.163}/README.md +60 -29
- nene2_python-1.8.163/docs/explanation/field-trial-methodology.md +106 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-195.md +345 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-196.md +357 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-197.md +205 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-198.md +314 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-199.md +220 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-200.md +300 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-201.md +300 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-202.md +202 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-203.md +213 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-204.md +518 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-205.md +277 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-206.md +240 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-207.md +299 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-208.md +295 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-209.md +272 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-210.md +220 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-211.md +172 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-212.md +189 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-213.md +210 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-214.md +160 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-215.md +151 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-216.md +178 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-217.md +139 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-218.md +138 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-219.md +167 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-220.md +245 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-221.md +162 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-222.md +164 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-223.md +124 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-224.md +0 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-225.md +149 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-226.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-227.md +128 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-228.md +163 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-229.md +118 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-230.md +121 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-231.md +138 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-232.md +153 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-233.md +121 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-234.md +150 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-235.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-236.md +155 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-237.md +139 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-238.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-239.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-240.md +152 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-241.md +122 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-242.md +121 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-243.md +138 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-244.md +154 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-245.md +117 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-246.md +145 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-247.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-248.md +155 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-249.md +139 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-250.md +120 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-251.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-252.md +149 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-253.md +116 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-254.md +120 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-255.md +139 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-256.md +150 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-257.md +118 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-258.md +136 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-259.md +117 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-260.md +153 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-261.md +139 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-262.md +116 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-263.md +116 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-264.md +146 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-265.md +119 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-266.md +114 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-267.md +136 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-268.md +151 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-269.md +117 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-270.md +134 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-271.md +117 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-272.md +148 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-273.md +132 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-274.md +117 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-275.md +118 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-276.md +143 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-277.md +117 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-278.md +118 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-279.md +135 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-280.md +142 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-281.md +121 -0
- nene2_python-1.8.163/docs/field-trials/2026-05-field-trial-282.md +136 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/INDEX.md +96 -4
- nene2_python-1.8.163/docs/how-to/concurrency-patterns.md +68 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/middleware-stack.md +29 -1
- nene2_python-1.8.163/docs/how-to/release-and-publish.md +71 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/run-tests.md +8 -4
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/index.md +4 -0
- nene2_python-1.8.163/docs/ja/explanation/field-trial-methodology.md +104 -0
- nene2_python-1.8.163/docs/ja/how-to/release-and-publish.md +69 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/how-to/run-tests.md +6 -4
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/reference/framework-modules.md +2 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/reference/framework-modules.md +122 -0
- nene2_python-1.8.163/docs/review/2026-05-22.md +94 -0
- nene2_python-1.8.163/docs/review/2026-05-23.md +83 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/roadmap.md +48 -44
- nene2_python-1.8.163/docs/todo/current.md +179 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/pyproject.toml +6 -3
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/app.py +83 -6
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/handler.py +37 -35
- nene2_python-1.8.163/src/example/note/handler.py +97 -0
- nene2_python-1.8.163/src/example/tag/handler.py +89 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/__init__.py +17 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/api_key.py +1 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/bearer_token.py +14 -0
- nene2_python-1.8.163/src/nene2/auth/composite.py +129 -0
- nene2_python-1.8.163/src/nene2/auth/local_bearer_jwt.py +60 -0
- nene2_python-1.8.163/src/nene2/auth/local_issuer.py +70 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/http/__init__.py +11 -1
- nene2_python-1.8.163/src/nene2/http/context.py +44 -0
- nene2_python-1.8.163/src/nene2/http/etag.py +128 -0
- nene2_python-1.8.163/src/nene2/http/query.py +67 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/__init__.py +3 -1
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/throttle.py +77 -33
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/comment/test_comment_http.py +28 -25
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/note/test_list_notes.py +24 -15
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/tag/test_tags.py +16 -16
- nene2_python-1.8.163/tests/example/test_examples_protected.py +53 -0
- nene2_python-1.8.163/tests/example/test_local_throttle_default.py +15 -0
- nene2_python-1.8.163/tests/example/test_system_routes.py +33 -0
- nene2_python-1.8.163/tests/nene2/auth/test_composite_auth.py +134 -0
- nene2_python-1.8.163/tests/nene2/auth/test_local_bearer_jwt.py +60 -0
- nene2_python-1.8.163/tests/nene2/auth/test_local_issuer.py +116 -0
- nene2_python-1.8.163/tests/nene2/http/test_context.py +68 -0
- nene2_python-1.8.163/tests/nene2/http/test_etag.py +173 -0
- nene2_python-1.8.163/tests/nene2/http/test_query.py +117 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_throttle.py +28 -9
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/scripts/test_export_openapi.py +2 -1
- {nene2_python-1.8.66 → nene2_python-1.8.163}/uv.lock +4 -4
- nene2_python-1.8.66/docs/todo/current.md +0 -77
- nene2_python-1.8.66/src/example/note/handler.py +0 -89
- nene2_python-1.8.66/src/example/tag/handler.py +0 -85
- nene2_python-1.8.66/src/nene2/http/etag.py +0 -23
- nene2_python-1.8.66/tests/nene2/http/test_etag.py +0 -51
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.gitignore +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/Dockerfile +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/LICENSE +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/alembic/README +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/alembic/env.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/alembic.ini +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/compose.yaml +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/de/index.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-167.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-168.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-169.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-170.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-171.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-172.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-173.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-174.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-175.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-176.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-177.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-178.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-179.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-180.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-181.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-182.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-183.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-184.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-185.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-186.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-187.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-188.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-189.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-190.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-191.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-192.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-193.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-194.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/fr/index.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/decimal-unicode-input.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/email-address-parsing.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/index.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/reference/api.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/zh/index.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/package-lock.json +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/package.json +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/__main__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/mcp.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/schema.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/conftest.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.66 → nene2_python-1.8.163}/tests/scripts/__init__.py +0 -0
|
@@ -50,3 +50,31 @@ jobs:
|
|
|
50
50
|
# PYSEC-2025-183: pyjwt weak-key-length — disputed by supplier, no fix version available.
|
|
51
51
|
# Transitive via mcp>=1.0. Re-evaluate when pyjwt releases a fix. (#280)
|
|
52
52
|
run: uv run pip-audit --ignore-vuln PYSEC-2025-183
|
|
53
|
+
|
|
54
|
+
package-build:
|
|
55
|
+
name: Package build verification
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@v5
|
|
62
|
+
with:
|
|
63
|
+
version: "latest"
|
|
64
|
+
|
|
65
|
+
# publish.yml と同じ uv build を PR ごとに走らせ、配布物の壊れを早期検出する(#541)。
|
|
66
|
+
- name: Build sdist + wheel
|
|
67
|
+
run: uv build
|
|
68
|
+
|
|
69
|
+
- name: Validate distribution metadata (twine check)
|
|
70
|
+
run: uvx twine check dist/*
|
|
71
|
+
|
|
72
|
+
- name: Verify clean install + import (no example/tests leakage)
|
|
73
|
+
run: |
|
|
74
|
+
uv venv /tmp/verify
|
|
75
|
+
uv pip install --python /tmp/verify/bin/python dist/*.whl
|
|
76
|
+
/tmp/verify/bin/python -c "import nene2; from nene2.http import PaginationResponse; print('import OK')"
|
|
77
|
+
# フレームワーク以外(example/tests)が配布物に混入していないことを保証
|
|
78
|
+
if /tmp/verify/bin/python -c "import example" 2>/dev/null; then
|
|
79
|
+
echo "ERROR: example package leaked into wheel"; exit 1
|
|
80
|
+
fi
|
|
@@ -10,7 +10,8 @@ NENE2 の設計哲学を Python で実装したリファレンスフレームワ
|
|
|
10
10
|
- DTO / バリデーション: Pydantic v2 + `dataclass(frozen=True)`
|
|
11
11
|
- 型チェック: `mypy --strict`
|
|
12
12
|
- Lint / Format: `ruff`
|
|
13
|
-
- テスト: `pytest` + `httpx
|
|
13
|
+
- テスト: `pytest` + `httpx`(466 tests、カバレッジ 80% 以上)
|
|
14
|
+
- 現状: **v1.8.97** / FT219 完了 / [Field Trial INDEX](docs/field-trials/INDEX.md)
|
|
14
15
|
|
|
15
16
|
## 設計原則(PHP 版 NENE2 と共通)
|
|
16
17
|
|
|
@@ -19,13 +20,28 @@ NENE2 の設計哲学を Python で実装したリファレンスフレームワ
|
|
|
19
20
|
3. ハンドラーは薄く: parse → use-case → response
|
|
20
21
|
4. エラーは RFC 9457 Problem Details(application/problem+json)
|
|
21
22
|
5. `ValidationException` → 422 自動マッピング
|
|
23
|
+
6. FastAPI アプリは `APIRouter` + `create_app()` をファイル末尾に配置([CLAUDE.md](CLAUDE.md))
|
|
22
24
|
|
|
23
25
|
## 全チェックコマンド(CI と同等)
|
|
24
26
|
|
|
25
27
|
```bash
|
|
26
|
-
uv run pytest &&
|
|
28
|
+
uv run pytest && \
|
|
29
|
+
uv run mypy src/ && \
|
|
30
|
+
uv run ruff check src/ tests/ && \
|
|
31
|
+
uv run ruff format --check src/ tests/ && \
|
|
32
|
+
uv run pip-audit --ignore-vuln PYSEC-2025-183
|
|
27
33
|
```
|
|
28
34
|
|
|
35
|
+
## ドキュメント入口
|
|
36
|
+
|
|
37
|
+
| 用途 | ファイル |
|
|
38
|
+
|---|---|
|
|
39
|
+
| 設計ポリシー(SSOT) | [CLAUDE.md](CLAUDE.md) |
|
|
40
|
+
| 現状・次タスク | [docs/todo/current.md](docs/todo/current.md) |
|
|
41
|
+
| FT 一覧 | [docs/field-trials/INDEX.md](docs/field-trials/INDEX.md) |
|
|
42
|
+
| API リファレンス | [docs/reference/framework-modules.md](docs/reference/framework-modules.md) |
|
|
43
|
+
| How-to | [docs/how-to/](docs/how-to/) |
|
|
44
|
+
|
|
29
45
|
## PHP 版リポジトリとの関係
|
|
30
46
|
|
|
31
47
|
PHP 版 NENE2 の設計決定・ADR・フィールドトライアル記録は `../NENE2/docs/` を参照。
|
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
All notable changes to nene2-python are documented here.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
> 詳細な版ごとの一行サマリーは [`docs/todo/current.md`](docs/todo/current.md) の
|
|
7
|
+
> マイルストーン表を参照。本ファイルにはリリース粒度の集約エントリを記録する。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## [1.8.163] — 2026-05-29
|
|
12
|
+
|
|
13
|
+
v1.8.35〜v1.8.163 の集約リリース。フィールドトライアル網羅スイープの完了と、
|
|
14
|
+
公開準備・ポリシー整合・ハウスキーピングを含む。
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- フィールドトライアル **FT203〜FT282**(標準ライブラリ検証 + セキュリティ深掘り)。
|
|
18
|
+
セキュリティ診断(`%3==0`)・クラッカーペンテスト(`%4==0`)・6 ペルソナ DX レビューを各 FT で実施。
|
|
19
|
+
危険プリミティブ回避シリーズ(pickle/marshal/eval、subprocess、SSRF、ReDoS、zip/tar slip、解凍爆弾、SSTI、XXE 等)を含む。
|
|
20
|
+
- FT ループ方法論ドキュメント [`docs/explanation/field-trial-methodology.md`](docs/explanation/field-trial-methodology.md)(EN/JA)— 目的・3 フェーズ・終着点を明文化 (#540)
|
|
21
|
+
- ETag / 条件付きリクエスト、`CompositeAuthMiddleware`、`InMemoryRateLimitStorage`、query ヘルパー、`LocalTokenIssuer`、`RequestScopedContext`
|
|
22
|
+
- リリース手順 how-to [`docs/how-to/release-and-publish.md`](docs/how-to/release-and-publish.md)、CI に配布物ビルド検証ジョブ (#541)
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- example の全ハンドラー(Note/Tag/Comment)に `response_model` を付与し OpenAPI にレスポンス型を出力 (#539)
|
|
26
|
+
- starlette 1.0.1 へ更新(PYSEC-2026-161 解消、#611)
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- example の 422 レスポンスで全フィールドのエラーを返すように修正 (#588)
|
|
30
|
+
- `APP_ENV=local` で throttle を既定無効化 (#592)
|
|
31
|
+
|
|
32
|
+
### Housekeeping
|
|
33
|
+
- FT サンドボックスを 5.1G→79M に整理(`ft-status.sh --clean-sandbox` 追加)、マージ済み orphan ブランチを削除
|
|
34
|
+
- #553(`/examples/ping`・`/examples/notes`)は #578 で実装済みを確認し close
|
|
35
|
+
|
|
6
36
|
---
|
|
7
37
|
|
|
8
38
|
## [1.8.34] — 2026-05-20
|
|
@@ -223,7 +223,14 @@ AI エージェント(Claude 等)がこのコードベースを正確に理
|
|
|
223
223
|
## 7. エラーハンドリングポリシー
|
|
224
224
|
|
|
225
225
|
- `ValidationException` → 422 validation-failed Problem Details(自動)
|
|
226
|
-
- `
|
|
226
|
+
- `ValidationError` は **`field / message / code` の 3 引数がすべて必須**(省略すると TypeError)
|
|
227
|
+
```python
|
|
228
|
+
from nene2.validation import ValidationError, ValidationException
|
|
229
|
+
raise ValidationException([
|
|
230
|
+
ValidationError(field="host", message="許可されていません", code="host_not_allowed")
|
|
231
|
+
])
|
|
232
|
+
```
|
|
233
|
+
- `ErrorHandlerMiddleware` が全例外をキャッチ(FT サンドボックスでも `add_middleware(ErrorHandlerMiddleware)` を忘れないこと)
|
|
227
234
|
- `APP_DEBUG=true` 時のみ例外メッセージを detail に含める
|
|
228
235
|
- **スタックトレース・DB 接続情報を公開レスポンスに含めない**
|
|
229
236
|
- ログには `logging` モジュールのみ使用(`print()` 禁止)
|
|
@@ -319,7 +326,7 @@ uv run pytest && \
|
|
|
319
326
|
uv run mypy src/ && \
|
|
320
327
|
uv run ruff check src/ tests/ && \
|
|
321
328
|
uv run ruff format --check src/ tests/ && \
|
|
322
|
-
uv run pip-audit
|
|
329
|
+
uv run pip-audit --ignore-vuln PYSEC-2025-183
|
|
323
330
|
|
|
324
331
|
# 個別
|
|
325
332
|
uv run pytest
|
|
@@ -329,7 +336,7 @@ uv run mypy src/
|
|
|
329
336
|
uv run ruff check src/ tests/
|
|
330
337
|
uv run ruff check src/ tests/ --fix # 自動修正
|
|
331
338
|
uv run ruff format src/ tests/
|
|
332
|
-
uv run pip-audit
|
|
339
|
+
uv run pip-audit --ignore-vuln PYSEC-2025-183 # 依存関係の脆弱性スキャン(CI と同じ)
|
|
333
340
|
|
|
334
341
|
# 開発サーバー
|
|
335
342
|
uv run uvicorn src.example.app:app --reload --port 8080
|
|
@@ -345,11 +352,13 @@ docker compose up app
|
|
|
345
352
|
```
|
|
346
353
|
src/
|
|
347
354
|
nene2/ フレームワークコア
|
|
348
|
-
http/ JSON レスポンス・ページネーション・Problem Details
|
|
349
|
-
middleware/
|
|
355
|
+
http/ JSON レスポンス・ページネーション・Problem Details・ETag・query ヘルパー
|
|
356
|
+
middleware/ ミドルウェアパイプライン + setup_middlewares()
|
|
350
357
|
validation/ ValidationException / ValidationError
|
|
351
358
|
config/ 型付き設定オブジェクト(AppSettings)
|
|
352
|
-
auth/
|
|
359
|
+
auth/ Bearer / API Key / CompositeAuth / LocalTokenIssuer
|
|
360
|
+
cache/ TtlCache[V]
|
|
361
|
+
security/ verify_hmac_signature()
|
|
353
362
|
database/ SqlAlchemyQueryExecutor / SqlAlchemyTransactionManager
|
|
354
363
|
mcp/ LocalMcpServer / HttpxMcpClient
|
|
355
364
|
log/ structlog セットアップ
|
|
@@ -358,20 +367,24 @@ src/
|
|
|
358
367
|
note/ Note ドメイン(entity / repository / use_case / handler / sqlalchemy_repository)
|
|
359
368
|
tag/ Tag ドメイン(entity / repository / use_case / handler / sqlalchemy_repository)
|
|
360
369
|
comment/ Comment ドメイン(Note に紐付く nested ドメイン)
|
|
361
|
-
app.py
|
|
370
|
+
app.py アプリケーションファクトリ(create_app)
|
|
362
371
|
mcp.py MCP サーバー(Note / Tag / Comment 全 15 ツール)
|
|
363
372
|
|
|
364
|
-
tests/ pytest テスト(src/
|
|
373
|
+
tests/ pytest テスト(src/ を鏡像、466 tests)
|
|
365
374
|
docs/
|
|
366
375
|
adr/ 設計決定記録(変更理由を残す)
|
|
367
|
-
how-to/ How-to
|
|
376
|
+
how-to/ How-to ガイド(24+ 本)
|
|
368
377
|
howto/ MCP セットアップガイド
|
|
369
|
-
field-trials/ フィールドトライアル記録(FT1〜
|
|
378
|
+
field-trials/ フィールドトライアル記録(FT1〜FT219+、INDEX.md 参照)
|
|
379
|
+
field-trials/INDEX.md FT 検索索引(テーマ・診断種別・Follow-up Issue)
|
|
380
|
+
todo/current.md 現状サマリー・次タスク
|
|
381
|
+
roadmap.md ロードマップ・PHP 版対応表
|
|
382
|
+
review/ 作業日報
|
|
370
383
|
tutorials/ チュートリアル
|
|
371
384
|
explanation/ アーキテクチャ解説
|
|
372
385
|
reference/ 設定・モジュールリファレンス
|
|
373
386
|
ja/ 日本語ドキュメント(上記すべての翻訳)
|
|
374
|
-
.github/workflows/ CI(GitHub Actions)
|
|
387
|
+
.github/workflows/ CI(GitHub Actions: Python 3.12 / 3.14)
|
|
375
388
|
```
|
|
376
389
|
|
|
377
390
|
---
|
|
@@ -476,4 +489,4 @@ Python 標準ライブラリ・サードパーティライブラリを nene2-pyt
|
|
|
476
489
|
| `JsonResponseFactory` | `fastapi.responses.JSONResponse` |
|
|
477
490
|
| `PHPStan level 8` | `mypy --strict` |
|
|
478
491
|
| `PHP-CS-Fixer` | `ruff format` |
|
|
479
|
-
| `composer check` | `uv run pytest && mypy && ruff check && ruff format --check && pip-audit` |
|
|
492
|
+
| `composer check` | `uv run pytest && mypy && ruff check && ruff format --check && pip-audit --ignore-vuln PYSEC-2025-183` |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nene2-python
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.163
|
|
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
|
|
7
|
-
Project-URL: Documentation, https://github.
|
|
7
|
+
Project-URL: Documentation, https://hideyukimori.github.io/nene2-python/
|
|
8
8
|
Project-URL: Bug Tracker, https://github.com/hideyukiMORI/nene2-python/issues
|
|
9
9
|
Author-email: hideyukiMORI <info.xion.cc@gmail.com>
|
|
10
10
|
License: MIT
|
|
@@ -18,6 +18,7 @@ Classifier: Operating System :: OS Independent
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
22
23
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
23
24
|
Classifier: Typing :: Typed
|
|
@@ -51,6 +52,8 @@ A Python reference framework implementing the [NENE2](https://github.com/hideyuk
|
|
|
51
52
|
[](https://www.python.org/)
|
|
52
53
|
[](LICENSE)
|
|
53
54
|
|
|
55
|
+
**Current release**: `v1.8.97` · **466 tests** · CI on Python 3.12 and 3.14
|
|
56
|
+
|
|
54
57
|
---
|
|
55
58
|
|
|
56
59
|
## Features
|
|
@@ -60,11 +63,14 @@ A Python reference framework implementing the [NENE2](https://github.com/hideyuk
|
|
|
60
63
|
- **`mypy --strict`** — equivalent to PHPStan level 8 type safety
|
|
61
64
|
- **ruff** — lint and format in one tool (replaces flake8, isort, black, bandit)
|
|
62
65
|
- **RFC 9457 Problem Details** — uniform error responses across all endpoints
|
|
63
|
-
- **Bearer Token / API Key auth** —
|
|
66
|
+
- **Bearer Token / API Key auth** — `LocalTokenVerifier`, `CompositeAuthMiddleware`, dev JWT helpers
|
|
64
67
|
- **MCP support** — expose UseCases as AI agent tools via `LocalMcpServer`
|
|
65
68
|
- **SQLAlchemy Core** — parameterised SQL without ORM overhead
|
|
66
|
-
- **Security middleware** — CSP,
|
|
69
|
+
- **Security middleware** — CSP, rate limiting, request size limit, CORS via `setup_middlewares()`
|
|
70
|
+
- **ETag / conditional requests** — `generate_etag()`, `check_not_modified()`, `check_precondition()`
|
|
71
|
+
- **TTL cache & webhooks** — `TtlCache[V]`, `verify_hmac_signature()`
|
|
67
72
|
- **structlog** — structured JSON logging with request ID correlation
|
|
73
|
+
- **219 field trials** — stdlib and framework patterns validated in sandbox apps ([INDEX](docs/field-trials/INDEX.md))
|
|
68
74
|
|
|
69
75
|
---
|
|
70
76
|
|
|
@@ -76,31 +82,44 @@ pip install nene2-python
|
|
|
76
82
|
uv add nene2-python
|
|
77
83
|
```
|
|
78
84
|
|
|
79
|
-
Requires Python 3.12
|
|
85
|
+
Requires Python 3.12+ (CI also tests 3.14).
|
|
80
86
|
|
|
81
87
|
---
|
|
82
88
|
|
|
83
89
|
## Quick Start
|
|
84
90
|
|
|
91
|
+
Use `APIRouter` + `create_app()` at the **end of the file** (see [CLAUDE.md](CLAUDE.md)). Register middlewares with `setup_middlewares()` so 500 responses still get `X-Request-Id` and security headers.
|
|
92
|
+
|
|
85
93
|
```python
|
|
86
|
-
from fastapi import FastAPI
|
|
94
|
+
from fastapi import APIRouter, FastAPI
|
|
87
95
|
from nene2.config import AppSettings
|
|
88
|
-
from nene2.middleware import
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
app
|
|
99
|
-
|
|
100
|
-
app
|
|
101
|
-
|
|
96
|
+
from nene2.middleware import setup_middlewares
|
|
97
|
+
|
|
98
|
+
router = APIRouter()
|
|
99
|
+
|
|
100
|
+
@router.get("/health")
|
|
101
|
+
def health() -> dict[str, str]:
|
|
102
|
+
return {"status": "ok"}
|
|
103
|
+
|
|
104
|
+
def create_app() -> FastAPI:
|
|
105
|
+
cfg = AppSettings()
|
|
106
|
+
app = FastAPI(title=cfg.app_name)
|
|
107
|
+
setup_middlewares(
|
|
108
|
+
app,
|
|
109
|
+
debug=cfg.app_debug,
|
|
110
|
+
throttle_limit=cfg.throttle_limit if cfg.throttle_enabled else None,
|
|
111
|
+
cors_allowed_origins=cfg.cors_origins if cfg.cors_enabled else None,
|
|
112
|
+
)
|
|
113
|
+
app.include_router(router)
|
|
114
|
+
return app
|
|
115
|
+
|
|
116
|
+
app = create_app()
|
|
102
117
|
```
|
|
103
118
|
|
|
119
|
+
See the full reference app in [`src/example/`](src/example/) (Note / Tag / Comment CRUD, auth, MCP).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
104
123
|
### Define a domain
|
|
105
124
|
|
|
106
125
|
```python
|
|
@@ -137,7 +156,6 @@ class GetNoteUseCase:
|
|
|
137
156
|
```python
|
|
138
157
|
from fastapi import APIRouter
|
|
139
158
|
from fastapi.responses import JSONResponse
|
|
140
|
-
from nene2.http import problem_details_response
|
|
141
159
|
|
|
142
160
|
router = APIRouter(prefix="/notes", tags=["notes"])
|
|
143
161
|
|
|
@@ -147,16 +165,14 @@ async def get_note(note_id: int) -> JSONResponse:
|
|
|
147
165
|
return JSONResponse({"id": note.id, "title": note.title, "body": note.body})
|
|
148
166
|
```
|
|
149
167
|
|
|
150
|
-
See the full working example in [`src/example/`](src/example/).
|
|
151
|
-
|
|
152
168
|
---
|
|
153
169
|
|
|
154
170
|
## Development Commands
|
|
155
171
|
|
|
156
172
|
```bash
|
|
157
173
|
uv sync # install dependencies
|
|
158
|
-
uv run pytest #
|
|
159
|
-
uv run mypy src/ # type check
|
|
174
|
+
uv run pytest # 466 tests, coverage ≥ 80%
|
|
175
|
+
uv run mypy src/ # type check (strict)
|
|
160
176
|
uv run ruff check src/ tests/ # lint
|
|
161
177
|
uv run ruff format src/ tests/ # format
|
|
162
178
|
uv run uvicorn src.example.app:app --reload --port 8080 # dev server
|
|
@@ -169,25 +185,39 @@ uv run pytest && \
|
|
|
169
185
|
uv run mypy src/ && \
|
|
170
186
|
uv run ruff check src/ tests/ && \
|
|
171
187
|
uv run ruff format --check src/ tests/ && \
|
|
172
|
-
uv run pip-audit
|
|
188
|
+
uv run pip-audit --ignore-vuln PYSEC-2025-183
|
|
173
189
|
```
|
|
174
190
|
|
|
191
|
+
CI also runs a **90% coverage gate** on `example/*/use_case.py`, `entity.py`, and `async_use_case.py`.
|
|
192
|
+
|
|
175
193
|
---
|
|
176
194
|
|
|
177
195
|
## Framework Modules
|
|
178
196
|
|
|
179
197
|
| Module | Purpose |
|
|
180
198
|
|---|---|
|
|
181
|
-
| `nene2.http` |
|
|
182
|
-
| `nene2.middleware` |
|
|
183
|
-
| `nene2.auth` |
|
|
199
|
+
| `nene2.http` | Pagination, Problem Details, health checks, ETag, query helpers, `RequestScopedContext` |
|
|
200
|
+
| `nene2.middleware` | Full pipeline + `setup_middlewares()`, rate-limit storage protocol |
|
|
201
|
+
| `nene2.auth` | Bearer / API Key / composite auth, `LocalTokenIssuer`, `make_require_auth()` |
|
|
184
202
|
| `nene2.database` | `SqlAlchemyQueryExecutor`, `SqlAlchemyTransactionManager`, `DatabaseHealthCheck` |
|
|
185
203
|
| `nene2.config` | `AppSettings` (pydantic-settings, reads from env / `.env`) |
|
|
186
204
|
| `nene2.validation` | `ValidationException`, `ValidationError` |
|
|
205
|
+
| `nene2.cache` | `TtlCache[V]` — thread-safe in-memory TTL cache |
|
|
206
|
+
| `nene2.security` | `verify_hmac_signature()` for webhook verification |
|
|
187
207
|
| `nene2.mcp` | `LocalMcpServer`, `HttpxMcpClient` |
|
|
188
208
|
| `nene2.log` | `setup_logging()` (structlog, JSON in production) |
|
|
189
209
|
| `nene2.use_case` | `UseCaseProtocol[I, O]`, `AsyncUseCaseProtocol[I, O]` |
|
|
190
210
|
|
|
211
|
+
Details: [Framework modules reference](docs/reference/framework-modules.md) · [How-to guides](docs/how-to/)
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Versioning
|
|
216
|
+
|
|
217
|
+
- **`pyproject.toml` `version`** — bumped with each merged FT or feature PR (currently `1.8.97`).
|
|
218
|
+
- **Git tags** (`v1.8.N`) — created on selected releases; may lag behind `pyproject.toml` during rapid FT loops.
|
|
219
|
+
- See [docs/todo/current.md](docs/todo/current.md) for the latest milestone table.
|
|
220
|
+
|
|
191
221
|
---
|
|
192
222
|
|
|
193
223
|
## PHP NENE2 Correspondence
|
|
@@ -197,7 +227,7 @@ uv run pip-audit
|
|
|
197
227
|
| `readonly class` | `dataclass(frozen=True, slots=True)` |
|
|
198
228
|
| `PHPStan level 8` | `mypy --strict` |
|
|
199
229
|
| `PHP-CS-Fixer` | `ruff format` |
|
|
200
|
-
| `composer check` | `uv run pytest && mypy && ruff check && ruff format --check && pip-audit` |
|
|
230
|
+
| `composer check` | `uv run pytest && mypy && ruff check && ruff format --check && pip-audit --ignore-vuln PYSEC-2025-183` |
|
|
201
231
|
| `ValidationException` | `nene2.validation.ValidationException` |
|
|
202
232
|
| `PaginationQueryParser` | `nene2.http.PaginationQueryParser` |
|
|
203
233
|
| `ErrorHandlerMiddleware` | `nene2.middleware.ErrorHandlerMiddleware` |
|
|
@@ -208,4 +238,6 @@ uv run pip-audit
|
|
|
208
238
|
## Related
|
|
209
239
|
|
|
210
240
|
- [NENE2 (PHP)](https://github.com/hideyukiMORI/NENE2) — PHP reference implementation
|
|
211
|
-
- [Documentation](https://hideyukimori.github.io/nene2-python/) —
|
|
241
|
+
- [Documentation (GitHub Pages)](https://hideyukimori.github.io/nene2-python/) — tutorials, how-to, reference (Diátaxis)
|
|
242
|
+
- [Field Trial INDEX](docs/field-trials/INDEX.md) — FT1–FT219 searchable index
|
|
243
|
+
- [Roadmap](docs/roadmap.md) — current status and planned work
|
|
@@ -6,6 +6,8 @@ A Python reference framework implementing the [NENE2](https://github.com/hideyuk
|
|
|
6
6
|
[](https://www.python.org/)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
9
|
+
**Current release**: `v1.8.97` · **466 tests** · CI on Python 3.12 and 3.14
|
|
10
|
+
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## Features
|
|
@@ -15,11 +17,14 @@ A Python reference framework implementing the [NENE2](https://github.com/hideyuk
|
|
|
15
17
|
- **`mypy --strict`** — equivalent to PHPStan level 8 type safety
|
|
16
18
|
- **ruff** — lint and format in one tool (replaces flake8, isort, black, bandit)
|
|
17
19
|
- **RFC 9457 Problem Details** — uniform error responses across all endpoints
|
|
18
|
-
- **Bearer Token / API Key auth** —
|
|
20
|
+
- **Bearer Token / API Key auth** — `LocalTokenVerifier`, `CompositeAuthMiddleware`, dev JWT helpers
|
|
19
21
|
- **MCP support** — expose UseCases as AI agent tools via `LocalMcpServer`
|
|
20
22
|
- **SQLAlchemy Core** — parameterised SQL without ORM overhead
|
|
21
|
-
- **Security middleware** — CSP,
|
|
23
|
+
- **Security middleware** — CSP, rate limiting, request size limit, CORS via `setup_middlewares()`
|
|
24
|
+
- **ETag / conditional requests** — `generate_etag()`, `check_not_modified()`, `check_precondition()`
|
|
25
|
+
- **TTL cache & webhooks** — `TtlCache[V]`, `verify_hmac_signature()`
|
|
22
26
|
- **structlog** — structured JSON logging with request ID correlation
|
|
27
|
+
- **219 field trials** — stdlib and framework patterns validated in sandbox apps ([INDEX](docs/field-trials/INDEX.md))
|
|
23
28
|
|
|
24
29
|
---
|
|
25
30
|
|
|
@@ -31,31 +36,44 @@ pip install nene2-python
|
|
|
31
36
|
uv add nene2-python
|
|
32
37
|
```
|
|
33
38
|
|
|
34
|
-
Requires Python 3.12
|
|
39
|
+
Requires Python 3.12+ (CI also tests 3.14).
|
|
35
40
|
|
|
36
41
|
---
|
|
37
42
|
|
|
38
43
|
## Quick Start
|
|
39
44
|
|
|
45
|
+
Use `APIRouter` + `create_app()` at the **end of the file** (see [CLAUDE.md](CLAUDE.md)). Register middlewares with `setup_middlewares()` so 500 responses still get `X-Request-Id` and security headers.
|
|
46
|
+
|
|
40
47
|
```python
|
|
41
|
-
from fastapi import FastAPI
|
|
48
|
+
from fastapi import APIRouter, FastAPI
|
|
42
49
|
from nene2.config import AppSettings
|
|
43
|
-
from nene2.middleware import
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
app
|
|
54
|
-
|
|
55
|
-
app
|
|
56
|
-
|
|
50
|
+
from nene2.middleware import setup_middlewares
|
|
51
|
+
|
|
52
|
+
router = APIRouter()
|
|
53
|
+
|
|
54
|
+
@router.get("/health")
|
|
55
|
+
def health() -> dict[str, str]:
|
|
56
|
+
return {"status": "ok"}
|
|
57
|
+
|
|
58
|
+
def create_app() -> FastAPI:
|
|
59
|
+
cfg = AppSettings()
|
|
60
|
+
app = FastAPI(title=cfg.app_name)
|
|
61
|
+
setup_middlewares(
|
|
62
|
+
app,
|
|
63
|
+
debug=cfg.app_debug,
|
|
64
|
+
throttle_limit=cfg.throttle_limit if cfg.throttle_enabled else None,
|
|
65
|
+
cors_allowed_origins=cfg.cors_origins if cfg.cors_enabled else None,
|
|
66
|
+
)
|
|
67
|
+
app.include_router(router)
|
|
68
|
+
return app
|
|
69
|
+
|
|
70
|
+
app = create_app()
|
|
57
71
|
```
|
|
58
72
|
|
|
73
|
+
See the full reference app in [`src/example/`](src/example/) (Note / Tag / Comment CRUD, auth, MCP).
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
59
77
|
### Define a domain
|
|
60
78
|
|
|
61
79
|
```python
|
|
@@ -92,7 +110,6 @@ class GetNoteUseCase:
|
|
|
92
110
|
```python
|
|
93
111
|
from fastapi import APIRouter
|
|
94
112
|
from fastapi.responses import JSONResponse
|
|
95
|
-
from nene2.http import problem_details_response
|
|
96
113
|
|
|
97
114
|
router = APIRouter(prefix="/notes", tags=["notes"])
|
|
98
115
|
|
|
@@ -102,16 +119,14 @@ async def get_note(note_id: int) -> JSONResponse:
|
|
|
102
119
|
return JSONResponse({"id": note.id, "title": note.title, "body": note.body})
|
|
103
120
|
```
|
|
104
121
|
|
|
105
|
-
See the full working example in [`src/example/`](src/example/).
|
|
106
|
-
|
|
107
122
|
---
|
|
108
123
|
|
|
109
124
|
## Development Commands
|
|
110
125
|
|
|
111
126
|
```bash
|
|
112
127
|
uv sync # install dependencies
|
|
113
|
-
uv run pytest #
|
|
114
|
-
uv run mypy src/ # type check
|
|
128
|
+
uv run pytest # 466 tests, coverage ≥ 80%
|
|
129
|
+
uv run mypy src/ # type check (strict)
|
|
115
130
|
uv run ruff check src/ tests/ # lint
|
|
116
131
|
uv run ruff format src/ tests/ # format
|
|
117
132
|
uv run uvicorn src.example.app:app --reload --port 8080 # dev server
|
|
@@ -124,25 +139,39 @@ uv run pytest && \
|
|
|
124
139
|
uv run mypy src/ && \
|
|
125
140
|
uv run ruff check src/ tests/ && \
|
|
126
141
|
uv run ruff format --check src/ tests/ && \
|
|
127
|
-
uv run pip-audit
|
|
142
|
+
uv run pip-audit --ignore-vuln PYSEC-2025-183
|
|
128
143
|
```
|
|
129
144
|
|
|
145
|
+
CI also runs a **90% coverage gate** on `example/*/use_case.py`, `entity.py`, and `async_use_case.py`.
|
|
146
|
+
|
|
130
147
|
---
|
|
131
148
|
|
|
132
149
|
## Framework Modules
|
|
133
150
|
|
|
134
151
|
| Module | Purpose |
|
|
135
152
|
|---|---|
|
|
136
|
-
| `nene2.http` |
|
|
137
|
-
| `nene2.middleware` |
|
|
138
|
-
| `nene2.auth` |
|
|
153
|
+
| `nene2.http` | Pagination, Problem Details, health checks, ETag, query helpers, `RequestScopedContext` |
|
|
154
|
+
| `nene2.middleware` | Full pipeline + `setup_middlewares()`, rate-limit storage protocol |
|
|
155
|
+
| `nene2.auth` | Bearer / API Key / composite auth, `LocalTokenIssuer`, `make_require_auth()` |
|
|
139
156
|
| `nene2.database` | `SqlAlchemyQueryExecutor`, `SqlAlchemyTransactionManager`, `DatabaseHealthCheck` |
|
|
140
157
|
| `nene2.config` | `AppSettings` (pydantic-settings, reads from env / `.env`) |
|
|
141
158
|
| `nene2.validation` | `ValidationException`, `ValidationError` |
|
|
159
|
+
| `nene2.cache` | `TtlCache[V]` — thread-safe in-memory TTL cache |
|
|
160
|
+
| `nene2.security` | `verify_hmac_signature()` for webhook verification |
|
|
142
161
|
| `nene2.mcp` | `LocalMcpServer`, `HttpxMcpClient` |
|
|
143
162
|
| `nene2.log` | `setup_logging()` (structlog, JSON in production) |
|
|
144
163
|
| `nene2.use_case` | `UseCaseProtocol[I, O]`, `AsyncUseCaseProtocol[I, O]` |
|
|
145
164
|
|
|
165
|
+
Details: [Framework modules reference](docs/reference/framework-modules.md) · [How-to guides](docs/how-to/)
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Versioning
|
|
170
|
+
|
|
171
|
+
- **`pyproject.toml` `version`** — bumped with each merged FT or feature PR (currently `1.8.97`).
|
|
172
|
+
- **Git tags** (`v1.8.N`) — created on selected releases; may lag behind `pyproject.toml` during rapid FT loops.
|
|
173
|
+
- See [docs/todo/current.md](docs/todo/current.md) for the latest milestone table.
|
|
174
|
+
|
|
146
175
|
---
|
|
147
176
|
|
|
148
177
|
## PHP NENE2 Correspondence
|
|
@@ -152,7 +181,7 @@ uv run pip-audit
|
|
|
152
181
|
| `readonly class` | `dataclass(frozen=True, slots=True)` |
|
|
153
182
|
| `PHPStan level 8` | `mypy --strict` |
|
|
154
183
|
| `PHP-CS-Fixer` | `ruff format` |
|
|
155
|
-
| `composer check` | `uv run pytest && mypy && ruff check && ruff format --check && pip-audit` |
|
|
184
|
+
| `composer check` | `uv run pytest && mypy && ruff check && ruff format --check && pip-audit --ignore-vuln PYSEC-2025-183` |
|
|
156
185
|
| `ValidationException` | `nene2.validation.ValidationException` |
|
|
157
186
|
| `PaginationQueryParser` | `nene2.http.PaginationQueryParser` |
|
|
158
187
|
| `ErrorHandlerMiddleware` | `nene2.middleware.ErrorHandlerMiddleware` |
|
|
@@ -163,4 +192,6 @@ uv run pip-audit
|
|
|
163
192
|
## Related
|
|
164
193
|
|
|
165
194
|
- [NENE2 (PHP)](https://github.com/hideyukiMORI/NENE2) — PHP reference implementation
|
|
166
|
-
- [Documentation](https://hideyukimori.github.io/nene2-python/) —
|
|
195
|
+
- [Documentation (GitHub Pages)](https://hideyukimori.github.io/nene2-python/) — tutorials, how-to, reference (Diátaxis)
|
|
196
|
+
- [Field Trial INDEX](docs/field-trials/INDEX.md) — FT1–FT219 searchable index
|
|
197
|
+
- [Roadmap](docs/roadmap.md) — current status and planned work
|