nlbone 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.
@@ -1 +1,4 @@
1
- from .query_builder import get_paginated_response, apply_pagination
1
+ from .query_builder import get_paginated_response, apply_pagination
2
+ from .engine import init_sync_engine, init_async_engine, sync_ping, sync_session, async_ping, async_session
3
+ from .repository import SqlAlchemyRepository, AsyncSqlAlchemyRepository
4
+ from .uow import SqlAlchemyUnitOfWork, AsyncSqlAlchemyUnitOfWork
@@ -117,3 +117,14 @@ def sync_ping() -> None:
117
117
  with eng.connect() as conn:
118
118
  conn.execute(text("SELECT 1"))
119
119
 
120
+ def get_async_session_factory() -> async_sessionmaker[AsyncSession]:
121
+ if _async_session_factory is None:
122
+ init_async_engine()
123
+ assert _async_session_factory is not None
124
+ return _async_session_factory
125
+
126
+ def get_sync_session_factory() -> sessionmaker[Session]:
127
+ if _sync_session_factory is None:
128
+ init_sync_engine()
129
+ assert _sync_session_factory is not None
130
+ return _sync_session_factory
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+ from typing import Generic, Iterable, Optional, Type, TypeVar, List
3
+
4
+ from sqlalchemy import select
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+ from sqlalchemy.orm import Session
7
+ from nlbone.core.ports.repo import Repository, AsyncRepository
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ class SqlAlchemyRepository(Repository[T], Generic[T]):
13
+ def __init__(self, session: Session, model: Type[T]) -> None:
14
+ self.session = session
15
+ self.model = model
16
+
17
+ def get(self, id) -> Optional[T]:
18
+ return self.session.get(self.model, id)
19
+
20
+ def add(self, obj: T) -> None:
21
+ self.session.add(obj)
22
+
23
+ def remove(self, obj: T) -> None:
24
+ self.session.delete(obj)
25
+
26
+ def list(self, *, limit: int | None = None, offset: int = 0) -> Iterable[T]:
27
+ q = self.session.query(self.model).offset(offset)
28
+ if limit is not None:
29
+ q = q.limit(limit)
30
+ return q.all()
31
+
32
+
33
+ class AsyncSqlAlchemyRepository(AsyncRepository, Generic[T]):
34
+ def __init__(self, session: AsyncSession, model: Type[T]) -> None:
35
+ self.session = session
36
+ self.model = model
37
+
38
+ async def get(self, id) -> Optional[T]:
39
+ return await self.session.get(self.model, id)
40
+
41
+ def add(self, obj: T) -> None:
42
+ self.session.add(obj)
43
+
44
+ async def remove(self, obj: T) -> None:
45
+ await self.session.delete(obj)
46
+
47
+ async def list(self, *, limit: int | None = None, offset: int = 0) -> List[T]:
48
+ stmt = select(self.model).offset(offset)
49
+ if limit is not None:
50
+ stmt = stmt.limit(limit)
51
+ res = await self.session.execute(stmt)
52
+ return list(res.scalars().all())
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from sqlalchemy.ext.asyncio import async_sessionmaker, AsyncSession
6
+ from sqlalchemy.orm import Session, sessionmaker
7
+ from nlbone.core.ports.uow import UnitOfWork
8
+ from nlbone.core.ports.uow import AsyncUnitOfWork as AsyncUnitOfWorkPort
9
+
10
+
11
+ class SqlAlchemyUnitOfWork(UnitOfWork):
12
+ """sync UoW for SQLAlchemy."""
13
+
14
+ def __init__(self, session_factory: sessionmaker) -> None:
15
+ self._session_factory = session_factory
16
+ self.session: Session | None = None
17
+
18
+ def __enter__(self) -> "SqlAlchemyUnitOfWork":
19
+ self.session = self._session_factory()
20
+ return self
21
+
22
+ def __exit__(self, exc_type, exc, tb) -> None:
23
+ try:
24
+ if exc_type is None:
25
+ self.commit()
26
+ else:
27
+ self.rollback()
28
+ finally:
29
+ if self.session is not None:
30
+ self.session.close()
31
+ self.session = None
32
+
33
+ def commit(self) -> None:
34
+ if self.session:
35
+ self.session.commit()
36
+
37
+ def rollback(self) -> None:
38
+ if self.session:
39
+ self.session.rollback()
40
+
41
+
42
+ class AsyncSqlAlchemyUnitOfWork(AsyncUnitOfWorkPort):
43
+ """Transactional boundary for async SQLAlchemy."""
44
+
45
+ def __init__(self, session_factory: async_sessionmaker[AsyncSession]) -> None:
46
+ self._sf = session_factory
47
+ self.session: Optional[AsyncSession] = None
48
+
49
+ async def __aenter__(self) -> "AsyncSqlAlchemyUnitOfWork":
50
+ self.session = self._sf()
51
+ return self
52
+
53
+ async def __aexit__(self, exc_type, exc, tb) -> None:
54
+ try:
55
+ if exc_type is None:
56
+ await self.commit()
57
+ else:
58
+ await self.rollback()
59
+ finally:
60
+ if self.session is not None:
61
+ await self.session.close()
62
+ self.session = None
63
+
64
+ async def commit(self) -> None:
65
+ if self.session:
66
+ await self.session.commit()
67
+
68
+ async def rollback(self) -> None:
69
+ if self.session:
70
+ await self.session.rollback()
@@ -0,0 +1 @@
1
+ from .event_bus import InMemoryEventBus
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+ from collections import defaultdict
3
+ from typing import Callable, Dict, Iterable, List
4
+ from nlbone.core.domain.base import DomainEvent
5
+ from nlbone.core.ports.event_bus import EventBusPort
6
+
7
+ class InMemoryEventBus(EventBusPort):
8
+ def __init__(self) -> None:
9
+ self._handlers: Dict[str, List[Callable[[DomainEvent], None]]] = defaultdict(list)
10
+
11
+ def publish(self, events: Iterable[DomainEvent]) -> None:
12
+ for evt in events:
13
+ for h in self._handlers.get(evt.name, []):
14
+ try:
15
+ h(evt)
16
+ except Exception:
17
+ pass
18
+
19
+ def subscribe(self, event_name: str, handler: Callable[[DomainEvent], None]) -> None:
20
+ self._handlers[event_name].append(handler)
nlbone/config/settings.py CHANGED
@@ -22,12 +22,21 @@ def _guess_env_file() -> str | None:
22
22
  return str(f)
