matrx-orm 3.0.21__tar.gz → 3.0.22__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 (175) hide show
  1. matrx_orm-3.0.22/DESKTOP-API-INTEGRATION.md +494 -0
  2. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/PKG-INFO +4 -1
  3. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/README.md +1 -0
  4. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/TRANSACTION_POOLER_PLAN.MD +2 -1
  5. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/pyproject.toml +4 -1
  6. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/__init__.py +17 -0
  7. matrx_orm-3.0.22/src/matrx_orm/api/__init__.py +37 -0
  8. matrx_orm-3.0.22/src/matrx_orm/api/auth.py +164 -0
  9. matrx_orm-3.0.22/src/matrx_orm/api/config.py +70 -0
  10. matrx_orm-3.0.22/src/matrx_orm/api/handlers.py +291 -0
  11. matrx_orm-3.0.22/src/matrx_orm/api/protocol.py +154 -0
  12. matrx_orm-3.0.22/src/matrx_orm/api/server.py +434 -0
  13. matrx_orm-3.0.22/tests/level1/test_api_auth.py +133 -0
  14. matrx_orm-3.0.22/tests/level1/test_api_config.py +57 -0
  15. matrx_orm-3.0.22/tests/level1/test_api_handlers.py +175 -0
  16. matrx_orm-3.0.22/tests/level1/test_api_protocol.py +183 -0
  17. matrx_orm-3.0.22/tests/sample_project_desktop/.env.example +10 -0
  18. matrx_orm-3.0.22/tests/sample_project_desktop/README.md +57 -0
  19. matrx_orm-3.0.22/tests/sample_project_desktop/__init__.py +0 -0
  20. matrx_orm-3.0.22/tests/sample_project_desktop/client_example.py +197 -0
  21. matrx_orm-3.0.22/tests/sample_project_desktop/client_example.ts +352 -0
  22. matrx_orm-3.0.22/tests/sample_project_desktop/server.py +130 -0
  23. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.arman/pending/versioning/INITIAL.md +0 -0
  24. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.env.example +0 -0
  25. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.github/workflows/publish.yml +0 -0
  26. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.gitignore +0 -0
  27. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.python-version +0 -0
  28. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.vscode/settings.json +0 -0
  29. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/CLAUDE.md +0 -0
  30. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/MIGRATIONS.md +0 -0
  31. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/MODULE_README.md +0 -0
  32. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/RESERVED_NAMES.md +0 -0
  33. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/TASKS-TEMPLATE.md +0 -0
  34. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/TASKS.md +0 -0
  35. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/WRITE-QUEUE-UPGRADE.md +0 -0
  36. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/database/orm/extended/managers/ai_model_base.py +0 -0
  37. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/docs/migrations.md +0 -0
  38. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/extended_jan5_backup.py +0 -0
  39. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/main.py +0 -0
  40. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/release.sh +0 -0
  41. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/scripts/git-branches.sh +0 -0
  42. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/scripts/publish.sh +0 -0
  43. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/scripts/release.sh +0 -0
  44. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/MODULE_README.md +0 -0
  45. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/MODULE_README.md +0 -0
  46. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/MODULE_README.md +0 -0
  47. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/__init__.py +0 -0
  48. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/base_adapter.py +0 -0
  49. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/postgresql.py +0 -0
  50. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/client/MODULE_README.md +0 -0
  51. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/client/__init__.py +0 -0
  52. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/client/postgres_connection.py +0 -0
  53. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/EXTENDED-TASK.md +0 -0
  54. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/MODULE_README.md +0 -0
  55. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  56. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/TASKS.md +0 -0
  57. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/__init__.py +0 -0
  58. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/async_db_manager.py +0 -0
  59. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/base.py +0 -0
  60. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/config.py +0 -0
  61. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/expressions.py +0 -0
  62. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/extended.py +0 -0
  63. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/fields.py +0 -0
  64. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/model_dto.py +0 -0
  65. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/model_view.py +0 -0
  66. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/paginator.py +0 -0
  67. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/pydantic_bridge.py +0 -0
  68. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/registry.py +0 -0
  69. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/relations.py +0 -0
  70. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/signals.py +0 -0
  71. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/transaction.py +0 -0
  72. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/types.py +0 -0
  73. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/write_queue.py +0 -0
  74. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/error_handling.py +0 -0
  75. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/exceptions.py +0 -0
  76. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/extended/__init__.py +0 -0
  77. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/extended/app_error_handler.py +0 -0
  78. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/middleware/__init__.py +0 -0
  79. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/middleware/base.py +0 -0
  80. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/MODULE_README.md +0 -0
  81. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/__init__.py +0 -0
  82. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/cli.py +0 -0
  83. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/ddl.py +0 -0
  84. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/diff.py +0 -0
  85. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/executor.py +0 -0
  86. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/integration.py +0 -0
  87. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/loader.py +0 -0
  88. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/operations.py +0 -0
  89. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/state.py +0 -0
  90. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/table_filter.py +0 -0
  91. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/MODULE_README.md +0 -0
  92. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/__init__.py +0 -0
  93. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/create.py +0 -0
  94. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/delete.py +0 -0
  95. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/read.py +0 -0
  96. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/update.py +0 -0
  97. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/py.typed +0 -0
  98. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/MODULE_README.md +0 -0
  99. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/__init__.py +0 -0
  100. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/db_objects.py +0 -0
  101. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  102. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  103. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/MODULE_README.md +0 -0
  104. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/__init__.py +0 -0
  105. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/builder.py +0 -0
  106. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/executor.py +0 -0
  107. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/MODULE_README.md +0 -0
  108. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/__init__.py +0 -0
  109. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/code_handler.py +0 -0
  110. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/columns.py +0 -0
  111. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/common.py +0 -0
  112. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/generator.py +0 -0
  113. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  114. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  115. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  116. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  117. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/relationships.py +0 -0
  118. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/runner.py +0 -0
  119. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/schema.py +0 -0
  120. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/schema_manager.py +0 -0
  121. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/tables.py +0 -0
  122. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/views.py +0 -0
  123. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/MODULE_README.md +0 -0
  124. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/__init__.py +0 -0
  125. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/executor.py +0 -0
  126. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/queries.py +0 -0
  127. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/registry.py +0 -0
  128. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/types.py +0 -0
  129. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/utils.py +0 -0
  130. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/state.py +0 -0
  131. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/utils/__init__.py +0 -0
  132. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/utils/sql_utils.py +0 -0
  133. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/utils/type_converters.py +0 -0
  134. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/MODULE_README.md +0 -0
  135. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/__init__.py +0 -0
  136. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/conftest.py +0 -0
  137. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/MODULE_README.md +0 -0
  138. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/__init__.py +0 -0
  139. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_config.py +0 -0
  140. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_ddl_generator.py +0 -0
  141. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_exceptions.py +0 -0
  142. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_fields.py +0 -0
  143. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_migration_diff_types.py +0 -0
  144. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_migration_loader.py +0 -0
  145. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_model_instance.py +0 -0
  146. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_model_meta.py +0 -0
  147. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_query_builder.py +0 -0
  148. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_query_executor_sql.py +0 -0
  149. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_registry.py +0 -0
  150. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_relations.py +0 -0
  151. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_state_cache.py +0 -0
  152. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/MODULE_README.md +0 -0
  153. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/__init__.py +0 -0
  154. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/conftest.py +0 -0
  155. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_bulk_ops.py +0 -0
  156. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_cache_integration.py +0 -0
  157. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_crud.py +0 -0
  158. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_foreign_keys.py +0 -0
  159. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_m2m.py +0 -0
  160. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_manager.py +0 -0
  161. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_migrations_live.py +0 -0
  162. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_query_execution.py +0 -0
  163. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_schema_diff.py +0 -0
  164. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/.env.example +0 -0
  165. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/README.md +0 -0
  166. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/__init__.py +0 -0
  167. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/generate.py +0 -0
  168. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/generated/.gitkeep +0 -0
  169. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/matrx_orm.yaml +0 -0
  170. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/test_schema_generation.py +0 -0
  171. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/schema/entity_tests.py +0 -0
  172. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/schema/test_base_generation.py +0 -0
  173. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/schema/test_generate_schema.py +0 -0
  174. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/test_model_cls_refactor.py +0 -0
  175. {matrx_orm-3.0.21 → matrx_orm-3.0.22}/uv.lock +0 -0
