fastapi-startkit 0.49.0__tar.gz → 0.51.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.
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/PKG-INFO +61 -1
- fastapi_startkit-0.51.0/README.md +58 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/pyproject.toml +3 -2
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/application.py +2 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/container/container.py +4 -7
- fastapi_startkit-0.51.0/src/fastapi_startkit/events/__init__.py +13 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/events/dispatcher.py +141 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/events/fake.py +79 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/events/helpers.py +19 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/events/listener.py +16 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/events/provider.py +31 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/facades/Event.py +5 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/facades/Event.pyi +41 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/__init__.py +1 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/fastapi/__init__.py +15 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/fastapi/events.py +29 -0
- fastapi_startkit-0.51.0/src/fastapi_startkit/fastapi/middleware.py +54 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/providers/fastapi_provider.py +13 -1
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/connections/factory.py +35 -14
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/attribute.py +5 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/builder.py +53 -1
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/model.py +37 -4
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/BelongsToMany.py +28 -45
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/MorphMany.py +23 -21
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/MorphOne.py +21 -17
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/MorphToMany.py +12 -8
- fastapi_startkit-0.51.0/src/fastapi_startkit/py.typed +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/providers/provider.py +3 -2
- fastapi_startkit-0.49.0/src/fastapi_startkit/fastapi/__init__.py +0 -6
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/.DS_Store +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/agent.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/audio.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/audio_factory.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/config/ai.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/config/config.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/decorators.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/document.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/fakes.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/image.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/image_factory.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/lab.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/model_builder.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/pipeline.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/providers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/providers/ai_provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/response.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/runner.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/ai/testing.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/auth.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/channels.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/config.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/drivers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/drivers/log_driver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/drivers/reverb_driver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/event.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/helpers.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/manager.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/reverb/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/reverb/channel_manager.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/reverb/connection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/reverb/server.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/stubs/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/broadcasting/stubs/channels.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/carbon/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/carbon/carbon.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/config/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/config/app.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/configuration/Configuration.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/configuration/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/configuration/config.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/configuration/helpers.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/configuration/providers/ConfigurationProvider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/configuration/providers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/console/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/console/application.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/console/can_override_config.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/console/can_override_default_options.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/console/command.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/console/publish_command.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/container/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/environment/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/environment/environment.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/exceptions/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/exceptions/exceptions.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/exceptions/handler.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/AI.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/AI.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Cache.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Config.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Config.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Dump.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Dump.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Facade.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Gate.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Gate.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Hash.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Hash.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Loader.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Loader.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Mail.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Mail.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Notification.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Notification.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Queue.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Queue.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/RateLimiter.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/RateLimiter.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Request.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Request.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Response.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Response.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Session.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Session.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Url.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Url.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/View.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/View.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Vite.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/facades/Vite.pyi +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/commands/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/commands/serve_command.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/config/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/config/fastapi.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/exceptions.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/requests/model.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/routers/router.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/testing/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/testing/assertable_json.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/testing/test_case.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/fastapi/testing/test_response.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/foundation/app_provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/constant.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/context.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/inertia.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/middleware.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/props/props.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/inertia/provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/jsonapi/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/jsonapi/response.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/loader/Loader.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/loader/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/ChannelFactory.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/BaseChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/DailyChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/MultiBaseChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/SingleChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/SlackChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/StackChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/SyslogChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/TerminalChannel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/channels/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/config/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/config/channels.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/config/logging.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/drivers/BaseDriver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/drivers/LogSingleDriver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/drivers/LogSlackDriver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/drivers/LogSyslogDriver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/drivers/LogTerminalDriver.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/drivers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/factory.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/file.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/handler.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/listeners.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/logger.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/managers/LoggingManager.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/managers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/providers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/logging/providers/log_provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/collection/Collection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/collection/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/DBMigrateCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/DBSeedCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MakeMigrationCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MakeModelCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MakeModelDocstringCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MakeObserverCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MakeSeedCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MigrateFreshCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MigrateRefreshCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MigrateResetCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MigrateRollbackCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/MigrateStatusCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/ShellCommand.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/stubs/create_migration.stub +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/stubs/create_seed.stub +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/stubs/model.stub +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/stubs/observer.stub +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/commands/stubs/table_migration.stub +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/config/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/config/config.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/config/database.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/connections/connection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/connections/manager.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/connections/mysql_connection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/connections/postgres_connection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/connections/sqlite_connection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/exceptions.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/expressions/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/expressions/expressions.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/facades/DB.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/facades/Schema.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/facades/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/factory/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/factory/factory.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/helpers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/helpers/misc.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/migrations/Migration.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/migrations/Migrator.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/migrations/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/MigrationModel.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/caster.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/fields.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/observer.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/pivot.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/registry.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/models/relationship.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/observers/ObservesEvents.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/observers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/pagination/BasePaginator.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/pagination/LengthAwarePaginator.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/pagination/SimplePaginator.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/pagination/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/providers/DatabaseProvider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/providers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/EagerLoadMixin.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/EagerRelation.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/grammars/BaseGrammar.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/grammars/MSSQLGrammar.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/grammars/MySQLGrammar.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/grammars/PostgresGrammar.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/grammars/SQLiteGrammar.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/grammars/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/processors/MSSQLPostProcessor.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/processors/MySQLPostProcessor.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/processors/PostgresPostProcessor.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/processors/SQLitePostProcessor.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/processors/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/query/support.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/BaseRelationship.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/BelongsTo.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/HasMany.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/HasManyThrough.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/HasOne.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/HasOneThrough.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/MorphTo.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/relationships/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/Blueprint.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/Column.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/ColumnDiff.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/Constraint.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/ForeignKeyConstraint.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/Index.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/Table.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/TableDiff.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/platforms/MSSQLPlatform.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/platforms/MySQLPlatform.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/platforms/Platform.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/platforms/PostgresPlatform.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/platforms/SQLitePlatform.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/platforms/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/schema/schema.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/seeders/Seeder.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/seeders/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/stubs/create-migration.html +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/stubs/table-migration.html +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/testing/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm/testing/transaction.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/.gitignore +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/CACHEDIR.TAG +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/README.md +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/v/cache/lastfailed +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/v/cache/nodeids +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/argument.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/prompt.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/protocol.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/providers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/providers/mcp_provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/request.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/resource.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/response.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/server.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/mcp/tool.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/process/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/process/exception.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/process/fake.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/process/process.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/process/result.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/adapters/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/adapters/base.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/adapters/claude.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/adapters/gemini.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/commands/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/commands/skills.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/parser.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/registry.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/database/SKILL.md +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/fastapi/SKILL.md +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/inertia/SKILL.md +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/config/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/config/storage.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/data/mime.types +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/drivers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/drivers/fake.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/drivers/local.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/drivers/s3.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/file.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/filestream.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/helper.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/providers/provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/storage/storage.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/collection.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/dataclass.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/providers/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/string.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/support/uri.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/testing/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/testing/test_case.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/utils/structures.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/__init__.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/config/vite.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/exceptions.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/providers/provider.py +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/stubs/package.json +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/stubs/resources/css/app.css +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/stubs/resources/js/app.ts +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/stubs/resources/templates/index.html +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/stubs/tsconfig.json +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/stubs/vite.config.ts +0 -0
- {fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/vite/vite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fastapi-startkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.51.0
|
|
4
4
|
Summary: A modular, provider-driven framework for building async Python web applications with FastAPI — bundling a service container, configuration system, an async-first ORM, facades, and a Cleo-powered console.
|
|
5
5
|
Keywords: fastapi,framework,async,web,asgi,orm,sqlalchemy,pydantic,ioc-container,dependency-injection,service-provider,cli,console,configuration,microservices,starter-kit,boilerplate
|
|
6
6
|
Author: Bedram Tamang
|
|
@@ -46,3 +46,63 @@ Provides-Extra: mysql
|
|
|
46
46
|
Provides-Extra: postgres
|
|
47
47
|
Provides-Extra: sqlite
|
|
48
48
|
Provides-Extra: vite
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# FastAPI Startkit
|
|
52
|
+
|
|
53
|
+
[](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework)
|
|
54
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
55
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
56
|
+
|
|
57
|
+
A modular, provider-driven framework for building Python applications with FastAPI. It bundles a
|
|
58
|
+
service container, configuration system, an async-first ORM, facades, and a Cleo-powered console into
|
|
59
|
+
a single cohesive foundation.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install fastapi-startkit
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Optional extras enable additional capabilities:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
fastapi-startkit[fastapi] # FastAPI + Starlette
|
|
71
|
+
fastapi-startkit[database] # SQLAlchemy async ORM
|
|
72
|
+
fastapi-startkit[vite] # Jinja2 for Vite integration
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- **Service container** — IoC container with `bind`/`make`/`resolve` auto-wiring and lifecycle hooks.
|
|
78
|
+
- **Configuration** — dataclass-based config sourced from the environment with dotted-key access.
|
|
79
|
+
- **Providers** — two-phase (`register` / `boot`) service registration.
|
|
80
|
+
- **Routing** — a `Router` wrapper around FastAPI's `APIRouter` with a `resource()` CRUD shortcut.
|
|
81
|
+
- **ORM** — async-first Masonite ORM fork on SQLAlchemy async, with relationships and migrations.
|
|
82
|
+
- **Facades** — static-like access to container-resolved services (`Config`, `Auth`, ...).
|
|
83
|
+
- **Console** — Cleo-based CLI (`artisan`) for migrations, seeders, and code generation.
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
Full documentation is available at
|
|
88
|
+
[fastapi-startkit.github.io](https://fastapi-startkit.github.io).
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Install dependencies
|
|
94
|
+
uv sync --group dev --extra database --extra sqlite --extra fastapi --extra vite --extra postgres
|
|
95
|
+
|
|
96
|
+
# Run the test suite
|
|
97
|
+
uv run pytest tests/ -v
|
|
98
|
+
|
|
99
|
+
# Run tests with coverage
|
|
100
|
+
uv run pytest --cov --cov-report=term-missing
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Coverage is collected in CI and reported to
|
|
104
|
+
[Codecov](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework).
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
See the repository root for license details.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# FastAPI Startkit
|
|
2
|
+
|
|
3
|
+
[](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework)
|
|
4
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
5
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
6
|
+
|
|
7
|
+
A modular, provider-driven framework for building Python applications with FastAPI. It bundles a
|
|
8
|
+
service container, configuration system, an async-first ORM, facades, and a Cleo-powered console into
|
|
9
|
+
a single cohesive foundation.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install fastapi-startkit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Optional extras enable additional capabilities:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
fastapi-startkit[fastapi] # FastAPI + Starlette
|
|
21
|
+
fastapi-startkit[database] # SQLAlchemy async ORM
|
|
22
|
+
fastapi-startkit[vite] # Jinja2 for Vite integration
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Service container** — IoC container with `bind`/`make`/`resolve` auto-wiring and lifecycle hooks.
|
|
28
|
+
- **Configuration** — dataclass-based config sourced from the environment with dotted-key access.
|
|
29
|
+
- **Providers** — two-phase (`register` / `boot`) service registration.
|
|
30
|
+
- **Routing** — a `Router` wrapper around FastAPI's `APIRouter` with a `resource()` CRUD shortcut.
|
|
31
|
+
- **ORM** — async-first Masonite ORM fork on SQLAlchemy async, with relationships and migrations.
|
|
32
|
+
- **Facades** — static-like access to container-resolved services (`Config`, `Auth`, ...).
|
|
33
|
+
- **Console** — Cleo-based CLI (`artisan`) for migrations, seeders, and code generation.
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Full documentation is available at
|
|
38
|
+
[fastapi-startkit.github.io](https://fastapi-startkit.github.io).
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Install dependencies
|
|
44
|
+
uv sync --group dev --extra database --extra sqlite --extra fastapi --extra vite --extra postgres
|
|
45
|
+
|
|
46
|
+
# Run the test suite
|
|
47
|
+
uv run pytest tests/ -v
|
|
48
|
+
|
|
49
|
+
# Run tests with coverage
|
|
50
|
+
uv run pytest --cov --cov-report=term-missing
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Coverage is collected in CI and reported to
|
|
54
|
+
[Codecov](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework).
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
See the repository root for license details.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fastapi-startkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.51.0"
|
|
4
4
|
description = "A modular, provider-driven framework for building async Python web applications with FastAPI — bundling a service container, configuration system, an async-first ORM, facades, and a Cleo-powered console."
|
|
5
|
+
readme = "README.md"
|
|
5
6
|
keywords = [
|
|
6
7
|
"fastapi",
|
|
7
8
|
"framework",
|
|
@@ -148,7 +149,7 @@ omit = [
|
|
|
148
149
|
[tool.coverage.report]
|
|
149
150
|
show_missing = true
|
|
150
151
|
skip_covered = false
|
|
151
|
-
fail_under =
|
|
152
|
+
fail_under = 80
|
|
152
153
|
exclude_lines = [
|
|
153
154
|
"pragma: no cover",
|
|
154
155
|
"if TYPE_CHECKING:",
|
|
@@ -8,6 +8,7 @@ from typing import Type, Callable, Any, List, TypeVar, Generic
|
|
|
8
8
|
from .config import AppConfig
|
|
9
9
|
from .configuration.providers import ConfigurationProvider
|
|
10
10
|
from .container import Container
|
|
11
|
+
from .events import EventServiceProvider
|
|
11
12
|
from .environment.environment import Environment
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
@@ -27,6 +28,7 @@ TConfig = TypeVar("TConfig", bound=AppConfig)
|
|
|
27
28
|
class Application(Container, Generic[TConfig]):
|
|
28
29
|
DEFAULT_PROVIDERS = [
|
|
29
30
|
ConfigurationProvider,
|
|
31
|
+
EventServiceProvider,
|
|
30
32
|
AppProvider,
|
|
31
33
|
]
|
|
32
34
|
|
{fastapi_startkit-0.49.0 → fastapi_startkit-0.51.0}/src/fastapi_startkit/container/container.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Core of the IOC Container."""
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from ..exceptions import (
|
|
6
7
|
ContainerError,
|
|
@@ -42,15 +43,13 @@ class Container:
|
|
|
42
43
|
self.swaps = {}
|
|
43
44
|
self._remembered = {}
|
|
44
45
|
|
|
45
|
-
def bind(self, name, class_obj):
|
|
46
|
+
def bind(self, name: str | type, class_obj: Any) -> None:
|
|
46
47
|
"""Bind classes into the container with a key value pair.
|
|
47
48
|
|
|
48
49
|
Arguments:
|
|
49
|
-
name {string} --
|
|
50
|
+
name {string | type} -- Key to bind the object to; a class object is
|
|
51
|
+
accepted for class-keyed bindings (mirrors make()'s class-key support)
|
|
50
52
|
class_obj {object} -- The object you want to bind
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
self
|
|
54
53
|
"""
|
|
55
54
|
if inspect.ismodule(class_obj):
|
|
56
55
|
raise StrictContainerException(
|
|
@@ -63,8 +62,6 @@ class Container:
|
|
|
63
62
|
self.fire_hook("bind", name, class_obj)
|
|
64
63
|
self.objects.update({name: class_obj})
|
|
65
64
|
|
|
66
|
-
return self
|
|
67
|
-
|
|
68
65
|
def unbind(self, name):
|
|
69
66
|
"""Unbind classes from the container from a key.
|
|
70
67
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .dispatcher import Dispatcher
|
|
2
|
+
from .fake import EventFake
|
|
3
|
+
from .helpers import event
|
|
4
|
+
from .listener import Listener
|
|
5
|
+
from .provider import EventServiceProvider
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"Dispatcher",
|
|
9
|
+
"EventFake",
|
|
10
|
+
"EventServiceProvider",
|
|
11
|
+
"Listener",
|
|
12
|
+
"event",
|
|
13
|
+
]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Application event dispatcher.
|
|
2
|
+
|
|
3
|
+
An async-aware, Laravel-style event dispatcher. Events are plain classes;
|
|
4
|
+
listeners are callables or classes exposing a ``handle`` method. Both sync and
|
|
5
|
+
coroutine listeners are supported — coroutine results are awaited transparently.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import inspect
|
|
9
|
+
from fnmatch import fnmatchcase
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, cast, overload
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ..container import Container
|
|
14
|
+
from .fake import EventFake
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Dispatcher:
|
|
18
|
+
def __init__(self, container: "Container | None" = None):
|
|
19
|
+
self._container = container
|
|
20
|
+
self._listeners: dict[str, list] = {}
|
|
21
|
+
|
|
22
|
+
@overload
|
|
23
|
+
def listen(self, events, listener: None = None) -> Callable: ...
|
|
24
|
+
|
|
25
|
+
@overload
|
|
26
|
+
def listen(self, events, listener: Callable) -> None: ...
|
|
27
|
+
|
|
28
|
+
def listen(self, events, listener: Callable | None = None):
|
|
29
|
+
"""Register a listener for one or more events.
|
|
30
|
+
|
|
31
|
+
``events`` may be an event class, a string name, or a list of either.
|
|
32
|
+
String names may contain ``*`` wildcards (e.g. ``"user.*"``) — the
|
|
33
|
+
listener then fires for any matching event name. Used as a decorator
|
|
34
|
+
when ``listener`` is omitted.
|
|
35
|
+
"""
|
|
36
|
+
if listener is None:
|
|
37
|
+
|
|
38
|
+
def decorator(func: Callable) -> Callable:
|
|
39
|
+
self.listen(events, func)
|
|
40
|
+
return func
|
|
41
|
+
|
|
42
|
+
return decorator
|
|
43
|
+
|
|
44
|
+
for name in self._event_names(events):
|
|
45
|
+
self._listeners.setdefault(name, []).append(listener)
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def has_listeners(self, event) -> bool:
|
|
49
|
+
return bool(self._get_listeners(self._event_key(event)))
|
|
50
|
+
|
|
51
|
+
def forget(self, event) -> None:
|
|
52
|
+
"""Remove all listeners registered for a given event."""
|
|
53
|
+
self._listeners.pop(self._event_key(event), None)
|
|
54
|
+
|
|
55
|
+
def flush(self) -> None:
|
|
56
|
+
"""Remove every registered listener."""
|
|
57
|
+
self._listeners.clear()
|
|
58
|
+
|
|
59
|
+
async def dispatch(self, event, payload=None, halt: bool = False):
|
|
60
|
+
"""Fire an event and call its listeners in registration order.
|
|
61
|
+
|
|
62
|
+
Returns the list of listener responses. When ``halt`` is true, the first
|
|
63
|
+
non-``None`` response is returned immediately. A listener returning
|
|
64
|
+
``False`` stops propagation to later listeners.
|
|
65
|
+
"""
|
|
66
|
+
name, args = self._parse_event_payload(event, payload)
|
|
67
|
+
|
|
68
|
+
responses: list[Any] = []
|
|
69
|
+
for listener in self._get_listeners(name):
|
|
70
|
+
response = await self._call_listener(listener, args)
|
|
71
|
+
|
|
72
|
+
if halt and response is not None:
|
|
73
|
+
return response
|
|
74
|
+
|
|
75
|
+
if response is False:
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
responses.append(response)
|
|
79
|
+
|
|
80
|
+
return None if halt else responses
|
|
81
|
+
|
|
82
|
+
async def until(self, event, payload=None):
|
|
83
|
+
"""Dispatch an event, returning the first non-``None`` listener response."""
|
|
84
|
+
return await self.dispatch(event, payload, halt=True)
|
|
85
|
+
|
|
86
|
+
def fake(self, events_to_fake=None) -> "EventFake":
|
|
87
|
+
"""Swap the container's dispatcher for a recording fake (testing helper)."""
|
|
88
|
+
from .fake import EventFake
|
|
89
|
+
|
|
90
|
+
fake = EventFake(cast("Dispatcher", self), events_to_fake)
|
|
91
|
+
if self._container is not None:
|
|
92
|
+
self._container.bind("events", fake)
|
|
93
|
+
return fake
|
|
94
|
+
|
|
95
|
+
def _event_names(self, events) -> list[str]:
|
|
96
|
+
items = events if isinstance(events, (list, tuple)) else [events]
|
|
97
|
+
return [self._event_key(event) for event in items]
|
|
98
|
+
|
|
99
|
+
def _get_listeners(self, name: str) -> list:
|
|
100
|
+
"""All listeners for an event name: exact matches first, then any
|
|
101
|
+
wildcard-pattern listeners whose pattern matches the name."""
|
|
102
|
+
listeners = list(self._listeners.get(name, []))
|
|
103
|
+
for pattern, pattern_listeners in self._listeners.items():
|
|
104
|
+
if pattern != name and "*" in pattern and fnmatchcase(name, pattern):
|
|
105
|
+
listeners.extend(pattern_listeners)
|
|
106
|
+
return listeners
|
|
107
|
+
|
|
108
|
+
def _event_key(self, event) -> str:
|
|
109
|
+
if isinstance(event, str):
|
|
110
|
+
return event
|
|
111
|
+
cls = event if inspect.isclass(event) else type(event)
|
|
112
|
+
return f"{cls.__module__}.{cls.__qualname__}"
|
|
113
|
+
|
|
114
|
+
def _parse_event_payload(self, event, payload) -> tuple[str, list]:
|
|
115
|
+
if isinstance(event, str):
|
|
116
|
+
if payload is None:
|
|
117
|
+
args: list = []
|
|
118
|
+
elif isinstance(payload, (list, tuple)):
|
|
119
|
+
args = list(payload)
|
|
120
|
+
else:
|
|
121
|
+
args = [payload]
|
|
122
|
+
return event, args
|
|
123
|
+
|
|
124
|
+
return self._event_key(event), [event]
|
|
125
|
+
|
|
126
|
+
async def _call_listener(self, listener: Callable, args: list):
|
|
127
|
+
handler = self._resolve_listener(listener)
|
|
128
|
+
result = handler(*args)
|
|
129
|
+
if inspect.isawaitable(result):
|
|
130
|
+
result = await result
|
|
131
|
+
return result
|
|
132
|
+
|
|
133
|
+
def _resolve_listener(self, listener: Callable) -> Callable:
|
|
134
|
+
if inspect.isclass(listener):
|
|
135
|
+
return self._make(listener).handle
|
|
136
|
+
return listener
|
|
137
|
+
|
|
138
|
+
def _make(self, cls):
|
|
139
|
+
if self._container is not None:
|
|
140
|
+
return self._container.resolve(cls)
|
|
141
|
+
return cls()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""A recording fake dispatcher used by ``Event.fake()`` in tests.
|
|
2
|
+
|
|
3
|
+
Faked events are captured instead of dispatched to their listeners; any event
|
|
4
|
+
not in the fake list is forwarded to the real dispatcher. The recorded events
|
|
5
|
+
can then be asserted against.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Callable
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .dispatcher import Dispatcher
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EventFake:
|
|
15
|
+
def __init__(self, dispatcher: "Dispatcher", events_to_fake=None):
|
|
16
|
+
self._dispatcher = dispatcher
|
|
17
|
+
self._events_to_fake = self._normalize(events_to_fake)
|
|
18
|
+
self._dispatched: dict[str, list] = {}
|
|
19
|
+
|
|
20
|
+
async def dispatch(self, event, payload=None, halt: bool = False):
|
|
21
|
+
name, args = self._dispatcher._parse_event_payload(event, payload)
|
|
22
|
+
|
|
23
|
+
if self._should_fake(name):
|
|
24
|
+
recorded = args if isinstance(event, str) else event
|
|
25
|
+
self._dispatched.setdefault(name, []).append(recorded)
|
|
26
|
+
return None if halt else []
|
|
27
|
+
|
|
28
|
+
return await self._dispatcher.dispatch(event, payload, halt)
|
|
29
|
+
|
|
30
|
+
async def until(self, event, payload=None):
|
|
31
|
+
return await self.dispatch(event, payload, halt=True)
|
|
32
|
+
|
|
33
|
+
def listen(self, events, listener: Callable | None = None):
|
|
34
|
+
return self._dispatcher.listen(events, listener)
|
|
35
|
+
|
|
36
|
+
def has_listeners(self, event) -> bool:
|
|
37
|
+
return self._dispatcher.has_listeners(event)
|
|
38
|
+
|
|
39
|
+
def forget(self, event) -> None:
|
|
40
|
+
self._dispatcher.forget(event)
|
|
41
|
+
|
|
42
|
+
def flush(self) -> None:
|
|
43
|
+
self._dispatcher.flush()
|
|
44
|
+
|
|
45
|
+
def dispatched(self, event, callback: Callable | None = None) -> list:
|
|
46
|
+
"""Return recorded dispatches for an event, optionally filtered."""
|
|
47
|
+
records = self._dispatched.get(self._dispatcher._event_key(event), [])
|
|
48
|
+
if callback is None:
|
|
49
|
+
return list(records)
|
|
50
|
+
return [record for record in records if callback(record)]
|
|
51
|
+
|
|
52
|
+
def assert_dispatched(self, event, callback: Callable | None = None) -> list:
|
|
53
|
+
records = self.dispatched(event, callback)
|
|
54
|
+
assert records, f"The expected event [{self._dispatcher._event_key(event)}] was not dispatched."
|
|
55
|
+
return records
|
|
56
|
+
|
|
57
|
+
def assert_dispatched_times(self, event, times: int = 1) -> None:
|
|
58
|
+
count = len(self.dispatched(event))
|
|
59
|
+
assert count == times, (
|
|
60
|
+
f"The expected event [{self._dispatcher._event_key(event)}] was dispatched {count} "
|
|
61
|
+
f"time(s) instead of {times} time(s)."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def assert_not_dispatched(self, event, callback: Callable | None = None) -> None:
|
|
65
|
+
count = len(self.dispatched(event, callback))
|
|
66
|
+
assert count == 0, f"The unexpected event [{self._dispatcher._event_key(event)}] was dispatched."
|
|
67
|
+
|
|
68
|
+
def assert_nothing_dispatched(self) -> None:
|
|
69
|
+
total = sum(len(records) for records in self._dispatched.values())
|
|
70
|
+
assert total == 0, f"{total} unexpected event(s) were dispatched."
|
|
71
|
+
|
|
72
|
+
def _normalize(self, events) -> list[str]:
|
|
73
|
+
if events is None:
|
|
74
|
+
return []
|
|
75
|
+
items = events if isinstance(events, (list, tuple)) else [events]
|
|
76
|
+
return [self._dispatcher._event_key(event) for event in items]
|
|
77
|
+
|
|
78
|
+
def _should_fake(self, name: str) -> bool:
|
|
79
|
+
return not self._events_to_fake or name in self._events_to_fake
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Global ``event()`` helper mirroring Laravel's event helper."""
|
|
2
|
+
|
|
3
|
+
from typing import cast
|
|
4
|
+
|
|
5
|
+
from .dispatcher import Dispatcher
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def event(event, payload=None, halt: bool = False):
|
|
9
|
+
"""Dispatch an event through the container's dispatcher.
|
|
10
|
+
|
|
11
|
+
Returns the awaitable produced by ``Dispatcher.dispatch`` — callers should
|
|
12
|
+
``await`` it::
|
|
13
|
+
|
|
14
|
+
await event(OrderShipped(order))
|
|
15
|
+
"""
|
|
16
|
+
from ..application import app
|
|
17
|
+
|
|
18
|
+
dispatcher = cast(Dispatcher, app().make("events"))
|
|
19
|
+
return dispatcher.dispatch(event, payload, halt)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Optional base class for class-based listeners.
|
|
2
|
+
|
|
3
|
+
Listeners are not required to extend this — any callable, or any class exposing
|
|
4
|
+
a ``handle`` method, works. It exists to document the contract and to give
|
|
5
|
+
type checkers something to lean on. ``handle`` may be sync or async.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Listener(ABC):
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def handle(self, event: Any):
|
|
15
|
+
"""Handle the given event."""
|
|
16
|
+
...
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Service provider that registers the event dispatcher.
|
|
2
|
+
|
|
3
|
+
Subclass and define ``listen`` to map events to their listeners, mirroring
|
|
4
|
+
Laravel's ``EventServiceProvider``::
|
|
5
|
+
|
|
6
|
+
class AppEventServiceProvider(EventServiceProvider):
|
|
7
|
+
listen = {
|
|
8
|
+
UserRegistered: [SendWelcomeEmail, LogRegistration],
|
|
9
|
+
}
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import cast
|
|
13
|
+
|
|
14
|
+
from fastapi_startkit.support import Provider
|
|
15
|
+
|
|
16
|
+
from .dispatcher import Dispatcher
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EventServiceProvider(Provider):
|
|
20
|
+
provider_key = "events"
|
|
21
|
+
|
|
22
|
+
listen: dict = {}
|
|
23
|
+
|
|
24
|
+
def register(self) -> None:
|
|
25
|
+
self.app.bind("events", Dispatcher(self.app))
|
|
26
|
+
|
|
27
|
+
def boot(self) -> None:
|
|
28
|
+
dispatcher = cast(Dispatcher, self.app.make("events"))
|
|
29
|
+
for event, listeners in self.listen.items():
|
|
30
|
+
for listener in listeners:
|
|
31
|
+
dispatcher.listen(event, listener)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
|
|
3
|
+
from fastapi_startkit.events.fake import EventFake
|
|
4
|
+
|
|
5
|
+
class Event:
|
|
6
|
+
"""Facade for the event dispatcher registered under the 'events' key."""
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def listen(events: Any, listener: Callable | None = None) -> Callable | None:
|
|
10
|
+
"""Register a listener for one or more events (or use as a decorator)."""
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
async def dispatch(event: Any, payload: Any = None, halt: bool = False) -> Any:
|
|
15
|
+
"""Fire an event and invoke its listeners; returns their responses."""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
async def until(event: Any, payload: Any = None) -> Any:
|
|
20
|
+
"""Dispatch an event, returning the first non-None listener response."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def has_listeners(event: Any) -> bool:
|
|
25
|
+
"""Return whether any listener is registered for the event."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def forget(event: Any) -> None:
|
|
30
|
+
"""Remove all listeners registered for the event."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def flush() -> None:
|
|
35
|
+
"""Remove every registered listener."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def fake(events_to_fake: Any = None) -> EventFake:
|
|
40
|
+
"""Swap the dispatcher for a recording fake and return it."""
|
|
41
|
+
...
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .providers.fastapi_provider import FastAPIProvider
|
|
2
|
+
from .routers.router import Router
|
|
3
|
+
from .requests.model import RequestModel
|
|
4
|
+
from .config import FastAPIConfig
|
|
5
|
+
from .events import RequestHandled
|
|
6
|
+
from .middleware import RequestLifecycleMiddleware
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"FastAPIProvider",
|
|
10
|
+
"Router",
|
|
11
|
+
"RequestModel",
|
|
12
|
+
"FastAPIConfig",
|
|
13
|
+
"RequestHandled",
|
|
14
|
+
"RequestLifecycleMiddleware",
|
|
15
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Structured events emitted by the FastAPI request lifecycle.
|
|
2
|
+
|
|
3
|
+
These ride on the framework's Laravel-style event dispatcher (see
|
|
4
|
+
``fastapi_startkit.events``) — this module only defines the event payload,
|
|
5
|
+
it does not dispatch, log, or persist anything itself.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class RequestHandled:
|
|
13
|
+
"""Dispatched once per HTTP request by ``RequestLifecycleMiddleware``.
|
|
14
|
+
|
|
15
|
+
Register a listener the normal way to observe it::
|
|
16
|
+
|
|
17
|
+
from fastapi_startkit.facades import Event
|
|
18
|
+
from fastapi_startkit.fastapi.events import RequestHandled
|
|
19
|
+
|
|
20
|
+
Event.listen(RequestHandled, lambda e: logger.info(
|
|
21
|
+
"%s %s -> %s (%.1fms) [%s]", e.method, e.path, e.status_code, e.duration_ms, e.request_id
|
|
22
|
+
))
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
method: str
|
|
26
|
+
path: str
|
|
27
|
+
status_code: int
|
|
28
|
+
duration_ms: float
|
|
29
|
+
request_id: str
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""ASGI middleware that emits a structured ``RequestHandled`` event per request.
|
|
2
|
+
|
|
3
|
+
This is dispatch-only: it fires the event through the framework's existing
|
|
4
|
+
Event dispatcher (task #940) and does not persist, log, or render anything
|
|
5
|
+
itself. Consumers — a logging listener, tests, future tooling — register
|
|
6
|
+
listeners the normal way via ``Event.listen(RequestHandled, ...)``.
|
|
7
|
+
|
|
8
|
+
Out of scope for this middleware (left as extension points for follow-up
|
|
9
|
+
work): DB query watchers, exception watchers, and any other Telescope-style
|
|
10
|
+
watcher, plus storage/UI for the emitted events.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
|
17
|
+
from starlette.requests import Request
|
|
18
|
+
from starlette.responses import Response
|
|
19
|
+
|
|
20
|
+
from fastapi_startkit.events.helpers import event
|
|
21
|
+
|
|
22
|
+
from .events import RequestHandled
|
|
23
|
+
|
|
24
|
+
REQUEST_ID_HEADER = "X-Request-Id"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RequestLifecycleMiddleware(BaseHTTPMiddleware):
|
|
28
|
+
"""Dispatches ``RequestHandled`` (method, path, status, duration, request id)."""
|
|
29
|
+
|
|
30
|
+
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
31
|
+
request_id = request.headers.get(REQUEST_ID_HEADER) or str(uuid.uuid4())
|
|
32
|
+
request.state.request_id = request_id
|
|
33
|
+
|
|
34
|
+
started_at = time.perf_counter()
|
|
35
|
+
response = None
|
|
36
|
+
try:
|
|
37
|
+
response = await call_next(request)
|
|
38
|
+
return response
|
|
39
|
+
finally:
|
|
40
|
+
duration_ms = (time.perf_counter() - started_at) * 1000
|
|
41
|
+
status_code = response.status_code if response is not None else 500
|
|
42
|
+
|
|
43
|
+
if response is not None:
|
|
44
|
+
response.headers.setdefault(REQUEST_ID_HEADER, request_id)
|
|
45
|
+
|
|
46
|
+
await event(
|
|
47
|
+
RequestHandled(
|
|
48
|
+
method=request.method,
|
|
49
|
+
path=request.url.path,
|
|
50
|
+
status_code=status_code,
|
|
51
|
+
duration_ms=duration_ms,
|
|
52
|
+
request_id=request_id,
|
|
53
|
+
)
|
|
54
|
+
)
|