23
23
 
24
24
 
25
+ def _is_production_env() -> bool:
26
+ raw = os.getenv("NLBONE_ENV") or os.getenv("ENV") or os.getenv("ENVIRONMENT")
27
+ if not raw:
28
+ return False
29
+ return raw.strip().lower() in {"prod", "production"}
30
+
31
+
25
32
  class Settings(BaseSettings):
26
33
  # ---------------------------
27
34
  # App
28
35
  # ---------------------------
29
36
  PORT: int = 8000
30
- ENV: Literal["local", "dev", "staging", "prod"] = Field(default="local")
37
+ ENV: Literal["local", "dev", "staging", "prod"] = Field(default="local",
38
+ validation_alias=AliasChoices("NLBONE_ENV", "ENV",
39
+ "ENVIRONMENT"))
31
40
  DEBUG: bool = Field(default=False)
32
41
  LOG_LEVEL: Literal["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"] = Field(default="INFO")
33
42
  LOG_JSON: bool = Field(default=True)
@@ -54,13 +63,21 @@ class Settings(BaseSettings):
54
63
  # ---------------------------
55
64
  # Database
56
65
  # ---------------------------
57
- POSTGRES_DB_DSN: str = Field(default="postgresql+asyncpg://user:pass@localhost:5432/nlbone", validation_alias=AliasChoices("NLBONE_POSTGRES_DB_DSN",
58
- "POSTGRES_DB_DSN", "DATABASE_URL", "DB_DSN"))
66
+ POSTGRES_DB_DSN: str = Field(default="postgresql+asyncpg://user:pass@localhost:5432/nlbone",
67
+ validation_alias=AliasChoices("NLBONE_POSTGRES_DB_DSN",
68
+ "POSTGRES_DB_DSN", "DATABASE_URL", "DB_DSN"))
69
+ DB_ECHO: bool = Field(default=False)
70
+ DB_POOL_SIZE: int = Field(default=5)
71
+ DB_MAX_OVERFLOW: int = Field(default=10)
59
72
 
