mdb-engine 0.4.12__tar.gz → 0.4.14__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.
Files changed (104) hide show
  1. {mdb_engine-0.4.12/mdb_engine.egg-info → mdb_engine-0.4.14}/PKG-INFO +1 -1
  2. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/__init__.py +6 -5
  3. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/routing/websockets.py +95 -55
  4. {mdb_engine-0.4.12 → mdb_engine-0.4.14/mdb_engine.egg-info}/PKG-INFO +1 -1
  5. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/pyproject.toml +1 -1
  6. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/setup.py +1 -1
  7. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/LICENSE +0 -0
  8. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/MANIFEST.in +0 -0
  9. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/README.md +0 -0
  10. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/README.md +0 -0
  11. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/ARCHITECTURE.md +0 -0
  12. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/README.md +0 -0
  13. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/__init__.py +0 -0
  14. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/audit.py +0 -0
  15. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/base.py +0 -0
  16. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/casbin_factory.py +0 -0
  17. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/casbin_models.py +0 -0
  18. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/config_defaults.py +0 -0
  19. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/config_helpers.py +0 -0
  20. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/cookie_utils.py +0 -0
  21. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/csrf.py +0 -0
  22. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/decorators.py +0 -0
  23. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/dependencies.py +0 -0
  24. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/helpers.py +0 -0
  25. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/integration.py +0 -0
  26. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/jwt.py +0 -0
  27. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/middleware.py +0 -0
  28. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/oso_factory.py +0 -0
  29. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/provider.py +0 -0
  30. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/rate_limiter.py +0 -0
  31. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/restrictions.py +0 -0
  32. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/session_manager.py +0 -0
  33. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/shared_middleware.py +0 -0
  34. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/shared_users.py +0 -0
  35. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/token_lifecycle.py +0 -0
  36. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/token_store.py +0 -0
  37. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/users.py +0 -0
  38. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/auth/utils.py +0 -0
  39. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/__init__.py +0 -0
  40. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/commands/__init__.py +0 -0
  41. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/commands/generate.py +0 -0
  42. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/commands/migrate.py +0 -0
  43. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/commands/show.py +0 -0
  44. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/commands/validate.py +0 -0
  45. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/main.py +0 -0
  46. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/cli/utils.py +0 -0
  47. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/config.py +0 -0
  48. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/constants.py +0 -0
  49. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/README.md +0 -0
  50. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/__init__.py +0 -0
  51. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/app_registration.py +0 -0
  52. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/app_secrets.py +0 -0
  53. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/connection.py +0 -0
  54. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/encryption.py +0 -0
  55. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/engine.py +0 -0
  56. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/index_management.py +0 -0
  57. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/manifest.py +0 -0
  58. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/ray_integration.py +0 -0
  59. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/seeding.py +0 -0
  60. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/service_initialization.py +0 -0
  61. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/core/types.py +0 -0
  62. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/README.md +0 -0
  63. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/__init__.py +0 -0
  64. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/abstraction.py +0 -0
  65. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/connection.py +0 -0
  66. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/query_validator.py +0 -0
  67. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/resource_limiter.py +0 -0
  68. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/database/scoped_wrapper.py +0 -0
  69. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/dependencies.py +0 -0
  70. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/di/__init__.py +0 -0
  71. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/di/container.py +0 -0
  72. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/di/providers.py +0 -0
  73. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/di/scopes.py +0 -0
  74. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/embeddings/README.md +0 -0
  75. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/embeddings/__init__.py +0 -0
  76. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/embeddings/dependencies.py +0 -0
  77. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/embeddings/service.py +0 -0
  78. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/exceptions.py +0 -0
  79. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/indexes/README.md +0 -0
  80. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/indexes/__init__.py +0 -0
  81. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/indexes/helpers.py +0 -0
  82. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/indexes/manager.py +0 -0
  83. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/memory/README.md +0 -0
  84. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/memory/__init__.py +0 -0
  85. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/memory/service.py +0 -0
  86. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/observability/README.md +0 -0
  87. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/observability/__init__.py +0 -0
  88. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/observability/health.py +0 -0
  89. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/observability/logging.py +0 -0
  90. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/observability/metrics.py +0 -0
  91. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/repositories/__init__.py +0 -0
  92. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/repositories/base.py +0 -0
  93. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/repositories/mongo.py +0 -0
  94. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/repositories/unit_of_work.py +0 -0
  95. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/routing/README.md +0 -0
  96. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/routing/__init__.py +0 -0
  97. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/utils/__init__.py +0 -0
  98. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine/utils/mongo.py +0 -0
  99. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine.egg-info/SOURCES.txt +0 -0
  100. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine.egg-info/dependency_links.txt +0 -0
  101. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine.egg-info/entry_points.txt +0 -0
  102. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine.egg-info/requires.txt +0 -0
  103. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/mdb_engine.egg-info/top_level.txt +0 -0
  104. {mdb_engine-0.4.12 → mdb_engine-0.4.14}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdb-engine