@@ -0,0 +1,494 @@
1
+ # Desktop App API Integration Guide
2
+
3
+ > Complete guide for integrating matrx-orm with desktop applications
4
+ > (Electron, Tauri, or any local HTTP client).
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ The `matrx_orm.api` module provides a **localhost-only HTTP + WebSocket server**
11
+ that exposes your ORM managers as JSON-RPC endpoints. Your desktop frontend
12
+ communicates with this server using standard HTTP requests.
13
+
14
+ ```
15
+ Desktop App (Electron/Tauri)
16
+
17
+ ├── POST /auth/token → Get bearer token
18
+ ├── POST /rpc → CRUD operations
19
+ ├── GET /ws → WebSocket (streaming)
20
+ ├── GET /health → Health check
21
+ └── GET /methods → List available methods
22
+
23
+
24
+ Python Backend (matrx-orm API Server)
25
+
26
+
27
+ PostgreSQL
28
+ ```
29
+
30
+ ---
31
+
32
+ ## 1. Installation
33
+
34
+ ```bash
35
+ pip install matrx-orm[api]
36
+ ```
37
+
38
+ This adds `aiohttp` as an optional dependency.
39
+
40
+ ---
41
+
42
+ ## 2. Python Backend Setup
43
+
44
+ ### Minimal Server
45
+
46
+ ```python
47
+ import asyncio
48
+ import os
49
+ from matrx_orm import register_database, DatabaseProjectConfig, BaseManager, Model
50
+ from matrx_orm.api import APIServer, APIConfig
51
+
52
+ # 1. Register your database
53
+ register_database(DatabaseProjectConfig(
54
+ name="myapp",
55
+ host=os.getenv("DB_HOST", "localhost"),
56
+ port=os.getenv("DB_PORT", "5432"),
57
+ database_name=os.getenv("DB_NAME", "myapp"),
58
+ user=os.getenv("DB_USER", "postgres"),
59
+ password=os.getenv("DB_PASSWORD", "postgres"),
60
+ ))
61
+
62
+ # 2. Import or define your models and managers
63
+ # (Typically these are auto-generated by schema introspection)
64
+ from myapp.models import User, Note
65
+ from myapp.managers import UserManager, NoteManager
66
+
67
+ user_mgr = UserManager(model=User)
68
+ note_mgr = NoteManager(model=Note)
69
+
70
+ # 3. Create and configure the API server
71
+ server = APIServer(config=APIConfig(
72
+ secret=os.getenv("API_SECRET", ""), # Auto-generates if empty
73
+ port=8745,
74
+ ))
75
+
76
+ server.register_manager("users", user_mgr)
77
+ server.register_manager("notes", note_mgr)
78
+
79
+ # 4. Start
80
+ asyncio.run(server.start())
81
+ ```
82
+
83
+ ### Configuration Options
84
+
85
+ | Parameter | Default | Description |
86
+ |-----------|---------|-------------|
87
+ | `host` | `"127.0.0.1"` | Bind address. **Keep this as localhost.** |
88
+ | `port` | `8745` | TCP port |
89
+ | `secret` | Auto-generated | Shared secret for token auth |
90
+ | `token_ttl` | `86400` (24h) | Token lifetime in seconds |
91
+ | `max_payload_bytes` | `10MB` | Maximum request body size |
92
+ | `cors_origins` | localhost:3000/5173 | Allowed CORS origins |
93
+ | `enable_websocket` | `True` | Enable `/ws` endpoint |
94
+ | `log_requests` | `True` | Log each incoming RPC call |
95
+
96
+ ### Custom Methods
97
+
98
+ You can register arbitrary async functions as RPC methods:
99
+
100
+ ```python
101
+ async def search_notes(query: str, limit: int = 10, **params):
102
+ # Custom logic using raw SQL, multiple managers, etc.
103
+ results = await Note.filter(title__icontains=query).limit(limit).all()
104
+ return [r.to_dict() for r in results]
105
+
106
+ server.register_method("notes.search", search_notes)
107
+ ```
108
+
109
+ ### Background Mode
110
+
111
+ For embedding in a larger application:
112
+
113
+ ```python
114
+ await server.start_background()
115
+
116
+ # ... your app runs ...
117
+
118
+ await server.stop()
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 3. Authentication Flow
124
+
125
+ ### Step 1: Obtain Token
126
+
127
+ ```
128
+ POST /auth/token
129
+ Content-Type: application/json
130
+
131
+ {
132
+ "secret": "your-shared-secret",
133
+ "client_id": "electron"
134
+ }
135
+ ```
136
+
137
+ Response:
138
+ ```json
139
+ {
140
+ "token": "eyJ...<base64>.<hex-signature>",
141
+ "expires_in": 86400
142
+ }
143
+ ```
144
+
145
+ ### Step 2: Use Token on All Requests
146
+
147
+ ```
148
+ POST /rpc
149
+ Authorization: Bearer eyJ...<base64>.<hex-signature>
150
+ Content-Type: application/json
151
+
152
+ {
153
+ "method": "users.load_items",
154
+ "params": {},
155
+ "id": 1
156
+ }
157
+ ```
158
+
159
+ ### Step 3: Revoke Token (Optional)
160
+
161
+ ```
162
+ POST /auth/revoke
163
+ Authorization: Bearer <current-token>
164
+ Content-Type: application/json
165
+
166
+ {
167
+ "token": "<token-to-revoke>"
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## 4. RPC Protocol
174
+
175
+ ### Request Format
176
+
177
+ ```json
178
+ {
179
+ "method": "namespace.operation",
180
+ "params": { "key": "value" },
181
+ "id": 1
182
+ }
183
+ ```
184
+
185
+ ### Success Response
186
+
187
+ ```json
188
+ {
189
+ "result": { "id": "...", "username": "alice" },
190
+ "error": null,
191
+ "id": 1
192
+ }
193
+ ```
194
+
195
+ ### Error Response
196
+
197
+ ```json
198
+ {
199
+ "result": null,
200
+ "error": {
201
+ "code": 1001,
202
+ "message": "No User found matching: id=abc-123",
203
+ "data": { "model": "User" }
204
+ },
205
+ "id": 1
206
+ }
207
+ ```
208
+
209
+ ### Batch Requests
210
+
211
+ Send an array of requests to execute them concurrently:
212
+
213
+ ```json
214
+ [
215
+ { "method": "users.count", "params": {}, "id": 1 },
216
+ { "method": "notes.count", "params": {}, "id": 2 },
217
+ { "method": "notes.load_items", "params": { "user_id": "..." }, "id": 3 }
218
+ ]
219
+ ```
220
+
221
+ Response is an array of responses in the same order.
222
+
223
+ ---
224
+
225
+ ## 5. Available Methods
226
+
227
+ Every registered manager exposes these methods under its namespace:
228
+
229
+ ### CRUD — Single Item
230
+
231
+ | Method | Params | Returns |
232
+ |--------|--------|---------|
233
+ | `load_item` | `id=...` (or any PK filter) | `dict` |
234
+ | `load_item_or_none` | `id=...` | `dict \| null` |
235
+ | `load_by_id` | `item_id=...` | `dict` |
236
+ | `create_item` | field=value pairs | `dict` |
237
+ | `update_item` | `item_id=..., field=value` | `dict` |
238
+ | `delete_item` | `item_id=...` | `bool` |
239
+ | `exists` | `item_id=...` | `bool` |
240
+
241
+ ### CRUD — Bulk
242
+
243
+ | Method | Params | Returns |
244
+ |--------|--------|---------|
245
+ | `load_items` | filter kwargs | `list[dict]` |
246
+ | `load_items_by_ids` | `ids=[...]` | `list[dict]` |
247
+ | `count` | filter kwargs | `int` |
248
+ | `create_items` | `items_data=[{...}, ...]` | `list[dict]` |
249
+ | `update_where` | `filters={...}, field=value` | `UpdateResult` |
250
+ | `delete_where` | filter kwargs | `int` |
251
+ | `upsert_item` | `data={...}, conflict_fields=[...], update_fields=[...]` | `dict` |
252
+ | `get_or_create` | `defaults={...}, lookup_field=value` | `dict` |
253
+
254
+ ### Relationships
255
+
256
+ | Method | Params | Returns |
257
+ |--------|--------|---------|
258
+ | `get_item_with_fk` | `item_id=..., fk_field="..."` | `[item, related]` |
259
+ | `get_item_with_ifk` | `item_id=..., ifk_name="..."` | `[item, [related...]]` |
260
+ | `get_item_with_m2m` | `item_id=..., m2m_name="..."` | `[item, [related...]]` |
261
+ | `get_item_with_all_related` | `item_id=...` | `[item, all_relations]` |
262
+ | `add_m2m` | `item_id=..., m2m_name="...", *target_ids` | `int` |
263
+ | `remove_m2m` | `item_id=..., m2m_name="...", *target_ids` | `int` |
264
+ | `set_m2m` | `item_id=..., m2m_name="...", target_ids=[...]` | `null` |
265
+ | `clear_m2m` | `item_id=..., m2m_name="..."` | `int` |
266
+
267
+ ### Dict Variants
268
+
269
+ | Method | Returns |
270
+ |--------|---------|
271
+ | `get_item_dict` | `dict` |
272
+ | `create_item_get_dict` | `dict` |
273
+ | `update_item_get_dict` | `dict` |
274
+
275
+ ---
276
+
277
+ ## 6. Error Codes
278
+
279
+ | Code | Name | When |
280
+ |------|------|------|
281
+ | `-32700` | `PARSE_ERROR` | Invalid JSON |
282
+ | `-32600` | `INVALID_REQUEST` | Missing method or bad format |
283
+ | `-32601` | `METHOD_NOT_FOUND` | Unknown namespace or method |
284
+ | `-32602` | `INVALID_PARAMS` | Wrong parameter types |
285
+ | `-32603` | `INTERNAL_ERROR` | Unhandled server error |
286
+ | `1001` | `NOT_FOUND` | Record does not exist |
287
+ | `1002` | `VALIDATION_ERROR` | Data validation failed |
288
+ | `1003` | `INTEGRITY_ERROR` | DB constraint violation |
289
+ | `1004` | `AUTH_REQUIRED` | No token provided |
290
+ | `1005` | `AUTH_INVALID` | Token expired or invalid |
291
+ | `1008` | `CONNECTION_ERROR` | Database connection failed |
292
+ | `1009` | `QUERY_ERROR` | SQL query error |
293
+
294
+ ---
295
+
296
+ ## 7. WebSocket Usage
297
+
298
+ The WebSocket endpoint provides the same RPC interface with lower overhead
299
+ for high-frequency calls.
300
+
301
+ ### Connection Flow
302
+
303
+ ```javascript
304
+ const ws = new WebSocket("ws://127.0.0.1:8745/ws");
305
+
306
+ ws.onopen = () => {
307
+ // First message: authenticate
308
+ ws.send(JSON.stringify({ token: "your-bearer-token" }));
309
+ };
310
+
311
+ ws.onmessage = (event) => {
312
+ const data = JSON.parse(event.data);
313
+
314
+ if (data.type === "auth_ok") {
315
+ // Authenticated — now send RPC calls
316
+ ws.send(JSON.stringify({
317
+ method: "users.load_items",
318
+ params: {},
319
+ id: 1,
320
+ }));
321
+ }
322
+
323
+ if (data.result !== undefined) {
324
+ // RPC response
325
+ console.log("Result:", data.result);
326
+ }
327
+ };
328
+ ```
329
+
330
+ ---
331
+
332
+ ## 8. Client Libraries
333
+
334
+ ### TypeScript (Electron / Tauri)
335
+
336
+ See `tests/sample_project_desktop/client_example.ts` for a complete,
337
+ zero-dependency TypeScript client with both HTTP and WebSocket support.
338
+
339
+ ### Python
340
+
341
+ See `tests/sample_project_desktop/client_example.py` for an `aiohttp`-based
342
+ Python client.
343
+
344
+ ---
345
+
346
+ ## 9. Security Checklist
347
+
348
+ - [x] Server binds to `127.0.0.1` only — not reachable from network
349
+ - [x] HMAC-SHA256 signed tokens — tamper-proof
350
+ - [x] Token expiry (configurable TTL, default 24h)
351
+ - [x] Token revocation support
352
+ - [x] CORS restricted to specific origins
353
+ - [x] Only allow-listed manager methods are callable (no arbitrary code exec)
354
+ - [x] ORM errors mapped to safe error codes (no stack traces leaked)
355
+ - [x] Maximum payload size enforced
356
+
357
+ ### For Production Desktop Apps
358
+
359
+ Additional steps to implement in your application layer:
360
+
361
+ 1. **Secret delivery**: Write the auto-generated secret to a file readable
362
+ only by the current user (`chmod 600`), then read it from the desktop app.
363
+ 2. **Process isolation**: Run the Python backend as a child process of the
364
+ desktop app. Kill it on app exit.
365
+ 3. **Port discovery**: If port 8745 is taken, use port 0 (OS-assigned) and
366
+ communicate the actual port back to the parent process.
367
+ 4. **Scoped tokens**: Issue tokens with limited scopes (e.g. `["users.read"]`)
368
+ to restrict what the frontend can do.
369
+
370
+ ---
371
+
372
+ ## 10. Integration with matrx-local
373
+
374
+ If you're using the [matrx-local](https://github.com/armanisadeghi/matrx-local)
375
+ desktop application, here's what needs to happen on that side:
376
+
377
+ ### Required Changes in matrx-local
378
+
379
+ 1. **Install the Python backend** — The Electron app should bundle or spawn
380
+ a Python process that runs the matrx-orm API server.
381
+
382
+ 2. **Process management** — Add a module to spawn the Python backend on app
383
+ start and kill it on app quit:
384
+
385
+ ```typescript
386
+ // main/python-backend.ts
387
+ import { spawn, ChildProcess } from "child_process";
388
+ import { app } from "electron";
389
+ import path from "path";
390
+
391
+ let pythonProcess: ChildProcess | null = null;
392
+
393
+ export function startPythonBackend(secret: string): void {
394
+ const scriptPath = path.join(__dirname, "../python/server.py");
395
+
396
+ pythonProcess = spawn("python", [scriptPath], {
397
+ env: {
398
+ ...process.env,
399
+ API_SECRET: secret,
400
+ API_PORT: "8745",
401
+ },
402
+ });
403
+
404
+ pythonProcess.stdout?.on("data", (data) => {
405
+ console.log(`[python] ${data}`);
406
+ });
407
+
408
+ pythonProcess.stderr?.on("data", (data) => {
409
+ console.error(`[python] ${data}`);
410
+ });
411
+ }
412
+
413
+ export function stopPythonBackend(): void {
414
+ pythonProcess?.kill();
415
+ pythonProcess = null;
416
+ }
417
+
418
+ app.on("will-quit", stopPythonBackend);
419
+ ```
420
+
421
+ 3. **Client initialization** — Use the `MatrxClient` class from
422
+ `client_example.ts`:
423
+
424
+ ```typescript
425
+ // renderer/api.ts
426
+ import { MatrxClient } from "./matrx-client";
427
+
428
+ export const api = new MatrxClient(
429
+ "http://127.0.0.1:8745",
430
+ window.__MATRX_SECRET__ // Passed from main process
431
+ );
432
+
433
+ // Call on app startup
434
+ await api.authenticate();
435
+ ```
436
+
437
+ 4. **React hooks** (if using React):
438
+
439
+ ```typescript
440
+ // hooks/useMatrx.ts
441
+ import { useEffect, useState } from "react";
442
+ import { api } from "../api";
443
+
444
+ export function useMatrxQuery<T>(
445
+ method: string,
446
+ params: Record<string, unknown> = {},
447
+ deps: unknown[] = []
448
+ ) {
449
+ const [data, setData] = useState<T | null>(null);
450
+ const [loading, setLoading] = useState(true);
451
+ const [error, setError] = useState<Error | null>(null);
452
+
453
+ useEffect(() => {
454
+ let cancelled = false;
455
+ setLoading(true);
456
+
457
+ api.call<T>(method, params)
458
+ .then((result) => {
459
+ if (!cancelled) {
460
+ setData(result);
461
+ setLoading(false);
462
+ }
463
+ })
464
+ .catch((err) => {
465
+ if (!cancelled) {
466
+ setError(err);
467
+ setLoading(false);
468
+ }
469
+ });
470
+
471
+ return () => { cancelled = true; };
472
+ }, deps);
473
+
474
+ return { data, loading, error };
475
+ }
476
+
477
+ // Usage in a component:
478
+ function UserList() {
479
+ const { data: users, loading } = useMatrxQuery<User[]>("users.load_items");
480
+ if (loading) return <div>Loading...</div>;
481
+ return <ul>{users?.map(u => <li key={u.id}>{u.username}</li>)}</ul>;
482
+ }
483
+ ```
484
+
485
+ ### Files to Create in matrx-local
486
+
487
+ | File | Purpose |
488
+ |------|---------|
489
+ | `python/server.py` | Copy of your API server script |
490
+ | `python/requirements.txt` | `matrx-orm[api]` |
491
+ | `src/main/python-backend.ts` | Process spawner |
492
+ | `src/renderer/matrx-client.ts` | Copy of `client_example.ts` |
493
+ | `src/renderer/hooks/useMatrx.ts` | React hooks for data fetching |
494
+ | `src/renderer/api.ts` | Client singleton |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrx-orm
3
- Version: 3.0.21
3
+ Version: 3.0.22
4
4
  Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching. Works with any PostgreSQL database.
5
5
  Project-URL: Homepage, https://github.com/armanisadeghi/matrx-orm
6
6
  Project-URL: Repository, https://github.com/armanisadeghi/matrx-orm
@@ -32,6 +32,8 @@ Requires-Dist: matrx-utils>=1.0.16
32
32
  Requires-Dist: psycopg-pool>=3.2.5
33
33
  Requires-Dist: psycopg[binary]>=3.2.5
34
34
  Requires-Dist: pyyaml>=6.0.3
35
+ Provides-Extra: api
36
+ Requires-Dist: aiohttp>=3.9.0; extra == 'api'
35
37
  Provides-Extra: dev
36
38
  Requires-Dist: asyncpg-stubs>=0.30.0; extra == 'dev'
37
39
  Requires-Dist: pyright>=1.1.408; extra == 'dev'
@@ -1836,6 +1838,7 @@ When you run the schema builder against a database that has `vector(n)` columns,
1836
1838
 
1837
1839
  | Version | Highlights |
1838
1840
  |---|---|
1841
+ | **v3.0.22** | Patch release |
1839
1842
  | **v3.0.21** | Patch release |
1840
1843
  | **v3.0.20** | Patch release |
1841
1844
  | **v3.0.19** | Patch release |
@@ -1795,6 +1795,7 @@ When you run the schema builder against a database that has `vector(n)` columns,
1795
1795
 
1796
1796
  | Version | Highlights |
1797
1797
  |---|---|
1798
+ | **v3.0.22** | Patch release |
1798
1799
  | **v3.0.21** | Patch release |
1799
1800
  | **v3.0.20** | Patch release |
1800
1801
  | **v3.0.19** | Patch release |
@@ -287,4 +287,5 @@ For batch-heavy workloads on any tier: the transaction pooler (port 6543\) is fr
287
287
  | Database migration | `Direct :5432 (IPv4 OK)` | `Supabase MCP / IPv6 runner` | Never run via Supavisor |
288
288
  | Pool exhausted | Circuit break, 503, alert | Circuit break, 503, alert | ORM raises PoolExhaustedError |
289
289
 
290
- Document generated from live Supabase MCP data — March 2026\. Update ProjectConnectionConfig entries if plans, compute tier, or IPv4 add-ons change.
290
+ Document generated from live Supabase MCP data — March 2026\. Update ProjectConnectionConfig entries if plans, compute tier, or IPv4 add-ons change.
291
+
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.0.21"
3
+ version = "3.0.22"
4
4
  description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching. Works with any PostgreSQL database."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -49,6 +49,9 @@ dependencies = [
49
49
  ]