60
73
  # ---------------------------
61
74
  # Messaging / Cache
62
75
  # ---------------------------
63
76
  REDIS_URL: str = Field(default="redis://localhost:6379/0")
77
+ # --- Event bus / Outbox ---
78
+ EVENT_BUS_BACKEND: Literal["inmemory"] = Field(default="inmemory")
79
+ OUTBOX_ENABLED: bool = Field(default=False)
80
+ OUTBOX_POLL_INTERVAL_MS: int = Field(default=500)
64
81
 
65
82
  # ---------------------------
66
83
  # UPLOADCHI
@@ -80,6 +97,8 @@ class Settings(BaseSettings):
80
97
 
81
98
  @classmethod
82
99
  def load(cls, env_file: str | None = None) -> "Settings":
100
+ if _is_production_env():
101
+ return cls()
83
102
  return cls(_env_file=env_file or _guess_env_file())
84
103
 
85
104
 
nlbone/container.py CHANGED
@@ -3,15 +3,30 @@ from typing import Any, Mapping, Optional
3
3
 
4
4
  from dependency_injector import containers, providers
5
5
 
6
+ from nlbone.adapters.db.sqlalchemy import SqlAlchemyUnitOfWork, AsyncUnitOfWork
7
+ from nlbone.adapters.db.sqlalchemy.engine import get_sync_session_factory, get_async_session_factory
6
8
  from nlbone.adapters.http_clients.uploadchi import UploadchiClient
7
9
  from nlbone.adapters.http_clients.uploadchi_async import UploadchiAsyncClient
8
10
  from nlbone.adapters.auth.keycloak import KeycloakAuthService
11
+ from nlbone.adapters.messaging import InMemoryEventBus
12
+ from nlbone.core.ports import EventBusPort
9
13
  from nlbone.core.ports.files import FileServicePort, AsyncFileServicePort
10
14
 
11
15
 
12
16
  class Container(containers.DeclarativeContainer):
13
17
  config = providers.Configuration(strict=False)
14
18
 
19
+ sync_session_factory = providers.Singleton(get_sync_session_factory)
20
+ async_session_factory = providers.Singleton(get_async_session_factory)
21
+
22
+ # --- UoW ---
23
+ uow = providers.Factory(SqlAlchemyUnitOfWork, session_factory=sync_session_factory)
24
+ async_uow = providers.Factory(AsyncUnitOfWork, session_factory=async_session_factory)
25
+
26
+ # --- Event bus ---
27
+ event_bus: providers.Singleton[EventBusPort] = providers.Singleton(InMemoryEventBus)
28
+
29
+ # --- Services ---
15
30
  auth: providers.Singleton[KeycloakAuthService] = providers.Singleton(KeycloakAuthService, settings=config)
16
31
  file_service: providers.Singleton[FileServicePort] = providers.Singleton(UploadchiClient)
17
32
  afiles_service: providers.Singleton[AsyncFileServicePort] = providers.Singleton(UploadchiAsyncClient)