3
- Version: 0.4.12
3
+ Version: 0.4.14
4
4
  Summary: MongoDB Engine
5
5
  Home-page: https://github.com/ranfysvalle02/mdb-engine
6
6
  Author: Fabian Valle
@@ -82,11 +82,12 @@ from .repositories import Entity, MongoRepository, Repository, UnitOfWork
82
82
  from .utils import clean_mongo_doc, clean_mongo_docs
83
83
 
84
84
  __version__ = (
85
- "0.4.12" # Fix CSRF middleware rejecting WebSocket connections
86
- # - Skip CSRF middleware on child apps in multi-app setups (parent handles it)
87
- # - Merge child app public routes into parent CSRF exempt list
88
- # - WebSocket connections now work correctly in multi-app SSO setups
89
- # - Security maintained: parent app CSRF middleware protects all routes
85
+ "0.4.14" # WebSocket security documentation and test updates
86
+ # - Comprehensive security guide (WEBSOCKET_SECURITY_MULTI_APP_SSO.md)
87
+ # - Updated all documentation to reflect subprotocol-only authentication
88
+ # - Added integration tests for subprotocol authentication in multi-app SSO
89
+ # - Enhanced unit test documentation with security-focused explanations
90
+ # - Complete test coverage for WebSocket security scenarios
90
91
  )
91
92
 