50
50
 
51
51
  [project.optional-dependencies]
52
+ api = [
53
+ "aiohttp>=3.9.0",
54
+ ]
52
55
  dev = [
53
56
  "asyncpg-stubs>=0.30.0",
54
57
  "pyright>=1.1.408",
@@ -191,6 +191,12 @@ from .migrations import (
191
191
  migrate_and_rebuild,
192
192
  )
193
193
 
194
+ # ── Desktop API (optional — requires `pip install matrx-orm[api]`) ─────────
195
+ # Imports are deferred to avoid hard dependency on aiohttp.
196
+ def _lazy_api():
197
+ from matrx_orm.api import APIServer, APIConfig, TokenAuth
198
+ return APIServer, APIConfig, TokenAuth
199
+
194
200
  __all__ = [
195
201
  # ── Configuration ──────────────────────────────────────────────────────────
196
202
  "DatabaseProjectConfig",
@@ -385,4 +391,15 @@ __all__ = [
385
391
  "create_empty",
386
392
  "rollback",
387
393
  "migrate_and_rebuild",
394
+ # ── Desktop API (optional) ────────────────────────────────────────────────
395
+ "APIServer",
396
+ "APIConfig",
397
+ "TokenAuth",
388
398
  ]
399
+
400
+ # Lazy imports for optional API module
401
+ def __getattr__(name: str):
402
+ if name in ("APIServer", "APIConfig", "TokenAuth"):
403
+ from matrx_orm import api as _api
404
+ return getattr(_api, name)
405
+ raise AttributeError(f"module 'matrx_orm' has no attribute {name!r}")
@@ -0,0 +1,37 @@
1
+ """Secure local API server for desktop application integration.
2
+
3
+ Provides a localhost-only HTTP + WebSocket server that exposes ORM operations
4
+ to desktop frontends (Electron, Tauri, etc.) with token-based authentication.
5
+
6
+ Install the optional dependency group::
7
+
8
+ pip install matrx-orm[api]
9
+
10
+ Quick start::
11
+
12
+ from matrx_orm.api import APIServer, APIConfig
13
+
14
+ server = APIServer(config=APIConfig(secret="my-secret"))
15
+ server.register_manager("users", user_manager)
16
+ await server.start()
17
+ """
18
+
19
+ from matrx_orm.api.config import APIConfig
20
+ from matrx_orm.api.protocol import (
21
+ RPCRequest,
22
+ RPCResponse,
23
+ RPCError,
24
+ ErrorCode,
25
+ )
26
+ from matrx_orm.api.auth import TokenAuth
27
+ from matrx_orm.api.server import APIServer
28
+
29
+ __all__ = [
30
+ "APIConfig",
31
+ "APIServer",
32
+ "TokenAuth",
33
+ "RPCRequest",
34
+ "RPCResponse",
35
+ "RPCError",
36
+ "ErrorCode",
37
+ ]