@@ -0,0 +1,16 @@
1
+ from typing import Iterable, Sequence
2
+ from nlbone.core.domain.base import AggregateRoot, DomainEvent
3
+ from nlbone.core.ports.event_bus import EventBusPort
4
+
5
+ def collect_events(*aggregates: Iterable[AggregateRoot]) -> list[DomainEvent]:
6
+ events: list[DomainEvent] = []
7
+ for agg in aggregates:
8
+ if isinstance(agg, AggregateRoot):
9
+ events.extend(agg.pull_events())
10
+ else:
11
+ for a in agg:
12
+ events.extend(a.pull_events())
13
+ return events
14
+
15
+ def publish_events(bus: EventBusPort, events: Sequence[DomainEvent]) -> None:
16
+ if events: bus.publish(events)
@@ -0,0 +1,10 @@
1
+ from typing import Protocol, TypeVar
2
+
3
+
4
+ InT = TypeVar("InT"); OutT = TypeVar("OutT")
5
+
6
+ class UseCase(Protocol):
7
+ def __call__(self, command: InT) -> OutT: ...
8
+
9
+ class AsyncUseCase(Protocol):
10
+ async def __call__(self, command: InT) -> OutT: ...
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from datetime import datetime, timezone
4
+ from typing import Any, Generic, List, TypeVar
5
+
6
+ TId = TypeVar("TId")
7
+
8
+
9
+ class DomainError(Exception):
10
+ """Base domain exception."""
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class DomainEvent:
15
+ """Immutable domain event."""
16
+ occurred_at: datetime = datetime.now(timezone.utc)
17
+
18
+ @property
19
+ def name(self):
20
+ return self.__class__.__name__
21
+
22
+
23
+ class ValueObject:
24
+ """Base for value objects (immutable in practice)."""
25
+ def __eq__(self, other: Any) -> bool:
26
+ return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
27
+
28
+ def __hash__(self) -> int: # allow in sets/dicts
29
+ return hash(tuple(sorted(self.__dict__.items())))
30
+
31
+
32
+ class Entity(Generic[TId]):
33
+ id: TId
34
+
35
+
36
+ class AggregateRoot(Entity[TId]):
37
+ """Aggregate root with domain event collection."""
38
+ def __init__(self, *args, **kwargs) -> None:
39
+ self._domain_events: List[DomainEvent] = []
40
+
41
+ def _raise(self, event: DomainEvent) -> None:
42
+ self._domain_events.append(event)
43
+
44
+ def pull_events(self) -> List[DomainEvent]:
45
+ events = list(self._domain_events)
46
+ self._domain_events.clear()
47
+ return events
@@ -1 +1,5 @@
1
- from .auth import AuthService
1
+ from .auth import AuthService
2
+ from .repo import Repository, AsyncRepository
3
+ from .files import FileServicePort, AsyncFileServicePort
4
+ from .uow import UnitOfWork, AsyncUnitOfWork
5
+ from .event_bus import EventBusPort
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+ from typing import Callable, Iterable, Protocol
3
+ from nlbone.core.domain.base import DomainEvent
4
+
5
+ class EventBusPort(Protocol):
6
+ def publish(self, events: Iterable[DomainEvent]) -> None: ...
7
+ def subscribe(self, event_name: str, handler: Callable[[DomainEvent], None]) -> None: ...
nlbone/core/ports/repo.py CHANGED
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+ from typing import Iterable, Optional, Protocol, TypeVar, List
3
+
4
+ T = TypeVar("T")
5
+
6
+ class Repository(Protocol[T]): # ← نه Protocol, Generic[T]
7
+ def get(self, id) -> Optional[T]: ...
8
+ def add(self, obj: T) -> None: ...
9
+ def remove(self, obj: T) -> None: ...
10
+ def list(self, *, limit: int | None = None, offset: int = 0) -> Iterable[T]: ...
11
+
12
+ class AsyncRepository(Protocol[T]):
13
+ async def get(self, id) -> Optional[T]: ...
14
+ def add(self, obj: T) -> None: ...
15
+ async def remove(self, obj: T) -> None: ...
16
+ async def list(self, *, limit: int | None = None, offset: int = 0) -> List[T]: ...
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+ from typing import Protocol, runtime_checkable
3
+
4
+ @runtime_checkable
5
+ class UnitOfWork(Protocol):
6
+ def __enter__(self) -> "UnitOfWork": ...
7
+ def __exit__(self, exc_type, exc, tb) -> None: ...
8
+ def commit(self) -> None: ...
9
+ def rollback(self) -> None: ...
10
+
11
+ @runtime_checkable
12
+ class AsyncUnitOfWork(Protocol):
13
+ async def __aenter__(self) -> "AsyncUnitOfWork": ...
14
+ async def __aexit__(self, exc_type, exc, tb) -> None: ...
15
+ async def commit(self) -> None: ...
16
+ async def rollback(self) -> None: ...
@@ -1,2 +1,3 @@
1
1
  from .db import get_session, get_async_session
