core-framework 1.2.0__tar.gz → 1.3.0__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.
- core_framework-1.3.0/.github/workflows/publish-pypi.yml +34 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/CHANGELOG.md +17 -1
- core_framework-1.3.0/PKG-INFO +115 -0
- core_framework-1.3.0/README.md +81 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/firebase.py +8 -1
- core_framework-1.3.0/docker-compose.dev.yaml +24 -0
- core_framework-1.3.0/docs/todo.md +10 -0
- core_framework-1.3.0/firebase_config.example.json +13 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/pyproject.toml +1 -1
- {core_framework-1.2.0 → core_framework-1.3.0}/uv.lock +1 -1
- core_framework-1.2.0/.logfire/.gitignore +0 -1
- core_framework-1.2.0/.logfire/logfire_credentials.json +0 -6
- core_framework-1.2.0/PKG-INFO +0 -131
- core_framework-1.2.0/README.md +0 -97
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/api-layer.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/api-reference-docs.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/api-security.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/api-validation.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/application-layer.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/constants-final.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/database-triggers.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/domain-caller-context.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/domain-imports.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/domain-input-guards.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/flow-documentation.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/implementation-workflow.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/layer-boundaries.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/no-code-in-docs.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/no-docstrings.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/repository-read-consistency.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/strong-read-opt-in.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/tech-stack.mdc +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/skills/add-config/SKILL.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/skills/add-domain/SKILL.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/skills/code-review/SKILL.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.dockerignore +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.github/workflows/_deploy.yml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.github/workflows/dev-ci-cd.yaml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.github/workflows/manual-deployment.yaml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.github/workflows/test.yaml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.gitignore +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.pre-commit-config.yaml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/.python-version +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/LICENSE +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/comment/alembic/README +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/comment/alembic/env.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/comment/alembic/script.py.mako +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/comment/alembic.ini +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/extension/alembic/README +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/extension/alembic/env.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/extension/alembic/script.py.mako +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/extension/alembic.ini +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/moderation/alembic/README +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/moderation/alembic/env.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/moderation/alembic/script.py.mako +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/moderation/alembic.ini +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/notification/alembic/README +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/notification/alembic/env.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/notification/alembic/script.py.mako +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/notification/alembic.ini +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/post/alembic/README +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/post/alembic/env.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/post/alembic/script.py.mako +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/post/alembic.ini +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/user/alembic/README +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/user/alembic/env.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/user/alembic/script.py.mako +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/alembic/user/alembic.ini +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/config.toml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/config.toml.template +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/comments/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/comments/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/moderation/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/moderation/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/moderation/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/posts/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/posts/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/users/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/users/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/admin/users/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/auth/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/auth/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/auth/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/comments/authenticated/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/comments/authenticated/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/comments/public/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/comments/public/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/comments/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/comments/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/constants.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/dependencies.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/events/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/events/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/notifications/authenticated/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/notifications/authenticated/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/notifications/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/posts/authenticated/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/posts/authenticated/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/posts/public/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/posts/public/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/posts/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/posts/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/system/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/system/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/authenticated/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/authenticated/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/authenticated/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/public/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/public/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/public/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/router.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/api/users/shared/schemas.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/auth/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/auth/access_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/auth/auth_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/auth/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/bootstrap.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/comments/admin_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/comments/aggregation_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/comments/authenticated_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/comments/public_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/events/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/events/event_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/events/event_token.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/events/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/moderation/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/moderation/appeal_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/moderation/moderator_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/moderation/report_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/moderation/scheduled_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/moderation/user_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/notifications/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/notifications/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/notifications/inbox_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/notifications/mute_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/notifications/notification_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/posts/admin_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/posts/aggregation_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/posts/authenticated_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/posts/public_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/shared/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/shared/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/shared/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/shared/user_agent.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/users/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/users/admin_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/users/aggregation_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/users/authenticated_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/users/public_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/application/users/scheduled_service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/asgi.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/bundled_alembic.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/constants.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/cache.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/context.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/database.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/comment.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/common.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/moderation.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/notification.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/post.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/setup.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/exception_handlers/user.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/http_client.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/logging.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/middleware.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/observability.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/pagination.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/redis.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/runtime.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/core/settings.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/constants.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/dependencies.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/repository.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/comment/service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/dependencies.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/repository.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/moderation/service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/dependencies.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/repository.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/notification/service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/constants.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/dependencies.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/repository.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/post/service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/README.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/constants.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/dependencies.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/enums.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/exceptions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/models.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/repository.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/service.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/user/utils.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/domains/utils.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/main.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/migrate_cli.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/arq.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/auth.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/config.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/containers.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/firebase.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/hookspecs.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/httpx_test_client.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/migrations.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/testing/plugin.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/main.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/schedules/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/tasks/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/tasks/process_account_deletion.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/tasks/process_mute_lift.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/core_framework/worker/worker_context.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docker-compose.yml +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/dockerfile +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/api.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/architecture-decisions.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/architecture.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/conventions.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/core-framework-migration.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/database-triggers.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/event-outbox-design.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/auth/access_control.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/auth/registration.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/admin_comments.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/comment_report.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/comment_stats_aggregation.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/create_comment.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/delete_comment.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/edit_comment.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/comments/retrieve_comments.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/events/events.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/mentions/mentions_in_content.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/moderation/appeals.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/moderation/internal_notes.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/moderation/moderator_actions.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/moderation/reports.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/moderation/restrictions.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/notifications/notification_inbox.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/posts/admin_posts.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/posts/author_context.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/posts/hashtag_discovery.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/posts/post_like.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/posts/post_stats_aggregation.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/account.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/account_deletion.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/blocks.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/change_history.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/check_username_exists.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/follow.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/my_posts_and_comments.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/preferences.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/profile.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/flows/users/user_removal.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/follow-system-design.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/package-api.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/docs/testing-plugin-design.md +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/makefile +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/conftest.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/_http_helpers.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/comments/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/comments/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/moderation/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/moderation/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/posts/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/posts/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/users/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/admin/users/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/auth/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/auth/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/comments/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/comments/public/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/comments/public/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/events/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/notifications/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/notifications/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/followers_visibility_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/public/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/posts/public/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/system/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/system/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/users/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/users/authenticated/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/users/authenticated/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/users/public/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/api/users/public/router_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/account_deletion_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/aggregate_user_stats_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/conftest.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/mute_lift_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/integration/worker/utils_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/application/comments/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/application/events/event_service_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/application/notifications/inbox_service_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/core/bundled_alembic_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/core/migrate_cli_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/core/pagination_test.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/domains/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/domains/comment/__init__.py +0 -0
- {core_framework-1.2.0 → core_framework-1.3.0}/tests/unit/domains/user/service_test.py +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v6
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v7
|
|
18
|
+
with:
|
|
19
|
+
version: "0.7.9"
|
|
20
|
+
enable-cache: true
|
|
21
|
+
cache-dependency-glob: "uv.lock"
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version-file: ".python-version"
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- name: Publish
|
|
32
|
+
run: uv publish
|
|
33
|
+
env:
|
|
34
|
+
UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }}
|
|
@@ -4,6 +4,21 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.3.0] - 2026-05-16
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`docker-compose.dev.yaml`** for local Postgres and Redis (aligned with repo **`config.toml`**).
|
|
12
|
+
- **`firebase_config.example.json`** and **`FileNotFoundError`** with next-step text when **`firebase_config.json`** is missing in **`init_firebase`**.
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
- **`README.md`**: install (**`uv`**), local development, Firebase file copy, host / pytest integration examples.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Example Firebase JSON uses no PEM markers so **`detect-private-key`** pre-commit hooks do not false-positive.
|
|
21
|
+
|
|
7
22
|
## [1.2.0] - 2026-05-02
|
|
8
23
|
|
|
9
24
|
### Changed
|
|
@@ -124,4 +139,5 @@ First **SemVer-stable** release per **`docs/package-api.md`**.
|
|
|
124
139
|
[1.1.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.0.0...v1.1.0
|
|
125
140
|
[1.1.1]: https://github.com/NepNepFFXIV/core-framework/compare/v1.1.0...v1.1.1
|
|
126
141
|
[1.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.1.1...v1.2.0
|
|
127
|
-
[
|
|
142
|
+
[1.3.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.2.0...v1.3.0
|
|
143
|
+
[unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v1.3.0...HEAD
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: core-framework
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Core framework package (import as core_framework)
|
|
5
|
+
Project-URL: Homepage, https://github.com/NepNepFFXIV/core-framework
|
|
6
|
+
Project-URL: Repository, https://github.com/NepNepFFXIV/core-framework
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.14
|
|
10
|
+
Requires-Dist: alembic>=1.18.4
|
|
11
|
+
Requires-Dist: arq>=0.28.0
|
|
12
|
+
Requires-Dist: async-lru>=2.1.0
|
|
13
|
+
Requires-Dist: asyncer>=0.0.14
|
|
14
|
+
Requires-Dist: asyncpg>=0.31.0
|
|
15
|
+
Requires-Dist: brotli-asgi>=1.6.0
|
|
16
|
+
Requires-Dist: cashews[redis]>=7.5.0
|
|
17
|
+
Requires-Dist: device-detector>=6.2.0
|
|
18
|
+
Requires-Dist: fastapi[standard]>=0.136.1
|
|
19
|
+
Requires-Dist: firebase-admin>=7.4.0
|
|
20
|
+
Requires-Dist: httpx[http2]>=0.28.1
|
|
21
|
+
Requires-Dist: itsdangerous>=2.2.0
|
|
22
|
+
Requires-Dist: logfire[asyncpg,fastapi,httpx,redis]>=4.32.1
|
|
23
|
+
Requires-Dist: mashumaro[orjson]>=3.20
|
|
24
|
+
Requires-Dist: orjson>=3.11.7
|
|
25
|
+
Requires-Dist: python-ulid>=3.1.0
|
|
26
|
+
Requires-Dist: structlog>=25.5.0
|
|
27
|
+
Requires-Dist: tenacity>=9.1.4
|
|
28
|
+
Provides-Extra: testing
|
|
29
|
+
Requires-Dist: asgi-lifespan>=2.1.0; extra == 'testing'
|
|
30
|
+
Requires-Dist: pytest-xdist>=3.6.0; extra == 'testing'
|
|
31
|
+
Requires-Dist: pytest>=9.0.3; extra == 'testing'
|
|
32
|
+
Requires-Dist: testcontainers[postgres,redis]>=4.13.2; extra == 'testing'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Core framework
|
|
36
|
+
|
|
37
|
+
Install from PyPI as **`core-framework`**, import **`core_framework`**. MIT — [LICENSE](LICENSE). [Repository](https://github.com/NepNepFFXIV/core-framework), [PyPI](https://pypi.org/project/core-framework/).
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv add core-framework
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Local development
|
|
46
|
+
|
|
47
|
+
Use **Python 3.14**. Install **uv** and **Docker**.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
docker compose -f docker-compose.dev.yaml up -d
|
|
51
|
+
uv sync --locked --all-extras --dev
|
|
52
|
+
uv run cf-alembic
|
|
53
|
+
make run
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Firebase:** copy **`firebase_config.example.json`** to **`firebase_config.json`** at the repo root and replace with your Firebase project’s **service account** JSON from the console.
|
|
57
|
+
|
|
58
|
+
For **`make test`**, use the Firebase CLI (Auth emulator). Stop DB/Redis: **`docker compose -f docker-compose.dev.yaml down`**.
|
|
59
|
+
|
|
60
|
+
## Hosts
|
|
61
|
+
|
|
62
|
+
In your host app, load **`Settings`** or a **`Settings`** subclass from **`core_framework.core.settings`**, pass it to **`init_app`** from **`core_framework.main`**, then wire host-specific dependencies, exception handlers, and routers.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from core_framework.main import init_app
|
|
66
|
+
from fastapi import FastAPI
|
|
67
|
+
|
|
68
|
+
from myapp.settings import HostSettings, load_default_settings
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def build_app(settings: HostSettings | None = None) -> FastAPI:
|
|
72
|
+
resolved = settings if settings is not None else load_default_settings()
|
|
73
|
+
app = init_app(resolved)
|
|
74
|
+
|
|
75
|
+
from myapp.bootstrap import configure_dependencies
|
|
76
|
+
from myapp.exception_handlers import setup_exception_handlers
|
|
77
|
+
|
|
78
|
+
configure_dependencies(runtime=app.state.core_runtime)
|
|
79
|
+
setup_exception_handlers(app)
|
|
80
|
+
|
|
81
|
+
from myapp.api.router import router
|
|
82
|
+
|
|
83
|
+
app.include_router(router)
|
|
84
|
+
return app
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
From the host repository root, run **`uv run cf-alembic`**. See [core-framework-migration](docs/core-framework-migration.md).
|
|
88
|
+
|
|
89
|
+
## Host pytest
|
|
90
|
+
|
|
91
|
+
In your host repository, add the **`core-framework[testing]`** extra to dev or test dependencies. In **`tests/conftest.py`**, register **`pytest_core_framework_config`** (returns **`TestConfig`**) and a session-scoped **`anyio_backend`** fixture that returns **`"asyncio"`**.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import pytest
|
|
95
|
+
|
|
96
|
+
from core_framework.testing import TestConfig
|
|
97
|
+
|
|
98
|
+
from myapp.settings import load_default_settings
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def pytest_core_framework_config() -> TestConfig:
|
|
102
|
+
from myapp.main import build_app
|
|
103
|
+
|
|
104
|
+
return TestConfig(
|
|
105
|
+
settings_loader=load_default_settings,
|
|
106
|
+
app_factory=build_app,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@pytest.fixture(scope="session")
|
|
111
|
+
def anyio_backend() -> str:
|
|
112
|
+
return "asyncio"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
See [Package API — Testing](docs/package-api.md#testing-pytest-plugin) and [testing plugin design](docs/testing-plugin-design.md).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Core framework
|
|
2
|
+
|
|
3
|
+
Install from PyPI as **`core-framework`**, import **`core_framework`**. MIT — [LICENSE](LICENSE). [Repository](https://github.com/NepNepFFXIV/core-framework), [PyPI](https://pypi.org/project/core-framework/).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv add core-framework
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Local development
|
|
12
|
+
|
|
13
|
+
Use **Python 3.14**. Install **uv** and **Docker**.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
docker compose -f docker-compose.dev.yaml up -d
|
|
17
|
+
uv sync --locked --all-extras --dev
|
|
18
|
+
uv run cf-alembic
|
|
19
|
+
make run
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Firebase:** copy **`firebase_config.example.json`** to **`firebase_config.json`** at the repo root and replace with your Firebase project’s **service account** JSON from the console.
|
|
23
|
+
|
|
24
|
+
For **`make test`**, use the Firebase CLI (Auth emulator). Stop DB/Redis: **`docker compose -f docker-compose.dev.yaml down`**.
|
|
25
|
+
|
|
26
|
+
## Hosts
|
|
27
|
+
|
|
28
|
+
In your host app, load **`Settings`** or a **`Settings`** subclass from **`core_framework.core.settings`**, pass it to **`init_app`** from **`core_framework.main`**, then wire host-specific dependencies, exception handlers, and routers.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from core_framework.main import init_app
|
|
32
|
+
from fastapi import FastAPI
|
|
33
|
+
|
|
34
|
+
from myapp.settings import HostSettings, load_default_settings
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_app(settings: HostSettings | None = None) -> FastAPI:
|
|
38
|
+
resolved = settings if settings is not None else load_default_settings()
|
|
39
|
+
app = init_app(resolved)
|
|
40
|
+
|
|
41
|
+
from myapp.bootstrap import configure_dependencies
|
|
42
|
+
from myapp.exception_handlers import setup_exception_handlers
|
|
43
|
+
|
|
44
|
+
configure_dependencies(runtime=app.state.core_runtime)
|
|
45
|
+
setup_exception_handlers(app)
|
|
46
|
+
|
|
47
|
+
from myapp.api.router import router
|
|
48
|
+
|
|
49
|
+
app.include_router(router)
|
|
50
|
+
return app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
From the host repository root, run **`uv run cf-alembic`**. See [core-framework-migration](docs/core-framework-migration.md).
|
|
54
|
+
|
|
55
|
+
## Host pytest
|
|
56
|
+
|
|
57
|
+
In your host repository, add the **`core-framework[testing]`** extra to dev or test dependencies. In **`tests/conftest.py`**, register **`pytest_core_framework_config`** (returns **`TestConfig`**) and a session-scoped **`anyio_backend`** fixture that returns **`"asyncio"`**.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import pytest
|
|
61
|
+
|
|
62
|
+
from core_framework.testing import TestConfig
|
|
63
|
+
|
|
64
|
+
from myapp.settings import load_default_settings
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def pytest_core_framework_config() -> TestConfig:
|
|
68
|
+
from myapp.main import build_app
|
|
69
|
+
|
|
70
|
+
return TestConfig(
|
|
71
|
+
settings_loader=load_default_settings,
|
|
72
|
+
app_factory=build_app,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.fixture(scope="session")
|
|
77
|
+
def anyio_backend() -> str:
|
|
78
|
+
return "asyncio"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
See [Package API — Testing](docs/package-api.md#testing-pytest-plugin) and [testing plugin design](docs/testing-plugin-design.md).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
5
|
from firebase_admin import credentials, get_app, initialize_app
|
|
@@ -16,6 +17,12 @@ def init_firebase(settings: Settings) -> None:
|
|
|
16
17
|
cred: credentials.Base | None = None
|
|
17
18
|
options: dict[str, Any] | None = {"projectId": "test"}
|
|
18
19
|
if not os.getenv("FIREBASE_AUTH_EMULATOR_HOST"):
|
|
19
|
-
|
|
20
|
+
path = Path(settings.PROJECT_ROOT) / "firebase_config.json"
|
|
21
|
+
if not path.is_file():
|
|
22
|
+
raise FileNotFoundError(
|
|
23
|
+
f"Missing Firebase service account file: {path}. "
|
|
24
|
+
"Copy firebase_config.example.json to firebase_config.json and paste your project's service account JSON."
|
|
25
|
+
)
|
|
26
|
+
cred = credentials.Certificate(path)
|
|
20
27
|
options = None
|
|
21
28
|
initialize_app(cred, options)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:18
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_USER: postgres
|
|
6
|
+
POSTGRES_PASSWORD: postgres
|
|
7
|
+
POSTGRES_DB: postgres
|
|
8
|
+
ports:
|
|
9
|
+
- "6969:5432"
|
|
10
|
+
healthcheck:
|
|
11
|
+
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
|
12
|
+
interval: 5s
|
|
13
|
+
timeout: 3s
|
|
14
|
+
retries: 10
|
|
15
|
+
|
|
16
|
+
redis:
|
|
17
|
+
image: redis:8
|
|
18
|
+
ports:
|
|
19
|
+
- "6379:6379"
|
|
20
|
+
healthcheck:
|
|
21
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
22
|
+
interval: 5s
|
|
23
|
+
timeout: 3s
|
|
24
|
+
retries: 10
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Backlog
|
|
2
|
+
|
|
3
|
+
- ETag
|
|
4
|
+
- py-spy
|
|
5
|
+
- Stale-while-revalidate
|
|
6
|
+
- Backpressure — uvicorn settings (https://uvicorn.dev/settings/)
|
|
7
|
+
- Backpressure — route level with semaphore
|
|
8
|
+
- https://github.com/rennf93/fastapi-guard
|
|
9
|
+
- https://github.com/TypeError/secure
|
|
10
|
+
- Outbox pattern
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "service_account",
|
|
3
|
+
"project_id": "your-firebase-project-id",
|
|
4
|
+
"private_key_id": "replace-me",
|
|
5
|
+
"private_key": "<paste the full private_key string from the service account JSON Firebase downloads (PEM, not shown here)>",
|
|
6
|
+
"client_email": "firebase-adminsdk-example@your-firebase-project-id.iam.gserviceaccount.com",
|
|
7
|
+
"client_id": "000000000000000000000",
|
|
8
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
9
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
10
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
11
|
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-example%40your-firebase-project-id.iam.gserviceaccount.com",
|
|
12
|
+
"universe_domain": "googleapis.com"
|
|
13
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*
|
core_framework-1.2.0/PKG-INFO
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: core-framework
|
|
3
|
-
Version: 1.2.0
|
|
4
|
-
Summary: Core framework package (import as core_framework)
|
|
5
|
-
Project-URL: Homepage, https://github.com/NepNepFFXIV/core-framework
|
|
6
|
-
Project-URL: Repository, https://github.com/NepNepFFXIV/core-framework
|
|
7
|
-
License-Expression: MIT
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Requires-Python: >=3.14
|
|
10
|
-
Requires-Dist: alembic>=1.18.4
|
|
11
|
-
Requires-Dist: arq>=0.28.0
|
|
12
|
-
Requires-Dist: async-lru>=2.1.0
|
|
13
|
-
Requires-Dist: asyncer>=0.0.14
|
|
14
|
-
Requires-Dist: asyncpg>=0.31.0
|
|
15
|
-
Requires-Dist: brotli-asgi>=1.6.0
|
|
16
|
-
Requires-Dist: cashews[redis]>=7.5.0
|
|
17
|
-
Requires-Dist: device-detector>=6.2.0
|
|
18
|
-
Requires-Dist: fastapi[standard]>=0.136.1
|
|
19
|
-
Requires-Dist: firebase-admin>=7.4.0
|
|
20
|
-
Requires-Dist: httpx[http2]>=0.28.1
|
|
21
|
-
Requires-Dist: itsdangerous>=2.2.0
|
|
22
|
-
Requires-Dist: logfire[asyncpg,fastapi,httpx,redis]>=4.32.1
|
|
23
|
-
Requires-Dist: mashumaro[orjson]>=3.20
|
|
24
|
-
Requires-Dist: orjson>=3.11.7
|
|
25
|
-
Requires-Dist: python-ulid>=3.1.0
|
|
26
|
-
Requires-Dist: structlog>=25.5.0
|
|
27
|
-
Requires-Dist: tenacity>=9.1.4
|
|
28
|
-
Provides-Extra: testing
|
|
29
|
-
Requires-Dist: asgi-lifespan>=2.1.0; extra == 'testing'
|
|
30
|
-
Requires-Dist: pytest-xdist>=3.6.0; extra == 'testing'
|
|
31
|
-
Requires-Dist: pytest>=9.0.3; extra == 'testing'
|
|
32
|
-
Requires-Dist: testcontainers[postgres,redis]>=4.13.2; extra == 'testing'
|
|
33
|
-
Description-Content-Type: text/markdown
|
|
34
|
-
|
|
35
|
-
# Core framework
|
|
36
|
-
|
|
37
|
-
Reusable backend core published as the Python distribution **`core-framework`** (`pyproject.toml`). Import the implementation as **`core_framework`** (see [Core framework migration](docs/core-framework-migration.md), [Package API](docs/package-api.md), and [CHANGELOG](CHANGELOG.md) for version history).
|
|
38
|
-
|
|
39
|
-
**Repository:** [github.com/NepNepFFXIV/core-framework](https://github.com/NepNepFFXIV/core-framework)
|
|
40
|
-
|
|
41
|
-
**License:** MIT — see [LICENSE](LICENSE).
|
|
42
|
-
|
|
43
|
-
## Install
|
|
44
|
-
|
|
45
|
-
Pin **`core-framework`** from **Git** (use a **tag** or commit for reproducible builds):
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
uv add "core-framework @ git+https://github.com/NepNepFFXIV/core-framework.git@v1.2.0"
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
pip install "core-framework @ git+https://github.com/NepNepFFXIV/core-framework.git@v1.2.0"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
After it resolves, load **`Settings`**, call **`init_app(settings)`** from **`core_framework.main`** in your app factory, and apply bundled core schema from the host project root with **`uv run cf-alembic`** (see [Host integration](#host-integration) and [Package API](docs/package-api.md)).
|
|
56
|
-
|
|
57
|
-
To **hack on this repo**: clone it, run **`uv sync`** (or **`pip install -e .`**), keep a local **`config.toml`**, run **`uv run cf-alembic`**, then **`make run`** or serve **`core_framework.asgi:app`**.
|
|
58
|
-
|
|
59
|
-
## Host integration
|
|
60
|
-
|
|
61
|
-
Hosts depend on **`core-framework`**, own config and the DB, and import **`core_framework`**. Details: [Core framework migration](docs/core-framework-migration.md).
|
|
62
|
-
|
|
63
|
-
1. **Dependency** — Pin **`core-framework`** (path, Git tag, or index) and lock deploys (**`uv.lock`**, etc.).
|
|
64
|
-
1. **Settings** — Use **`Settings`** from **`core_framework.core.settings`**. Default loading uses **`config.toml`** and **environment variables**; **env wins** over TOML for the same field (see [Architecture decisions](docs/architecture-decisions.md#configuration-toml-primary-environment-overrides)). Subclass **only** if you need extra host-only fields; otherwise **`Settings()`** / **`model_validate`** is enough. Subclasses may need **`model_config`** tweaks (e.g. **`toml_file`**) if paths differ.
|
|
65
|
-
1. **App** — Call **`init_app(settings)`** from **`core_framework.main`** inside the host’s own **`main.py`** (or app factory). Importing **`core_framework.main`** has no side effects; only **`init_app(settings)`** runs the bootstrap. Do **not** import **`core_framework.asgi`** — that module exists so **`fastapi dev`** / **`uvicorn`** can serve the library’s own default-settings app standalone, and it eagerly loads **`core-framework`**’s own **`config.toml`** at import time. Do not mount **`router`** alone without the same bootstrap — see [Package API](docs/package-api.md).
|
|
66
|
-
1. **Database** — Apply core schema from the host root (where **`config.toml`** loads): **`uv run cf-alembic`**. Host-only tables: host migrations; order with core ([Database and Alembic](docs/core-framework-migration.md#database-and-alembic)).
|
|
67
|
-
1. **Worker** — **`WorkerSettings`** class in **`core_framework.worker.main`** (run with **`arq core_framework.worker.main.WorkerSettings`**). Bootstrap (**`configure_application_dependencies`**, pool connects, **`init_cashews_backend`**) runs in **`on_startup`**, not at import time. ARQ exposes **`ctx["redis"]`** for enqueue in cron handlers; **`CoreRuntime.redis_queue`** is a separate connected pool for runtime parity with the API — see **[Architecture — ARQ and CoreRuntime queue clients](docs/architecture.md#arq-and-coreruntime-queue-clients)**.
|
|
68
|
-
1. **API surface** — Only [Package API](docs/package-api.md) imports are stable; see [CHANGELOG](CHANGELOG.md).
|
|
69
|
-
1. **Upgrading / new `Settings` fields** — When you bump **`core-framework`**, read **[CHANGELOG](CHANGELOG.md)** for that release (and any required migration notes). New **`Settings`** keys are announced there: **required** keys must be added to the host’s **`config.toml`** / env before deploy; **optional** keys list defaults. Version bumps follow SemVer in **`pyproject.toml`** so hosts can judge impact.
|
|
70
|
-
|
|
71
|
-
## Testing in host projects
|
|
72
|
-
|
|
73
|
-
Use the **`core-framework[testing]`** optional extra in the host’s **dev** or **test** dependency group so pytest gets **`testcontainers`**, **`pytest-xdist`**, **`asgi-lifespan`**, and a compatible **`pytest`**. The package registers a pytest plugin automatically; you do **not** need **`pytest_plugins = (...)`** for **`core_framework.testing`**.
|
|
74
|
-
|
|
75
|
-
1. Add the extra (example with **`uv`**): in **`pyproject.toml`**, e.g. **`core-framework = { path = "…", extras = ["testing"] }`** or **`core-framework[testing]`** in **`[dependency-groups].dev`**.
|
|
76
|
-
1. In the host’s **root** **`tests/conftest.py`**, implement **`pytest_core_framework_config()`** returning **`TestConfig`** from **`core_framework.testing`** — pass **`settings_loader`** and **`app_factory`** (usually your **`load_default_settings`** and **`init_app`**). Optionally set **`alembic_root`**, **`firebase_project_id`**, **`include_arq_fixtures`**, etc.
|
|
77
|
-
1. In the same **`conftest.py`**, define a **session-scoped** **`anyio_backend`** fixture that returns **`"asyncio"`** (required so session-scoped async fixtures work with anyio’s pytest integration).
|
|
78
|
-
|
|
79
|
-
Minimal shape (adjust paths and flags to your host):
|
|
80
|
-
|
|
81
|
-
```python
|
|
82
|
-
from pathlib import Path
|
|
83
|
-
|
|
84
|
-
import pytest
|
|
85
|
-
|
|
86
|
-
from core_framework.testing import TestConfig
|
|
87
|
-
from my_host.settings import load_default_settings
|
|
88
|
-
from my_host.main import init_app
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def pytest_core_framework_config() -> TestConfig:
|
|
92
|
-
return TestConfig(
|
|
93
|
-
settings_loader=load_default_settings,
|
|
94
|
-
app_factory=init_app,
|
|
95
|
-
# alembic_root=Path(__file__).resolve().parent.parent / "alembic",
|
|
96
|
-
# firebase_project_id="test",
|
|
97
|
-
# include_arq_fixtures=False,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@pytest.fixture(scope="session")
|
|
102
|
-
def anyio_backend() -> str:
|
|
103
|
-
return "asyncio"
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
**Stable API and fixture list:** **[Package API — Testing](docs/package-api.md#testing-pytest-plugin)**. **Design notes (xdist, Firebase hybrid, risks):** **[Testing plugin design](docs/testing-plugin-design.md)**.
|
|
107
|
-
|
|
108
|
-
## Commands
|
|
109
|
-
|
|
110
|
-
- `make run` — run the standalone core app locally.
|
|
111
|
-
- `make alembic` — run bundled core migrations via `cf-alembic`.
|
|
112
|
-
- `make ruff` — lint and format.
|
|
113
|
-
- `make ty` — type check.
|
|
114
|
-
- `make vulture` — scan for unused code.
|
|
115
|
-
- `make test` — run tests with the Firebase auth emulator.
|
|
116
|
-
|
|
117
|
-
## Adding Domains
|
|
118
|
-
|
|
119
|
-
For a new core domain with its own schema name, use `.cursor/skills/add-domain/SKILL.md`.
|
|
120
|
-
The initial scaffold creates settings, an empty Alembic tree, domain class shells, and bootstrap wiring; migration revisions and `ALEMBIC_DOMAINS` are added later with real DDL.
|
|
121
|
-
|
|
122
|
-
## TODO
|
|
123
|
-
|
|
124
|
-
- ETag
|
|
125
|
-
- py-spy
|
|
126
|
-
- Stale-while-revalidate
|
|
127
|
-
- Backpressure - uvicorn settings https://uvicorn.dev/settings/
|
|
128
|
-
- Backpressure - route level with semaphore
|
|
129
|
-
- https://github.com/rennf93/fastapi-guard
|
|
130
|
-
- https://github.com/TypeError/secure
|
|
131
|
-
- Outbox pattern
|
core_framework-1.2.0/README.md
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Core framework
|
|
2
|
-
|
|
3
|
-
Reusable backend core published as the Python distribution **`core-framework`** (`pyproject.toml`). Import the implementation as **`core_framework`** (see [Core framework migration](docs/core-framework-migration.md), [Package API](docs/package-api.md), and [CHANGELOG](CHANGELOG.md) for version history).
|
|
4
|
-
|
|
5
|
-
**Repository:** [github.com/NepNepFFXIV/core-framework](https://github.com/NepNepFFXIV/core-framework)
|
|
6
|
-
|
|
7
|
-
**License:** MIT — see [LICENSE](LICENSE).
|
|
8
|
-
|
|
9
|
-
## Install
|
|
10
|
-
|
|
11
|
-
Pin **`core-framework`** from **Git** (use a **tag** or commit for reproducible builds):
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
uv add "core-framework @ git+https://github.com/NepNepFFXIV/core-framework.git@v1.2.0"
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
pip install "core-framework @ git+https://github.com/NepNepFFXIV/core-framework.git@v1.2.0"
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
After it resolves, load **`Settings`**, call **`init_app(settings)`** from **`core_framework.main`** in your app factory, and apply bundled core schema from the host project root with **`uv run cf-alembic`** (see [Host integration](#host-integration) and [Package API](docs/package-api.md)).
|
|
22
|
-
|
|
23
|
-
To **hack on this repo**: clone it, run **`uv sync`** (or **`pip install -e .`**), keep a local **`config.toml`**, run **`uv run cf-alembic`**, then **`make run`** or serve **`core_framework.asgi:app`**.
|
|
24
|
-
|
|
25
|
-
## Host integration
|
|
26
|
-
|
|
27
|
-
Hosts depend on **`core-framework`**, own config and the DB, and import **`core_framework`**. Details: [Core framework migration](docs/core-framework-migration.md).
|
|
28
|
-
|
|
29
|
-
1. **Dependency** — Pin **`core-framework`** (path, Git tag, or index) and lock deploys (**`uv.lock`**, etc.).
|
|
30
|
-
1. **Settings** — Use **`Settings`** from **`core_framework.core.settings`**. Default loading uses **`config.toml`** and **environment variables**; **env wins** over TOML for the same field (see [Architecture decisions](docs/architecture-decisions.md#configuration-toml-primary-environment-overrides)). Subclass **only** if you need extra host-only fields; otherwise **`Settings()`** / **`model_validate`** is enough. Subclasses may need **`model_config`** tweaks (e.g. **`toml_file`**) if paths differ.
|
|
31
|
-
1. **App** — Call **`init_app(settings)`** from **`core_framework.main`** inside the host’s own **`main.py`** (or app factory). Importing **`core_framework.main`** has no side effects; only **`init_app(settings)`** runs the bootstrap. Do **not** import **`core_framework.asgi`** — that module exists so **`fastapi dev`** / **`uvicorn`** can serve the library’s own default-settings app standalone, and it eagerly loads **`core-framework`**’s own **`config.toml`** at import time. Do not mount **`router`** alone without the same bootstrap — see [Package API](docs/package-api.md).
|
|
32
|
-
1. **Database** — Apply core schema from the host root (where **`config.toml`** loads): **`uv run cf-alembic`**. Host-only tables: host migrations; order with core ([Database and Alembic](docs/core-framework-migration.md#database-and-alembic)).
|
|
33
|
-
1. **Worker** — **`WorkerSettings`** class in **`core_framework.worker.main`** (run with **`arq core_framework.worker.main.WorkerSettings`**). Bootstrap (**`configure_application_dependencies`**, pool connects, **`init_cashews_backend`**) runs in **`on_startup`**, not at import time. ARQ exposes **`ctx["redis"]`** for enqueue in cron handlers; **`CoreRuntime.redis_queue`** is a separate connected pool for runtime parity with the API — see **[Architecture — ARQ and CoreRuntime queue clients](docs/architecture.md#arq-and-coreruntime-queue-clients)**.
|
|
34
|
-
1. **API surface** — Only [Package API](docs/package-api.md) imports are stable; see [CHANGELOG](CHANGELOG.md).
|
|
35
|
-
1. **Upgrading / new `Settings` fields** — When you bump **`core-framework`**, read **[CHANGELOG](CHANGELOG.md)** for that release (and any required migration notes). New **`Settings`** keys are announced there: **required** keys must be added to the host’s **`config.toml`** / env before deploy; **optional** keys list defaults. Version bumps follow SemVer in **`pyproject.toml`** so hosts can judge impact.
|
|
36
|
-
|
|
37
|
-
## Testing in host projects
|
|
38
|
-
|
|
39
|
-
Use the **`core-framework[testing]`** optional extra in the host’s **dev** or **test** dependency group so pytest gets **`testcontainers`**, **`pytest-xdist`**, **`asgi-lifespan`**, and a compatible **`pytest`**. The package registers a pytest plugin automatically; you do **not** need **`pytest_plugins = (...)`** for **`core_framework.testing`**.
|
|
40
|
-
|
|
41
|
-
1. Add the extra (example with **`uv`**): in **`pyproject.toml`**, e.g. **`core-framework = { path = "…", extras = ["testing"] }`** or **`core-framework[testing]`** in **`[dependency-groups].dev`**.
|
|
42
|
-
1. In the host’s **root** **`tests/conftest.py`**, implement **`pytest_core_framework_config()`** returning **`TestConfig`** from **`core_framework.testing`** — pass **`settings_loader`** and **`app_factory`** (usually your **`load_default_settings`** and **`init_app`**). Optionally set **`alembic_root`**, **`firebase_project_id`**, **`include_arq_fixtures`**, etc.
|
|
43
|
-
1. In the same **`conftest.py`**, define a **session-scoped** **`anyio_backend`** fixture that returns **`"asyncio"`** (required so session-scoped async fixtures work with anyio’s pytest integration).
|
|
44
|
-
|
|
45
|
-
Minimal shape (adjust paths and flags to your host):
|
|
46
|
-
|
|
47
|
-
```python
|
|
48
|
-
from pathlib import Path
|
|
49
|
-
|
|
50
|
-
import pytest
|
|
51
|
-
|
|
52
|
-
from core_framework.testing import TestConfig
|
|
53
|
-
from my_host.settings import load_default_settings
|
|
54
|
-
from my_host.main import init_app
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def pytest_core_framework_config() -> TestConfig:
|
|
58
|
-
return TestConfig(
|
|
59
|
-
settings_loader=load_default_settings,
|
|
60
|
-
app_factory=init_app,
|
|
61
|
-
# alembic_root=Path(__file__).resolve().parent.parent / "alembic",
|
|
62
|
-
# firebase_project_id="test",
|
|
63
|
-
# include_arq_fixtures=False,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@pytest.fixture(scope="session")
|
|
68
|
-
def anyio_backend() -> str:
|
|
69
|
-
return "asyncio"
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Stable API and fixture list:** **[Package API — Testing](docs/package-api.md#testing-pytest-plugin)**. **Design notes (xdist, Firebase hybrid, risks):** **[Testing plugin design](docs/testing-plugin-design.md)**.
|
|
73
|
-
|
|
74
|
-
## Commands
|
|
75
|
-
|
|
76
|
-
- `make run` — run the standalone core app locally.
|
|
77
|
-
- `make alembic` — run bundled core migrations via `cf-alembic`.
|
|
78
|
-
- `make ruff` — lint and format.
|
|
79
|
-
- `make ty` — type check.
|
|
80
|
-
- `make vulture` — scan for unused code.
|
|
81
|
-
- `make test` — run tests with the Firebase auth emulator.
|
|
82
|
-
|
|
83
|
-
## Adding Domains
|
|
84
|
-
|
|
85
|
-
For a new core domain with its own schema name, use `.cursor/skills/add-domain/SKILL.md`.
|
|
86
|
-
The initial scaffold creates settings, an empty Alembic tree, domain class shells, and bootstrap wiring; migration revisions and `ALEMBIC_DOMAINS` are added later with real DDL.
|
|
87
|
-
|
|
88
|
-
## TODO
|
|
89
|
-
|
|
90
|
-
- ETag
|
|
91
|
-
- py-spy
|
|
92
|
-
- Stale-while-revalidate
|
|
93
|
-
- Backpressure - uvicorn settings https://uvicorn.dev/settings/
|
|
94
|
-
- Backpressure - route level with semaphore
|
|
95
|
-
- https://github.com/rennf93/fastapi-guard
|
|
96
|
-
- https://github.com/TypeError/secure
|
|
97
|
-
- Outbox pattern
|
|
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
|
{core_framework-1.2.0 → core_framework-1.3.0}/.cursor/rules/domain-repository-exceptions.mdc
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
|