nene2-python 1.8.11__tar.gz → 1.8.13__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.11 → nene2_python-1.8.13}/CHANGELOG.md +22 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/PKG-INFO +1 -1
- nene2_python-1.8.13/docs/field-trials/2026-05-field-trial-52.md +90 -0
- nene2_python-1.8.13/docs/field-trials/2026-05-field-trial-53.md +81 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/pyproject.toml +1 -1
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/auth/api_key.py +9 -7
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/auth/local_verifier.py +2 -2
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/auth/test_api_key.py +35 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/auth/test_bearer_token.py +13 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/uv.lock +1 -1
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.env.example +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.gitignore +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/AGENTS.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/CLAUDE.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/Dockerfile +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/LICENSE +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/README.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/alembic/README +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/alembic/env.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/alembic.ini +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/compose.yaml +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/de/index.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/fr/index.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/index.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/index.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/reference/api.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/roadmap.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/todo/current.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/zh/index.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/package-lock.json +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/package.json +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/__main__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/app.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/mcp.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/schema.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.11 → nene2_python-1.8.13}/tests/scripts/test_export_openapi.py +0 -0
|
@@ -5,6 +5,28 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [1.8.13] — 2026-05-20
|
|
9
|
+
|
|
10
|
+
FT53 フィールドトライアル — ApiKeyAuthMiddleware 実運用検証 + header_name パラメータ追加。
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `ApiKeyAuthMiddleware` に `header_name: str = "X-Api-Key"` パラメータを追加 — カスタムヘッダー名 (`X-Service-Token` 等) を指定可能に。エラーメッセージにも `header_name` が反映される (#286) (FT53)
|
|
14
|
+
- Field trial report: `docs/field-trials/2026-05-field-trial-53.md`
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [1.8.12] — 2026-05-20
|
|
19
|
+
|
|
20
|
+
FT52 フィールドトライアル — ミドルウェアスタック組み合わせ検証 + LocalTokenVerifier 改善。
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- `LocalTokenVerifier.__init__` — `allowed_tokens` の型を `list[str] | set[str] | frozenset[str]` に変更。内部で `frozenset` に変換し `in` 演算が O(1) になった (#284) (FT52)
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- Field trial report: `docs/field-trials/2026-05-field-trial-52.md`
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
8
30
|
## [1.8.11] — 2026-05-20
|
|
9
31
|
|
|
10
32
|
FT51 フィールドトライアル — SimpleDomainHandler 実運用検証 + problem_details_response バグ修正。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nene2-python
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.13
|
|
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,90 @@
|
|
|
1
|
+
# Field Trial 52: ミドルウェアスタック組み合わせ検証
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-05-20
|
|
4
|
+
**Theme**: 全ミドルウェアスタック (BearerToken + Throttle + RequestLogging + SecurityHeaders) の組み合わせ実運用
|
|
5
|
+
**Version under test**: v1.8.11
|
|
6
|
+
**FT App**: `/home/xi/docker/nene2-python-FT/ft52-middleware-stack/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`ErrorHandlerMiddleware` + `SecurityHeadersMiddleware` + `RequestIdMiddleware` +
|
|
13
|
+
`RequestLoggingMiddleware` + `ThrottleMiddleware` + `BearerTokenMiddleware` を
|
|
14
|
+
全スタックで積み重ねて動作を検証した。
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 実装内容
|
|
19
|
+
|
|
20
|
+
### ミドルウェアスタック
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
app.add_middleware(ErrorHandlerMiddleware)
|
|
24
|
+
app.add_middleware(SecurityHeadersMiddleware, csp="default-src 'self'", hsts="max-age=31536000")
|
|
25
|
+
app.add_middleware(RequestIdMiddleware)
|
|
26
|
+
app.add_middleware(RequestLoggingMiddleware, exclude_paths=["/health"], extra_context={"service": "ft52", "env": "test"})
|
|
27
|
+
app.add_middleware(ThrottleMiddleware, limit=5, window=60, exclude_paths=["/health"])
|
|
28
|
+
app.add_middleware(BearerTokenMiddleware, verifier=verifier, exclude_paths=["/health", "/docs", "/openapi.json"])
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`/health` は認証・ログ・レート制限をすべてバイパスする設計。
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## テスト結果
|
|
36
|
+
|
|
37
|
+
10 tests, all passed (after fixing FP52-1).
|
|
38
|
+
|
|
39
|
+
| テスト | 結果 |
|
|
40
|
+
|---|---|
|
|
41
|
+
| /health は認証不要 | ✅ |
|
|
42
|
+
| /items は認証必須 | ✅ |
|
|
43
|
+
| 有効トークンで /items アクセス | ✅ |
|
|
44
|
+
| セキュリティヘッダー存在確認 | ✅ |
|
|
45
|
+
| HSTS ヘッダー確認 | ✅ |
|
|
46
|
+
| X-Request-Id ヘッダー確認 | ✅ |
|
|
47
|
+
| X-RateLimit-Limit ヘッダー確認 | ✅ |
|
|
48
|
+
| /health はレート制限なし (10回連続) | ✅ |
|
|
49
|
+
| レート制限超過で 429 | ✅ |
|
|
50
|
+
| 無効トークンで 401 | ✅ |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 摩擦ポイント
|
|
55
|
+
|
|
56
|
+
### FP52-1: `LocalTokenVerifier` の引数名が直感的でない / `set` 非対応
|
|
57
|
+
|
|
58
|
+
**状況**: `LocalTokenVerifier(valid_tokens={valid_token})` と書いたが:
|
|
59
|
+
1. 引数名は `valid_tokens` ではなく `allowed_tokens`
|
|
60
|
+
2. `set` を渡すと型エラー(内部が `list[str]` を期待)
|
|
61
|
+
|
|
62
|
+
**修正**: `allowed_tokens` の型を `list[str] | set[str] | frozenset[str]` に変更し、
|
|
63
|
+
内部で `frozenset` に変換するよう修正 (#284)。
|
|
64
|
+
内部を `frozenset` にすることで `in` 演算の O(n) → O(1) 改善も得られた。
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## フレームワーク変更
|
|
69
|
+
|
|
70
|
+
### `LocalTokenVerifier` — `set` / `frozenset` を受け入れ可能に (#284)
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# 修正前
|
|
74
|
+
def __init__(self, allowed_tokens: list[str]) -> None:
|
|
75
|
+
self._allowed = allowed_tokens
|
|
76
|
+
|
|
77
|
+
# 修正後
|
|
78
|
+
def __init__(self, allowed_tokens: list[str] | set[str] | frozenset[str]) -> None:
|
|
79
|
+
self._allowed: frozenset[str] = frozenset(allowed_tokens)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 全スタック動作確認
|
|
85
|
+
|
|
86
|
+
各ミドルウェアの機能がスタック状態でも正常に動作することを確認:
|
|
87
|
+
- `exclude_paths` の設定が各ミドルウェアで独立して機能する
|
|
88
|
+
- `BearerTokenMiddleware` が `ThrottleMiddleware` より外側(先に評価)
|
|
89
|
+
- `SecurityHeadersMiddleware` のヘッダーが全レスポンスに付与される
|
|
90
|
+
- `RequestIdMiddleware` の `X-Request-Id` が全レスポンスに付与される
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Field Trial 53: ApiKeyAuthMiddleware 実運用検証
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-05-20
|
|
4
|
+
**Theme**: `ApiKeyAuthMiddleware` の実運用パターン検証
|
|
5
|
+
**Version under test**: v1.8.12
|
|
6
|
+
**FT App**: `/home/xi/docker/nene2-python-FT/ft53-api-key-auth/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`ApiKeyAuthMiddleware` + `LocalTokenVerifier(set)` の組み合わせを複数 API キー対応の
|
|
13
|
+
サービスで実運用した。`exclude_paths` と `RequestIdMiddleware` との組み合わせも検証。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装内容
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
verifier = LocalTokenVerifier({"key-dev-001", "key-dev-002"})
|
|
21
|
+
app.add_middleware(
|
|
22
|
+
ApiKeyAuthMiddleware,
|
|
23
|
+
verifier=verifier,
|
|
24
|
+
exclude_paths=["/health", "/docs", "/openapi.json", "/redoc"],
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## テスト結果
|
|
31
|
+
|
|
32
|
+
9 tests, all passed.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 摩擦ポイント
|
|
37
|
+
|
|
38
|
+
### FP53-1: `ApiKeyAuthMiddleware` のヘッダー名が `X-Api-Key` に固定されている
|
|
39
|
+
|
|
40
|
+
**状況**: サービスによっては `X-Service-Token` や `X-Internal-Key` など
|
|
41
|
+
異なるヘッダー名を使いたい場合がある。
|
|
42
|
+
|
|
43
|
+
**修正**: `header_name: str = "X-Api-Key"` パラメータを追加 (#286)。
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
app.add_middleware(
|
|
47
|
+
ApiKeyAuthMiddleware,
|
|
48
|
+
verifier=verifier,
|
|
49
|
+
header_name="X-Service-Token", # カスタムヘッダー名
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
エラーメッセージにも `header_name` が反映される:
|
|
54
|
+
```
|
|
55
|
+
"A valid X-Service-Token header is required."
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**補足**: HTTP ヘッダーは RFC 7230 で大文字小文字無視であるため、
|
|
59
|
+
`X-API-KEY` / `X-Api-Key` などの大文字小文字の違いは問題にならない。
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## フレームワーク変更
|
|
64
|
+
|
|
65
|
+
### `ApiKeyAuthMiddleware` — `header_name` パラメータを追加 (#286)
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# デフォルト動作(後方互換)
|
|
69
|
+
ApiKeyAuthMiddleware(verifier=verifier) # X-Api-Key を使用
|
|
70
|
+
|
|
71
|
+
# カスタムヘッダー名
|
|
72
|
+
ApiKeyAuthMiddleware(verifier=verifier, header_name="X-Service-Token")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 結論
|
|
78
|
+
|
|
79
|
+
`ApiKeyAuthMiddleware` は `LocalTokenVerifier(set)` との組み合わせで
|
|
80
|
+
複数 API キーを管理する用途で問題なく動作する。
|
|
81
|
+
`header_name` パラメータの追加で柔軟性が向上した。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""API Key authentication middleware.
|
|
2
2
|
|
|
3
|
-
Validates X-Api-Key
|
|
3
|
+
Validates a configurable header (default: X-Api-Key) using a TokenVerifierProtocol.
|
|
4
4
|
Returns 401 Problem Details when key is absent or invalid.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -13,18 +13,18 @@ from nene2.http.problem_details import problem_details_response
|
|
|
13
13
|
from .exceptions import TokenVerificationException
|
|
14
14
|
from .interfaces import TokenVerifierProtocol
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
_DEFAULT_API_KEY_HEADER = "X-Api-Key"
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class ApiKeyAuthMiddleware(BaseHTTPMiddleware):
|
|
20
|
-
"""Require a valid
|
|
20
|
+
"""Require a valid API key header on every request.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
health-check endpoints or API documentation::
|
|
22
|
+
The header name defaults to ``X-Api-Key`` but can be customised::
|
|
24
23
|
|
|
25
24
|
app.add_middleware(
|
|
26
25
|
ApiKeyAuthMiddleware,
|
|
27
26
|
verifier=LocalTokenVerifier(api_keys),
|
|
27
|
+
header_name="X-Service-Token",
|
|
28
28
|
exclude_paths=["/docs", "/openapi.json", "/redoc", "/health"],
|
|
29
29
|
)
|
|
30
30
|
"""
|
|
@@ -34,16 +34,18 @@ class ApiKeyAuthMiddleware(BaseHTTPMiddleware):
|
|
|
34
34
|
app: object,
|
|
35
35
|
*,
|
|
36
36
|
verifier: TokenVerifierProtocol,
|
|
37
|
+
header_name: str = _DEFAULT_API_KEY_HEADER,
|
|
37
38
|
exclude_paths: list[str] | None = None,
|
|
38
39
|
) -> None:
|
|
39
40
|
super().__init__(app) # type: ignore[arg-type]
|
|
40
41
|
self._verifier = verifier
|
|
42
|
+
self._header_name = header_name
|
|
41
43
|
self._exclude_paths = set(exclude_paths or [])
|
|
42
44
|
|
|
43
45
|
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
44
46
|
if request.url.path in self._exclude_paths:
|
|
45
47
|
return await call_next(request)
|
|
46
|
-
api_key = request.headers.get(
|
|
48
|
+
api_key = request.headers.get(self._header_name, "")
|
|
47
49
|
try:
|
|
48
50
|
verified = bool(api_key) and self._verifier.verify(api_key)
|
|
49
51
|
except TokenVerificationException:
|
|
@@ -53,6 +55,6 @@ class ApiKeyAuthMiddleware(BaseHTTPMiddleware):
|
|
|
53
55
|
"unauthorized",
|
|
54
56
|
"Unauthorized",
|
|
55
57
|
401,
|
|
56
|
-
"A valid
|
|
58
|
+
f"A valid {self._header_name} header is required.",
|
|
57
59
|
)
|
|
58
60
|
return await call_next(request)
|
|
@@ -11,8 +11,8 @@ import secrets
|
|
|
11
11
|
class LocalTokenVerifier:
|
|
12
12
|
"""Verify tokens against a fixed allowlist using constant-time comparison."""
|
|
13
13
|
|
|
14
|
-
def __init__(self, allowed_tokens: list[str]) -> None:
|
|
15
|
-
self._allowed = allowed_tokens
|
|
14
|
+
def __init__(self, allowed_tokens: list[str] | set[str] | frozenset[str]) -> None:
|
|
15
|
+
self._allowed: frozenset[str] = frozenset(allowed_tokens)
|
|
16
16
|
|
|
17
17
|
@classmethod
|
|
18
18
|
def from_env(cls, env_var: str, *, separator: str = ",") -> "LocalTokenVerifier":
|
|
@@ -87,3 +87,38 @@ def test_verifier_raises_token_verification_exception_returns_401() -> None:
|
|
|
87
87
|
client = TestClient(app, raise_server_exceptions=False)
|
|
88
88
|
response = client.get("/secret", headers={"X-Api-Key": "any-key"})
|
|
89
89
|
assert response.status_code == 401
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_custom_header_name() -> None:
|
|
93
|
+
app = FastAPI()
|
|
94
|
+
app.add_middleware(
|
|
95
|
+
ApiKeyAuthMiddleware,
|
|
96
|
+
verifier=LocalTokenVerifier(["svc-token"]),
|
|
97
|
+
header_name="X-Service-Token",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@app.get("/secret")
|
|
101
|
+
async def secret() -> JSONResponse:
|
|
102
|
+
return JSONResponse({"ok": True})
|
|
103
|
+
|
|
104
|
+
client = TestClient(app, raise_server_exceptions=False)
|
|
105
|
+
assert client.get("/secret", headers={"X-Service-Token": "svc-token"}).status_code == 200
|
|
106
|
+
assert client.get("/secret", headers={"X-Api-Key": "svc-token"}).status_code == 401
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_custom_header_name_in_error_message() -> None:
|
|
110
|
+
app = FastAPI()
|
|
111
|
+
app.add_middleware(
|
|
112
|
+
ApiKeyAuthMiddleware,
|
|
113
|
+
verifier=LocalTokenVerifier(["tok"]),
|
|
114
|
+
header_name="X-Internal-Key",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@app.get("/secret")
|
|
118
|
+
async def secret() -> JSONResponse:
|
|
119
|
+
return JSONResponse({"ok": True})
|
|
120
|
+
|
|
121
|
+
client = TestClient(app, raise_server_exceptions=False)
|
|
122
|
+
response = client.get("/secret")
|
|
123
|
+
assert response.status_code == 401
|
|
124
|
+
assert "X-Internal-Key" in response.json().get("detail", "")
|
|
@@ -113,3 +113,16 @@ def test_exclude_paths_default_is_empty() -> None:
|
|
|
113
113
|
|
|
114
114
|
client = TestClient(app)
|
|
115
115
|
assert client.get("/health").status_code == 401
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_local_verifier_accepts_set() -> None:
|
|
119
|
+
verifier = LocalTokenVerifier({"tok-a", "tok-b"})
|
|
120
|
+
assert verifier.verify("tok-a") is True
|
|
121
|
+
assert verifier.verify("tok-b") is True
|
|
122
|
+
assert verifier.verify("tok-c") is False
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_local_verifier_accepts_frozenset() -> None:
|
|
126
|
+
verifier = LocalTokenVerifier(frozenset({"tok-x", "tok-y"}))
|
|
127
|
+
assert verifier.verify("tok-x") is True
|
|
128
|
+
assert verifier.verify("unknown") is False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nene2_python-1.8.11 → nene2_python-1.8.13}/alembic/versions/001_create_notes_and_tags_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|