2
- from .auth import has_access, client_has_access, current_client_id, current_user_id, current_request, user_authenticated
2
+ from .auth import has_access, client_has_access, current_client_id, current_user_id, current_request, user_authenticated
3
+ from .uow import get_uow, get_async_uow
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+ from collections.abc import Iterator
3
+ from typing import AsyncIterator
4
+
5
+ from fastapi import Request
6
+
7
+ from nlbone.adapters.db.sqlalchemy import AsyncSqlAlchemyUnitOfWork
8
+ from nlbone.core.ports.uow import UnitOfWork, AsyncUnitOfWork
9
+
10
+
11
+ def get_uow(request: Request) -> Iterator[UnitOfWork]:
12
+ """
13
+ Uses DI container mounted at app.state.container to create a UoW per request.
14
+ Assumes container.uow is a provider returning SqlAlchemyUnitOfWork(session_factory).
15
+ """
16
+ container = getattr(request.app.state, "container", None)
17
+ if container is None or not hasattr(container, "uow"):
18
+ raise RuntimeError("Container with 'uow' provider not configured on app.state.container")
19
+
20
+ uow = container.uow()
21
+ with uow as _uow:
22
+ yield _uow
23
+
24
+
25
+ async def get_async_uow(request: Request) -> AsyncIterator[AsyncUnitOfWork]:
26
+ container = getattr(request.app.state, "container", None)
27
+ if container is None or not hasattr(container, "async_uow"):
28
+ raise RuntimeError("Container.async_uow provider not configured")
29
+ uow: AsyncSqlAlchemyUnitOfWork = container.async_uow()
30
+ async with uow as _uow:
31
+ yield _uow
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nlbone
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Backbone package for interfaces and infrastructure in Python projects
5
5
  Author-email: Amir Hosein Kahkbazzadeh <a.khakbazzadeh@gmail.com>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  nlbone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- nlbone/container.py,sha256=KO8y8hfEt0graWhUi-FU_rG-WPckl-uF7H9JGcwEu38,1321
2
+ nlbone/container.py,sha256=XQVtW7r13ON6QCMA_MOEct1GO7yOPjHU8ZQ600k3iR8,2083
3
3
  nlbone/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  nlbone/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  nlbone/adapters/auth/__init__.py,sha256=Eh9kWjY1I8vi17gK0oOzBLJwJX_GFuUcJIN7cLU6lJg,41
@@ -7,43 +7,50 @@ nlbone/adapters/auth/keycloak.py,sha256=8UjT1GMenzolR-XAzlKERJDWh3TLBrxaIasMenRY
7
7
  nlbone/adapters/db/__init__.py,sha256=VzhE_VOTFxDE0yUOw-f8UCLFCVhdyKnrrhbqzZUy76A,218
8
8
  nlbone/adapters/db/memory.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  nlbone/adapters/db/postgres.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- nlbone/adapters/db/sqlalchemy/__init__.py,sha256=1yw3keC1GYqQ6AM4283if_TpeB0xbm0-FBD7XUf5bcg,67
10
+ nlbone/adapters/db/sqlalchemy/__init__.py,sha256=0J9r4m72TtlozNh9YAQM4Dxd68SpbME0TniH-G5buh4,312
11
11
  nlbone/adapters/db/sqlalchemy/base.py,sha256=-5FnCMKETJ2xykhViHQuNBdbRMaxuieuatgiEl4Lllw,73