92
93
  __all__ = [
@@ -311,10 +311,15 @@ async def authenticate_websocket(
311
311
  websocket: Any, app_slug: str, require_auth: bool = True
312
312
  ) -> tuple[str | None, str | None]:
313
313
  """
314
- Authenticate a WebSocket connection.
314
+ Authenticate a WebSocket connection via Sec-WebSocket-Protocol header.
315
+
316
+ Uses subprotocol tunneling to pass JWT tokens securely:
317
+ - Client: new WebSocket(url, [token])
318
+ - Server: Extracts token from sec-websocket-protocol header
319
+ - Bypasses CSRF issues and avoids URL logging risks
315
320
 
316
321
  Args:
317
- websocket: FastAPI WebSocket instance
322
+ websocket: FastAPI WebSocket instance (can access headers before accept)
318
323
  app_slug: App slug for context
319
324
  require_auth: Whether authentication is required
320
325
 
@@ -335,59 +340,57 @@ async def authenticate_websocket(
335
340
  return None, None
336
341
 
337
342
  try:
338
- # Try to get token from query params or cookies
339
343
  token = None
340
- query_token = None
341
- cookie_token = None
342
-
343
- # Check query parameters
344
- # FastAPI WebSocket query params are accessed via websocket.query_params
345
- if hasattr(websocket, "query_params"):
346
- if websocket.query_params:
347
- query_token = websocket.query_params.get("token")
348
- logger.info(
349
- f"WebSocket query_params for app '{app_slug}': {dict(websocket.query_params)}"
350
- )
351
- if query_token:
352
- token = query_token
353
- logger.info(
354
- f"WebSocket token found in query params for app "
355
- f"'{app_slug}' (length: {len(query_token)})"
356
- )
357
- else:
358
- logger.info(f"WebSocket query_params is empty for app '{app_slug}'")
359
- else:
360
- logger.warning(f"WebSocket has no query_params attribute for app '{app_slug}'")
344
+ selected_subprotocol = None
361
345
 
362
- # If no token in query, try to get from cookies (if available)
363
- # Check both ws_token (non-httponly, for JS access) and token (httponly)
364
- if not token:
365
- if hasattr(websocket, "cookies"):
366
- cookie_token = websocket.cookies.get("ws_token") or websocket.cookies.get("token")
367
- if cookie_token:
368
- token = cookie_token
369
- logger.debug(f"WebSocket token found in cookies for app '{app_slug}'")
370
- else:
371
- logger.debug(f"WebSocket has no cookies attribute for app '{app_slug}'")
372
-
373
- logger.info(
374
- f"WebSocket auth check for app '{app_slug}': "
375
- f"query_token={bool(query_token)}, "
376
- f"cookie_token={bool(cookie_token)}, "
377
- f"final_token={bool(token)}, require_auth={require_auth}"
378
- )
346
+ # Sec-WebSocket-Protocol (Subprotocol Tunneling)
347
+ # Browsers allow: new WebSocket(url, ["token", "THE_JWT_STRING"])
348
+ # This bypasses CSRF issues and avoids URL logging risks
349
+ try:
350
+ protocol_header = None
351
+ # Try multiple ways to access headers (FastAPI WebSocket compatibility)
352
+ if hasattr(websocket, "headers") and websocket.headers is not None:
353
+ # FastAPI WebSocket.headers is case-insensitive dict-like
354
+ protocol_header = websocket.headers.get("sec-websocket-protocol")
355
+
356
+ # Fallback: access via scope (ASGI standard) if headers not available
357
+ if not protocol_header and hasattr(websocket, "scope") and "headers" in websocket.scope:
358
+ headers_dict = dict(websocket.scope["headers"])
359
+ # Headers are bytes in ASGI, need to decode
360
+ protocol_header_bytes = headers_dict.get(b"sec-websocket-protocol")
361
+ if protocol_header_bytes:
362
+ protocol_header = protocol_header_bytes.decode("utf-8")
363
+
364
+ if protocol_header:
365
+ # Header format: "protocol1, protocol2, ..." or just "token"
366
+ protocols = [p.strip() for p in protocol_header.split(",")]
367
+ logger.debug(f"WebSocket subprotocols for app '{app_slug}': {protocols}")
368
+
369
+ # Look for a JWT-like token in the protocols
370
+ # JWTs are typically long (>20 chars) and don't contain spaces
371
+ for protocol in protocols:
372
+ if len(protocol) > 20 and " " not in protocol:
373
+ token = protocol
374
+ selected_subprotocol = protocol
375
+ logger.info(
376
+ f"WebSocket token found in subprotocol for app '{app_slug}' "
377
+ f"(length: {len(protocol)})"
378
+ )
379
+ break
380
+ except (AttributeError, TypeError, KeyError, UnicodeDecodeError) as e:
381
+ logger.debug(f"Could not access WebSocket headers for subprotocol check: {e}")
379
382
 
380
383
  if not token:
381
384
  logger.warning(
382
- f"No token found for WebSocket connection to app '{app_slug}' "
383
- f"(require_auth={require_auth})"
385
+ f"No token found in subprotocol header for WebSocket connection to app "
386
+ f"'{app_slug}' (require_auth={require_auth}). "
387
+ f"Use: new WebSocket(url, [token]) to pass JWT token as subprotocol."
384
388
  )
385
389
  if require_auth:
386
- # Don't close before accepting - return error info instead
387
- # The caller will handle closing after accept
388
390
  return None, None # Signal auth failure
389
391
  return None, None
390
392
 
393
+ # Decode and validate token
391
394
  import jwt
392
395
 
393
396
  from ..auth.dependencies import SECRET_KEY
@@ -398,6 +401,13 @@ async def authenticate_websocket(
398
401
  user_id = payload.get("sub") or payload.get("user_id")
399
402
  user_email = payload.get("email")
400
403
 
404
+ # Store selected subprotocol on websocket scope for accept() to use
405
+ if selected_subprotocol:
406
+ # Store in scope so _accept_websocket_connection can access it
407
+ if hasattr(websocket, "scope"):
408
+ websocket.scope["_selected_subprotocol"] = selected_subprotocol
409
+ logger.debug(f"Stored subprotocol '{selected_subprotocol}' for WebSocket accept")
410
+
401
411
  logger.info(f"WebSocket authenticated successfully for app '{app_slug}': {user_email}")
402
412
  return user_id, user_email
403
413
  except (jwt.ExpiredSignatureError, jwt.InvalidTokenError) as decode_error:
@@ -409,7 +419,6 @@ async def authenticate_websocket(
409
419
  except (ValueError, TypeError, AttributeError, KeyError, RuntimeError) as e:
410
420
  logger.error(f"WebSocket authentication failed for app '{app_slug}': {e}", exc_info=True)
411
421
  if require_auth:
412
- # Don't close before accepting - return error info instead
413
422
  return None, None # Signal auth failure
414
423
  return None, None
415
424
 
@@ -470,11 +479,32 @@ def get_message_handler(
470
479
 
471
480
 
472
481
  async def _accept_websocket_connection(websocket: Any, app_slug: str) -> None:
473
- """Accept WebSocket connection with error handling."""
482
+ """
483
+ Accept WebSocket connection with subprotocol support.
484
+
485
+ If authentication found a token in the Sec-WebSocket-Protocol header,
486
+ we must accept with that specific subprotocol or the browser will reject
487
+ the connection.
488
+ """
474
489
  try:
475
- await websocket.accept()
490
+ # Check if auth stored a selected subprotocol
491
+ selected_subprotocol = None
492
+ if hasattr(websocket, "scope"):
493
+ selected_subprotocol = websocket.scope.get("_selected_subprotocol")
494
+
495
+ if selected_subprotocol:
496
+ # Accept with the specific subprotocol the client requested
497
+ await websocket.accept(subprotocol=selected_subprotocol)
498
+ logger.info(
499
+ f"✅ WebSocket accepted for app '{app_slug}' "
500
+ f"with subprotocol '{selected_subprotocol}'"
501
+ )
502
+ else:
503
+ # Standard accept without subprotocol
504
+ await websocket.accept()
505
+ logger.info(f"✅ WebSocket accepted for app '{app_slug}'")
506
+
476
507
  print(f"✅ [WEBSOCKET ACCEPTED] App: '{app_slug}'")
477
- logger.info(f"✅ WebSocket accepted for app '{app_slug}'")
478
508
  except (RuntimeError, ConnectionError, OSError) as accept_error:
479
509
  print(f"❌ [WEBSOCKET ACCEPT FAILED] App: '{app_slug}', Error: {accept_error}")
480
510
  logger.error(
@@ -654,13 +684,23 @@ def create_websocket_endpoint(
654
684
  f"(require_auth={require_auth}, query_params={query_str})"
655
685
  )
656
686
 
657
- # Accept connection FIRST (required before we can do anything)
658
- await _accept_websocket_connection(websocket, app_slug)
687
+ # CRITICAL: Authenticate BEFORE accepting connection
688
+ # This prevents CSRF middleware from rejecting established connections
689
+ # We can access headers/query_params before accept() is called
690
+ user_id, user_email = await authenticate_websocket(websocket, app_slug, require_auth)
659
691
 
660
- # Authenticate connection (after accept, so we can close properly if needed)
661
- user_id, user_email = await _authenticate_websocket_connection(
662
- websocket, app_slug, require_auth
663
- )
692
+ # Handle authentication failure
693
+ if require_auth and not user_id:
694
+ logger.warning(
695
+ f"WebSocket authentication failed for app '{app_slug}' - rejecting connection"
696
+ )
697
+ # Reject without accepting - FastAPI will send 403 if accept() not called
698
+ # We can't call websocket.close() before accept(), so we just return
699
+ # The connection will be rejected by the server
700
+ return
701
+
702
+ # Accept connection (with subprotocol if token was in protocol header)
703
+ await _accept_websocket_connection(websocket, app_slug)
664
704
 
665
705
  # Connect with metadata (websocket already accepted)
666
706
  connection = await manager.connect(websocket, user_id=user_id, user_email=user_email)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdb-engine
3
- Version: 0.4.12
3
+ Version: 0.4.14
4
4
  Summary: MongoDB Engine
5
5
  Home-page: https://github.com/ranfysvalle02/mdb-engine
6
6
  Author: Fabian Valle
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mdb-engine"
7
- version = "0.4.12"
7
+ version = "0.4.14"
8
8
  description = "MongoDB Engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -14,7 +14,7 @@ if readme_file.exists():
14
14
 
15
15
  setup(
16
16
  name="mdb-engine",
17
- version="0.4.12",
17
+ version="0.4.14",
18
18
  description="MongoDB Engine",
19
19
  long_description=long_description,
20
20
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes
File without changes