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.
- matrx_orm-3.0.22/DESKTOP-API-INTEGRATION.md +494 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/PKG-INFO +4 -1
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/README.md +1 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/TRANSACTION_POOLER_PLAN.MD +2 -1
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/pyproject.toml +4 -1
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/__init__.py +17 -0
- matrx_orm-3.0.22/src/matrx_orm/api/__init__.py +37 -0
- matrx_orm-3.0.22/src/matrx_orm/api/auth.py +164 -0
- matrx_orm-3.0.22/src/matrx_orm/api/config.py +70 -0
- matrx_orm-3.0.22/src/matrx_orm/api/handlers.py +291 -0
- matrx_orm-3.0.22/src/matrx_orm/api/protocol.py +154 -0
- matrx_orm-3.0.22/src/matrx_orm/api/server.py +434 -0
- matrx_orm-3.0.22/tests/level1/test_api_auth.py +133 -0
- matrx_orm-3.0.22/tests/level1/test_api_config.py +57 -0
- matrx_orm-3.0.22/tests/level1/test_api_handlers.py +175 -0
- matrx_orm-3.0.22/tests/level1/test_api_protocol.py +183 -0
- matrx_orm-3.0.22/tests/sample_project_desktop/.env.example +10 -0
- matrx_orm-3.0.22/tests/sample_project_desktop/README.md +57 -0
- matrx_orm-3.0.22/tests/sample_project_desktop/__init__.py +0 -0
- matrx_orm-3.0.22/tests/sample_project_desktop/client_example.py +197 -0
- matrx_orm-3.0.22/tests/sample_project_desktop/client_example.ts +352 -0
- matrx_orm-3.0.22/tests/sample_project_desktop/server.py +130 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.arman/pending/versioning/INITIAL.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.env.example +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.github/workflows/publish.yml +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.gitignore +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.python-version +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/.vscode/settings.json +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/CLAUDE.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/MIGRATIONS.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/RESERVED_NAMES.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/TASKS-TEMPLATE.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/TASKS.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/WRITE-QUEUE-UPGRADE.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/docs/migrations.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/extended_jan5_backup.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/main.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/release.sh +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/scripts/publish.sh +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/scripts/release.sh +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/adapters/postgresql.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/py.typed +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/state.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/src/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/conftest.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.0.21 → matrx_orm-3.0.22}/tests/test_model_cls_refactor.py +0 -0
- {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.
|
|
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.
|
|
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
|
+
]
|