12
- nlbone/adapters/db/sqlalchemy/engine.py,sha256=m3nVY7KU12ksM3vK9fEgYEobrz9L8hfsgjI8cSNtBMY,3030
12
+ nlbone/adapters/db/sqlalchemy/engine.py,sha256=o2vgFbEfAQuGCPC0GZ-fxBB1T2NJtbeD9wOuCYEMZlA,3447
13
13
  nlbone/adapters/db/sqlalchemy/query_builder.py,sha256=mgj0mE0tbBVOAm1nd2nfR3p4EgV_NPYASsfP_4lF6yA,8504
14
+ nlbone/adapters/db/sqlalchemy/repository.py,sha256=RpJ_zjYWkrhplBoQwAPFj3zxy2Qk9N8xP0RWA9kZIf4,1658
14
15
  nlbone/adapters/db/sqlalchemy/schema.py,sha256=iiE42UT-DJh-ohezLFBWTBFN5WtrfZdtKToQDNLvoOs,1044
16
+ nlbone/adapters/db/sqlalchemy/uow.py,sha256=CVWYxW5w1bhLM9QAf0hF3Ky4Y52_W414fnztc5jwX64,2087
15
17
  nlbone/adapters/http_clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
18
  nlbone/adapters/http_clients/email_gateway.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
19
  nlbone/adapters/http_clients/uploadchi.py,sha256=qguGDQ2NGNSN3BOFSFUINn8mKS4LI23UGURJNhDUH2E,4647
18
20
  nlbone/adapters/http_clients/uploadchi_async.py,sha256=u-CHisQoTjuhyXb5h-TLDnfeUnN47e-VEbKamUnkaYI,3710
19
- nlbone/adapters/messaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ nlbone/adapters/messaging/__init__.py,sha256=UDAwu3s-JQmOZjWz2Nu0SgHhnkbeOhKDH_zLD75oWMY,40
22
+ nlbone/adapters/messaging/event_bus.py,sha256=odexwsfBhlPL887tz5-ZWFyJr7PBwA6pX0Bc4wUKMis,778
20
23
  nlbone/adapters/messaging/redis.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
24
  nlbone/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
25
  nlbone/config/logging.py,sha256=68WRQejEpL6eHEY_cOgdlOjndKc-RWth0n4YmXnceC8,5041
23
- nlbone/config/settings.py,sha256=f-wbPwtia09yxiDY6u1W7JefBAinJPRjTHi5UCw_psU,3563
26
+ nlbone/config/settings.py,sha256=mnR-BXIPR3QpDlpAupuzVEd1QPIyEwsGOV2xBOOZzi8,4403
24
27
  nlbone/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
28
  nlbone/core/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ nlbone/core/application/events.py,sha256=4P4wzKtYFfnbt2_60DGdM3oEFyWLULrZywbjKQEug6s,603
26
30
  nlbone/core/application/services.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ nlbone/core/application/use_case.py,sha256=JfrCdFx10ngz8KeUF67PxJCzScaySHzCf8kbBN0hfgk,247
27
32
  nlbone/core/application/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- nlbone/core/application/use_cases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- nlbone/core/application/use_cases/register_user.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
33
  nlbone/core/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ nlbone/core/domain/base.py,sha256=e68mygG9-iG-OLKx9oX2GyyJG0nY0TCJ7bBkkWHh-6M,1247
31
35
  nlbone/core/domain/events.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
36
  nlbone/core/domain/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- nlbone/core/ports/__init__.py,sha256=J24ldeLIQ_AWqKpdNbtaEWq4-G4cyWx8XEG0Dt6keao,29
37
+ nlbone/core/ports/__init__.py,sha256=jhHy1xMOMGBXrLTB56RXkrFfyaP2mQw7rmygfMrUf4M,214
34
38
  nlbone/core/ports/auth.py,sha256=v2NiH8FzKXZ1MabzQxaK7AQvnt-GQindYIki90swTE4,492
39
+ nlbone/core/ports/event_bus.py,sha256=pB3c6y-zkQNTHdT49CNTvEzrykc6mlkLMnjN8P8SN4U,322
35
40
  nlbone/core/ports/files.py,sha256=yJwW0kFWxkrT4ntfXDdJYyKSEWB6daEuaI6zgmeg0Gg,1596
36
41
  nlbone/core/ports/messaging.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- nlbone/core/ports/repo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ nlbone/core/ports/repo.py,sha256=d0CqdEslAIb6Zu8hE0GpO1qNDFr2LId152oqIs2h2-o,647
43
+ nlbone/core/ports/uow.py,sha256=fAJUQ--4YUyv00dx0ywOCNoulaKJCwmHiIVKqo40ITo,554
38
44
  nlbone/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
45
  nlbone/interfaces/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
46
  nlbone/interfaces/api/exception_handlers.py,sha256=Z3_dTGAmpHfgaMe_HTW89OpRtq-aamzaMTNkMIOdW3w,4092
41
47
  nlbone/interfaces/api/exceptions.py,sha256=uJWNEu5-cgoMedYebNHuIFJioXl_fnBhO89E6FINT2A,2259
42
48
  nlbone/interfaces/api/routers.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
49
  nlbone/interfaces/api/schemas.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- nlbone/interfaces/api/dependencies/__init__.py,sha256=V9Nf0JcQkJ0FjBYDRyQsJcLQUejZ2pSmmn05hdKgK1Y,167
50
+ nlbone/interfaces/api/dependencies/__init__.py,sha256=0kXv1SgMhEf8VTarLoY_471JM898t4L9yOJqHBaV1pU,207
45
51
  nlbone/interfaces/api/dependencies/auth.py,sha256=GHUlZ5L2N6ilOaOJqRicHrORB3AD3z2Y-qrO9Q2dNr4,1774
46
52
  nlbone/interfaces/api/dependencies/db.py,sha256=IqDVq1lcCCxd22FBUg523lVANM_j71BYAQtsbrHc4M8,465
53
+ nlbone/interfaces/api/dependencies/uow.py,sha256=BGU0LUIwS2pjtEFTwYN0zXH78ZT59pj9ngYo76EGnHI,1182
47
54
  nlbone/interfaces/api/middleware/__init__.py,sha256=Xcxg9Oy8uToPXaTSdBTKhst-hZwsaIEhqxx4mmo1bZI,157
48
55
  nlbone/interfaces/api/middleware/access_log.py,sha256=dEjk_m4fyQ72S2xLzDydDoaw2F9Tvmfl_acat1YThE0,972
49
56
  nlbone/interfaces/api/middleware/add_request_context.py,sha256=i8EV4BvZyrBcNfU-uTkybr7J7ypYvJq8mXSntM6oel4,1816
@@ -58,7 +65,7 @@ nlbone/interfaces/jobs/sync_tokens.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
58
65
  nlbone/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
66
  nlbone/utils/context.py,sha256=AUiN1jM0ebNMopZQoJSqWTfUHuVrp-HV8x6g7QsbEJ8,1601
60
67
  nlbone/utils/time.py,sha256=dC0ucyAmHdNf3wpA_JPinl2VJRubWqx2vcRpJsT3-0k,102
61
- nlbone-0.3.2.dist-info/METADATA,sha256=5rsqIn8P0PDRG4fpM2foF_7F0NCTE8lciIDu1H8YxLs,2298
62
- nlbone-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
63
- nlbone-0.3.2.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- nlbone-0.3.2.dist-info/RECORD,,
68
+ nlbone-0.4.0.dist-info/METADATA,sha256=M_qMQmxoDizcmBeS-YQFEU_3MKnz8eOWqtP2mpv6wQA,2298
69
+ nlbone-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
70
+ nlbone-0.4.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ nlbone-0.4.0.dist-info/RECORD,,
File without changes
File without changes
File without changes