encino-orm 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. encino_orm-0.2.0/.github/workflows/ci.yml +79 -0
  2. encino_orm-0.2.0/.github/workflows/release.yml +46 -0
  3. encino_orm-0.2.0/.gitignore +25 -0
  4. encino_orm-0.2.0/CHANGELOG.md +47 -0
  5. encino_orm-0.2.0/LICENSE +21 -0
  6. encino_orm-0.2.0/PKG-INFO +168 -0
  7. encino_orm-0.2.0/README.md +146 -0
  8. encino_orm-0.2.0/docs/credits.md +36 -0
  9. encino_orm-0.2.0/docs/design/0-design.md +453 -0
  10. encino_orm-0.2.0/docs/design/1-model.md +851 -0
  11. encino_orm-0.2.0/docs/design/2-constraint.md +278 -0
  12. encino_orm-0.2.0/docs/design/3-graphql.md +380 -0
  13. encino_orm-0.2.0/docs/design/4-crud.md +463 -0
  14. encino_orm-0.2.0/docs/design/5-security.md +422 -0
  15. encino_orm-0.2.0/docs/design/6-from_db.md +361 -0
  16. encino_orm-0.2.0/docs/design/7-missing.md +380 -0
  17. encino_orm-0.2.0/docs/design/8-pk.md +554 -0
  18. encino_orm-0.2.0/docs/design/9-singleton.md +273 -0
  19. encino_orm-0.2.0/docs/engines.md +244 -0
  20. encino_orm-0.2.0/docs/getting-started.md +95 -0
  21. encino_orm-0.2.0/docs/guide.md +478 -0
  22. encino_orm-0.2.0/docs/index.md +37 -0
  23. encino_orm-0.2.0/docs/integrations.md +139 -0
  24. encino_orm-0.2.0/encino_orm/__init__.py +62 -0
  25. encino_orm-0.2.0/encino_orm/base.py +162 -0
  26. encino_orm-0.2.0/encino_orm/cli.py +139 -0
  27. encino_orm-0.2.0/encino_orm/context.py +61 -0
  28. encino_orm-0.2.0/encino_orm/engine.py +45 -0
  29. encino_orm-0.2.0/encino_orm/exceptions.py +22 -0
  30. encino_orm-0.2.0/encino_orm/graphql/__init__.py +9 -0
  31. encino_orm-0.2.0/encino_orm/graphql/filters.py +211 -0
  32. encino_orm-0.2.0/encino_orm/graphql/resolvers.py +65 -0
  33. encino_orm-0.2.0/encino_orm/graphql/scalars.py +19 -0
  34. encino_orm-0.2.0/encino_orm/graphql/schema.py +190 -0
  35. encino_orm-0.2.0/encino_orm/graphql/types.py +72 -0
  36. encino_orm-0.2.0/encino_orm/http/__init__.py +45 -0
  37. encino_orm-0.2.0/encino_orm/http/errors.py +22 -0
  38. encino_orm-0.2.0/encino_orm/http/parsing.py +65 -0
  39. encino_orm-0.2.0/encino_orm/http/registry.py +32 -0
  40. encino_orm-0.2.0/encino_orm/http/routes.py +108 -0
  41. encino_orm-0.2.0/encino_orm/introspection/__init__.py +13 -0
  42. encino_orm-0.2.0/encino_orm/introspection/codegen.py +106 -0
  43. encino_orm-0.2.0/encino_orm/introspection/tables.py +11 -0
  44. encino_orm-0.2.0/encino_orm/introspection/types.py +135 -0
  45. encino_orm-0.2.0/encino_orm/migration.py +56 -0
  46. encino_orm-0.2.0/encino_orm/model/__init__.py +111 -0
  47. encino_orm-0.2.0/encino_orm/model/cache_backend.py +59 -0
  48. encino_orm-0.2.0/encino_orm/model/cached.py +47 -0
  49. encino_orm-0.2.0/encino_orm/model/column.py +7 -0
  50. encino_orm-0.2.0/encino_orm/model/constraint.py +119 -0
  51. encino_orm-0.2.0/encino_orm/model/domain.py +65 -0
  52. encino_orm-0.2.0/encino_orm/model/exceptions.py +33 -0
  53. encino_orm-0.2.0/encino_orm/model/filter.py +200 -0
  54. encino_orm-0.2.0/encino_orm/model/hooks.py +34 -0
  55. encino_orm-0.2.0/encino_orm/model/index.py +27 -0
  56. encino_orm-0.2.0/encino_orm/model/model.py +1009 -0
  57. encino_orm-0.2.0/encino_orm/model/query_builder.py +286 -0
  58. encino_orm-0.2.0/encino_orm/model/records.py +22 -0
  59. encino_orm-0.2.0/encino_orm/model/references.py +32 -0
  60. encino_orm-0.2.0/encino_orm/model/scope.py +26 -0
  61. encino_orm-0.2.0/encino_orm/model/types.py +195 -0
  62. encino_orm-0.2.0/encino_orm/mysql.py +270 -0
  63. encino_orm-0.2.0/encino_orm/observability.py +149 -0
  64. encino_orm-0.2.0/encino_orm/pool.py +301 -0
  65. encino_orm-0.2.0/encino_orm/postgresql.py +258 -0
  66. encino_orm-0.2.0/encino_orm/query.py +34 -0
  67. encino_orm-0.2.0/encino_orm/security/__init__.py +33 -0
  68. encino_orm-0.2.0/encino_orm/security/exceptions.py +20 -0
  69. encino_orm-0.2.0/encino_orm/security/guard.py +71 -0
  70. encino_orm-0.2.0/encino_orm/security/jwt.py +56 -0
  71. encino_orm-0.2.0/encino_orm/security/models.py +62 -0
  72. encino_orm-0.2.0/encino_orm/security/permissions.py +56 -0
  73. encino_orm-0.2.0/encino_orm/sql.py +142 -0
  74. encino_orm-0.2.0/encino_orm/sqlite.py +245 -0
  75. encino_orm-0.2.0/encino_orm/transfer.py +177 -0
  76. encino_orm-0.2.0/pyproject.toml +46 -0
  77. encino_orm-0.2.0/tests/__init__.py +0 -0
  78. encino_orm-0.2.0/tests/conftest.py +15 -0
  79. encino_orm-0.2.0/tests/test_aggregates.py +45 -0
  80. encino_orm-0.2.0/tests/test_base.py +85 -0
  81. encino_orm-0.2.0/tests/test_bulk_upsert.py +126 -0
  82. encino_orm-0.2.0/tests/test_cached_model.py +59 -0
  83. encino_orm-0.2.0/tests/test_cli.py +61 -0
  84. encino_orm-0.2.0/tests/test_constraint.py +95 -0
  85. encino_orm-0.2.0/tests/test_crud.py +238 -0
  86. encino_orm-0.2.0/tests/test_d_recommendations.py +198 -0
  87. encino_orm-0.2.0/tests/test_decimal_json.py +107 -0
  88. encino_orm-0.2.0/tests/test_e_recommendations.py +72 -0
  89. encino_orm-0.2.0/tests/test_engine.py +54 -0
  90. encino_orm-0.2.0/tests/test_f_recommendations.py +58 -0
  91. encino_orm-0.2.0/tests/test_from_db.py +223 -0
  92. encino_orm-0.2.0/tests/test_g_recommendations.py +77 -0
  93. encino_orm-0.2.0/tests/test_graphql.py +293 -0
  94. encino_orm-0.2.0/tests/test_has_many.py +121 -0
  95. encino_orm-0.2.0/tests/test_hooks.py +122 -0
  96. encino_orm-0.2.0/tests/test_improvements.py +178 -0
  97. encino_orm-0.2.0/tests/test_index.py +141 -0
  98. encino_orm-0.2.0/tests/test_issues.py +161 -0
  99. encino_orm-0.2.0/tests/test_migrations.py +153 -0
  100. encino_orm-0.2.0/tests/test_model.py +204 -0
  101. encino_orm-0.2.0/tests/test_mysql.py +253 -0
  102. encino_orm-0.2.0/tests/test_observability.py +78 -0
  103. encino_orm-0.2.0/tests/test_pk.py +224 -0
  104. encino_orm-0.2.0/tests/test_pool.py +323 -0
  105. encino_orm-0.2.0/tests/test_postgresql.py +256 -0
  106. encino_orm-0.2.0/tests/test_query_builder.py +126 -0
  107. encino_orm-0.2.0/tests/test_records.py +60 -0
  108. encino_orm-0.2.0/tests/test_references.py +110 -0
  109. encino_orm-0.2.0/tests/test_scalar_types.py +86 -0
  110. encino_orm-0.2.0/tests/test_scope_softdelete.py +156 -0
  111. encino_orm-0.2.0/tests/test_security.py +224 -0
  112. encino_orm-0.2.0/tests/test_singleton.py +115 -0
  113. encino_orm-0.2.0/tests/test_sql_functions.py +138 -0
  114. encino_orm-0.2.0/tests/test_sqlite.py +296 -0
  115. encino_orm-0.2.0/tests/test_transfer.py +238 -0
  116. encino_orm-0.2.0/tests/test_types.py +64 -0
  117. encino_orm-0.2.0/uv.lock +641 -0
@@ -0,0 +1,79 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ test:
17
+ name: Python ${{ matrix.python-version }}
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 15
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ python-version: ["3.10", "3.11", "3.12"]
24
+
25
+ services:
26
+ mysql:
27
+ image: mysql:8.0
28
+ env:
29
+ MYSQL_ROOT_PASSWORD: admin
30
+ MYSQL_ROOT_HOST: "%"
31
+ MYSQL_DATABASE: encino_orm_test
32
+ ports:
33
+ - 3306:3306
34
+ options: >-
35
+ --health-cmd="mysqladmin ping -h 127.0.0.1 --password=admin --silent"
36
+ --health-interval=10s
37
+ --health-timeout=5s
38
+ --health-retries=12
39
+
40
+ postgres:
41
+ image: postgres:16-alpine
42
+ env:
43
+ POSTGRES_USER: postgres
44
+ POSTGRES_PASSWORD: admin
45
+ POSTGRES_DB: encino_orm_test
46
+ ports:
47
+ - 5432:5432
48
+ options: >-
49
+ --health-cmd="pg_isready -U postgres"
50
+ --health-interval=10s
51
+ --health-timeout=5s
52
+ --health-retries=12
53
+
54
+ steps:
55
+ - name: Checkout
56
+ uses: actions/checkout@v4
57
+
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@v5
60
+ with:
61
+ python-version: ${{ matrix.python-version }}
62
+ enable-cache: true
63
+
64
+ - name: Install dependencies
65
+ run: uv sync --extra http --extra security --extra graphql
66
+
67
+ - name: Run tests
68
+ env:
69
+ ENCINO_ORM_MYSQL_HOST: 127.0.0.1
70
+ ENCINO_ORM_MYSQL_PORT: "3306"
71
+ ENCINO_ORM_MYSQL_USER: root
72
+ ENCINO_ORM_MYSQL_PASSWORD: admin
73
+ ENCINO_ORM_MYSQL_DB: encino_orm_test
74
+ ENCINO_ORM_POSTGRES_HOST: 127.0.0.1
75
+ ENCINO_ORM_POSTGRES_PORT: "5432"
76
+ ENCINO_ORM_POSTGRES_USER: postgres
77
+ ENCINO_ORM_POSTGRES_PASSWORD: admin
78
+ ENCINO_ORM_POSTGRES_DB: encino_orm_test
79
+ run: uv run pytest -q
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write # para trusted publishing (OIDC) si se usa
12
+
13
+ jobs:
14
+ publish:
15
+ name: Build and publish
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v5
23
+ with:
24
+ enable-cache: true
25
+
26
+ - name: Build
27
+ run: uv build
28
+
29
+ # Publica a PyPI usando un token (secret PYPI_API_TOKEN).
30
+ # Alternativa sin token: Trusted Publishing (OIDC):
31
+ # 1. En PyPI > Settings > Publishing, añade el repo hvalles/encinorm
32
+ # con el workflow "release.yml".
33
+ # 2. Sustituye este paso por: run: uv publish --trusted-publishing always
34
+ # y elimina el env con el token.
35
+ - name: Publish to PyPI
36
+ env:
37
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
38
+ run: uv publish
39
+
40
+ # Para probar contra TestPyPI de forma manual:
41
+ # gh workflow run "Publish to PyPI" -f ... (o usa el botón "Run workflow")
42
+ # y comenta el paso anterior, descomentando:
43
+ # - name: Publish to TestPyPI
44
+ # env:
45
+ # UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
46
+ # run: uv publish --publish-url https://test.pypi.org/legacy/
@@ -0,0 +1,25 @@
1
+ # Entornos virtuales
2
+ .venv/
3
+ venv/
4
+ env/
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *.egg-info/
10
+ .eggs/
11
+ build/
12
+ dist/
13
+
14
+ # Pruebas y cobertura
15
+ .pytest_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # Entornos / editores
20
+ .env
21
+ .idea/
22
+ .vscode/
23
+
24
+ # Notas internas (no distribuir)
25
+ prompts/
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ Todos los cambios notables del proyecto se documentan en este archivo.
4
+
5
+ El formato sigue [Keep a Changelog](https://keepachangelog.com/es/1.1.0/) y el
6
+ proyecto usa [Versionado Semántico](https://semver.org/lang/es/). Mientras esté
7
+ en `0.x`, **no hay garantía de estabilidad** (ver `README.md`).
8
+
9
+ ## [Unreleased]
10
+
11
+ ### Añadido
12
+
13
+ - `Model.cursor(db=None, **values)`: instancia de consulta sin validación de
14
+ pydantic, para operaciones de lectura (`load`/`search`/`count`/`paginate`) y
15
+ de esquema (`create_table`) sobre modelos con campos `required=True`, sin
16
+ recurrir a `model_construct()` manual.
17
+
18
+ ### Corregido
19
+
20
+ - Ejemplos de `README.md` y `docs/getting-started.md` que requerían definir un
21
+ helper `_cursor()` manual: ahora usan el classmethod `cursor()`.
22
+
23
+ ## [0.1.0] - 2026-08-30
24
+
25
+ ### Añadido
26
+
27
+ - Núcleo ORM asíncrono para SQLite, MySQL y PostgreSQL (`Db`, `PoolDb`,
28
+ `session`, `create_db`).
29
+ - `Model` declarativo (basado en `pydantic`) con restricciones reutilizables
30
+ (`make_constraint` y presets `STR_*`, `INT`, `CURRENCY`, `DATETIME`,
31
+ `DECIMAL`, `JSON`, …).
32
+ - CRUD completo: `insert`, `save`, `upsert`, `load`, `update`, `delete`,
33
+ `search`, `count`, `paginate`, `insert_many` (bulk).
34
+ - Claves primarias simples (auto-incremental), naturales y **compuestas**, y
35
+ claves foráneas compuestas.
36
+ - Relaciones 1:1 y 1:N con carga por lotes (`batch_reference`,
37
+ `batch_has_many`).
38
+ - `Filter` componible y `QueryBuilder` con `join`, agregados y subconsultas.
39
+ - Migraciones versionadas (`Migration`, `migrations_from_dir`), `create_table`,
40
+ `diff_schema` y `sync_schema`.
41
+ - Conexión implícita (`set_default_db`, `bind`, `resolve_db`).
42
+ - Capas opcionales: REST (FastAPI), GraphQL (Strawberry), seguridad (RBAC + JWT)
43
+ y codegen/CLI (`encino_orm generate models`).
44
+ - Observabilidad (`trace_id`, `QueryTracer`) y caché (`CachedModel` +
45
+ `CacheBackend`).
46
+ - Documentación de usuario (`README.md` y `docs/`), guía para agregar motores y
47
+ licencia MIT.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 encino_orm contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.5
2
+ Name: encino-orm
3
+ Version: 0.2.0
4
+ Summary: Librería asíncrona de interfaz unificada para SQLite, MySQL y PostgreSQL
5
+ Project-URL: Repository, https://github.com/hvalles/encinorm
6
+ Author-email: Hector <valles.hector@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: aiomysql<0.3.2,>=0.2
11
+ Requires-Dist: aiosqlite
12
+ Requires-Dist: asyncpg>=0.31.0
13
+ Requires-Dist: pydantic>=2.13.4
14
+ Provides-Extra: graphql
15
+ Requires-Dist: strawberry-graphql>=0.200; extra == 'graphql'
16
+ Provides-Extra: http
17
+ Requires-Dist: fastapi>=0.110; extra == 'http'
18
+ Provides-Extra: security
19
+ Requires-Dist: fastapi>=0.110; extra == 'security'
20
+ Requires-Dist: pyjwt<2.13,>=2.8; extra == 'security'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # encino_orm · v0.2.0
24
+
25
+ ORM asíncrono de interfaz unificada para **SQLite**, **MySQL** y **PostgreSQL**,
26
+ construido sobre `pydantic`. Proporciona un modelo de datos declarativo, CRUD
27
+ tipado, validación, relaciones (1:1 y 1:N), consultas con filtros y agregados,
28
+ migraciones, y capas opcionales de producto: REST (FastAPI), GraphQL
29
+ (Strawberry), seguridad (RBAC + JWT) y generación de código desde la base de
30
+ datos.
31
+
32
+ > **Estado: experimental (v0.2.0).** encino_orm se encuentra en **fase
33
+ > experimental**: la API pública y su comportamiento pueden cambiar **sin previo
34
+ > aviso** en versiones posteriores, **sin garantía de compatibilidad
35
+ > retroactiva**. El núcleo ORM está probado sobre los tres motores (más de 350
36
+ > pruebas, con integraciones reales de MySQL y PostgreSQL), pero no se recomienda
37
+ > depender de una API estable en producción. Documentación de usuario en `docs/`;
38
+ > estado de preparación en `prompts/analisys-07.md`.
39
+
40
+ ---
41
+
42
+ ## Características
43
+
44
+ - **Tres motores** con un único API: `SqliteDb`, `MysqlDb`, `PostgresDb`, y un
45
+ pool de conexiones `PoolDb` con transacciones atómicas por `contextvar`.
46
+ - **Modelos declarativos** basados en `pydantic`, con restricciones reutilizables
47
+ (`STR_100`, `INT_POS`, `CURRENCY`, `DATETIME`, `DECIMAL`, `JSON`, …).
48
+ - **CRUD completo**: `insert`, `save`, `upsert`, `load`, `update`, `delete`,
49
+ `search`, `count`, `paginate`, `insert_many` (bulk).
50
+ - **Claves primarias flexibles**: `id` auto-incremental (por defecto), clave
51
+ natural simple o **clave compuesta**, y claves foráneas compuestas.
52
+ - **Relaciones**: referencias 1:1, colecciones 1:N (`has_many`), y carga por
53
+ lotes (`batch_reference`/`batch_has_many`) para evitar N+1.
54
+ - **Consultas**: `Filter` componible y `QueryBuilder` con `join`, `group_by`,
55
+ agregados (`sum`/`avg`/`min`/`max`/`count`) y subconsultas.
56
+ - **Esquema**: `create_table`, migraciones versionadas, `diff_schema` y
57
+ `sync_schema`.
58
+ - **Hooks** de ciclo de vida y **caché** (`CachedModel` + `CacheBackend`).
59
+ - **Conexión implícita**: `set_default_db`, `bind` y `session` eliminan la
60
+ necesidad de pasar `db` a cada instancia.
61
+ - **Observabilidad**: `trace_id` por request y `QueryTracer` con métricas.
62
+ - **Capas opcionales**: REST (`create_crud`), GraphQL (`build_schema`),
63
+ seguridad (`emit_token`, `require`, RBAC tri-estado) y CLI/codegen
64
+ (`encino_orm generate models`).
65
+
66
+ ---
67
+
68
+ ## Instalación
69
+
70
+ ```bash
71
+ # núcleo (SQLite + MySQL + PostgreSQL)
72
+ pip install -e .
73
+
74
+ # con extras opcionales
75
+ pip install -e ".[http,security,graphql]"
76
+ ```
77
+
78
+ Extras disponibles:
79
+
80
+ | Extra | Incluye |
81
+ |------------|------------------------------------------------------|
82
+ | `http` | `fastapi` (REST CRUD) |
83
+ | `security` | `fastapi` + `PyJWT` (RBAC + JWT) |
84
+ | `graphql` | `strawberry-graphql` (GraphQL) |
85
+
86
+ Requiere **Python 3.10+**.
87
+
88
+ ---
89
+
90
+ ## Inicio rápido
91
+
92
+ ```python
93
+ import asyncio
94
+ from encino_orm import create_db
95
+ from encino_orm.model import Model, Filter, STR_100, INT_POS
96
+
97
+ class User(Model):
98
+ _table = "users"
99
+ name: STR_100(required=True)
100
+ age: INT_POS()
101
+
102
+ async def main():
103
+ db = await create_db("sqlite", database=":memory:")
104
+
105
+ await User.cursor(db).create_table() # genera el DDL y lo aplica
106
+
107
+ u = User(db, name="Ana", age=30)
108
+ await u.insert() # INSERT (id auto-incremental)
109
+
110
+ found = await User.cursor(db, id=u.id).load() # SELECT por clave primaria
111
+ assert found.name == "Ana"
112
+
113
+ adults = await User.cursor(db).search(Filter.ge("age", 18)) # SELECT ... WHERE age >= 18
114
+ assert len(adults) == 1
115
+
116
+ await db.close()
117
+ ```
118
+
119
+ > Ejemplo completo (incluidos `Filter` y las capas REST/GraphQL) en
120
+ > `docs/getting-started.md`.
121
+
122
+ ---
123
+
124
+ ## Documentación
125
+
126
+ | Documento | Contenido |
127
+ |-----------|-----------|
128
+ | [Getting started](docs/getting-started.md) | Instalación y primer modelo en 5 minutos. |
129
+ | [Guía de uso](docs/guide.md) | Modelos, restricciones, CRUD, filtros, relaciones, claves primarias y foráneas. |
130
+ | [Integraciones](docs/integrations.md) | REST, GraphQL, seguridad, codegen/CLI y observabilidad. |
131
+ | [Agregar un motor](docs/engines.md) | Guía para desarrolladores: cómo añadir un nuevo motor de base de datos. |
132
+ | [Créditos](docs/credits.md) | Herramientas y tecnologías utilizadas. |
133
+ | [Diseño](docs/) | Documentos de diseño (`design_*.md`) de la arquitectura interna. |
134
+
135
+ ---
136
+
137
+ ## Pruebas
138
+
139
+ ```bash
140
+ uv run pytest # suite completa (SQLite + MySQL + PostgreSQL)
141
+ uv run pytest -m "not integration" # (si los servidores no están disponibles)
142
+ ```
143
+
144
+ Las integraciones de MySQL y PostgreSQL se omiten automáticamente si el servidor
145
+ correspondiente no está disponible.
146
+
147
+ ---
148
+
149
+ ## Licencia
150
+
151
+ Distribuido bajo la licencia [MIT](LICENSE). Consulta el archivo `LICENSE`
152
+ para el texto íntegro.
153
+
154
+ ---
155
+
156
+ ## Versionado
157
+
158
+ El proyecto sigue [Versionado Semántico](https://semver.org). Mientras esté en
159
+ `0.x`, **no hay garantía de estabilidad**: cada versión *minor* puede introducir
160
+ cambios incompatibles. La estabilidad de la API se declarará a partir de `1.0.0`.
161
+
162
+ ---
163
+
164
+ ## Créditos
165
+
166
+ Desarrollado con la asistencia de **[OpenCode](https://opencode.ai)** y el modelo
167
+ **[DeepSeek V4 Pro](https://www.deepseek.com)**. Véase
168
+ [docs/credits.md](docs/credits.md) para el detalle completo.
@@ -0,0 +1,146 @@
1
+ # encino_orm · v0.2.0
2
+
3
+ ORM asíncrono de interfaz unificada para **SQLite**, **MySQL** y **PostgreSQL**,
4
+ construido sobre `pydantic`. Proporciona un modelo de datos declarativo, CRUD
5
+ tipado, validación, relaciones (1:1 y 1:N), consultas con filtros y agregados,
6
+ migraciones, y capas opcionales de producto: REST (FastAPI), GraphQL
7
+ (Strawberry), seguridad (RBAC + JWT) y generación de código desde la base de
8
+ datos.
9
+
10
+ > **Estado: experimental (v0.2.0).** encino_orm se encuentra en **fase
11
+ > experimental**: la API pública y su comportamiento pueden cambiar **sin previo
12
+ > aviso** en versiones posteriores, **sin garantía de compatibilidad
13
+ > retroactiva**. El núcleo ORM está probado sobre los tres motores (más de 350
14
+ > pruebas, con integraciones reales de MySQL y PostgreSQL), pero no se recomienda
15
+ > depender de una API estable en producción. Documentación de usuario en `docs/`;
16
+ > estado de preparación en `prompts/analisys-07.md`.
17
+
18
+ ---
19
+
20
+ ## Características
21
+
22
+ - **Tres motores** con un único API: `SqliteDb`, `MysqlDb`, `PostgresDb`, y un
23
+ pool de conexiones `PoolDb` con transacciones atómicas por `contextvar`.
24
+ - **Modelos declarativos** basados en `pydantic`, con restricciones reutilizables
25
+ (`STR_100`, `INT_POS`, `CURRENCY`, `DATETIME`, `DECIMAL`, `JSON`, …).
26
+ - **CRUD completo**: `insert`, `save`, `upsert`, `load`, `update`, `delete`,
27
+ `search`, `count`, `paginate`, `insert_many` (bulk).
28
+ - **Claves primarias flexibles**: `id` auto-incremental (por defecto), clave
29
+ natural simple o **clave compuesta**, y claves foráneas compuestas.
30
+ - **Relaciones**: referencias 1:1, colecciones 1:N (`has_many`), y carga por
31
+ lotes (`batch_reference`/`batch_has_many`) para evitar N+1.
32
+ - **Consultas**: `Filter` componible y `QueryBuilder` con `join`, `group_by`,
33
+ agregados (`sum`/`avg`/`min`/`max`/`count`) y subconsultas.
34
+ - **Esquema**: `create_table`, migraciones versionadas, `diff_schema` y
35
+ `sync_schema`.
36
+ - **Hooks** de ciclo de vida y **caché** (`CachedModel` + `CacheBackend`).
37
+ - **Conexión implícita**: `set_default_db`, `bind` y `session` eliminan la
38
+ necesidad de pasar `db` a cada instancia.
39
+ - **Observabilidad**: `trace_id` por request y `QueryTracer` con métricas.
40
+ - **Capas opcionales**: REST (`create_crud`), GraphQL (`build_schema`),
41
+ seguridad (`emit_token`, `require`, RBAC tri-estado) y CLI/codegen
42
+ (`encino_orm generate models`).
43
+
44
+ ---
45
+
46
+ ## Instalación
47
+
48
+ ```bash
49
+ # núcleo (SQLite + MySQL + PostgreSQL)
50
+ pip install -e .
51
+
52
+ # con extras opcionales
53
+ pip install -e ".[http,security,graphql]"
54
+ ```
55
+
56
+ Extras disponibles:
57
+
58
+ | Extra | Incluye |
59
+ |------------|------------------------------------------------------|
60
+ | `http` | `fastapi` (REST CRUD) |
61
+ | `security` | `fastapi` + `PyJWT` (RBAC + JWT) |
62
+ | `graphql` | `strawberry-graphql` (GraphQL) |
63
+
64
+ Requiere **Python 3.10+**.
65
+
66
+ ---
67
+
68
+ ## Inicio rápido
69
+
70
+ ```python
71
+ import asyncio
72
+ from encino_orm import create_db
73
+ from encino_orm.model import Model, Filter, STR_100, INT_POS
74
+
75
+ class User(Model):
76
+ _table = "users"
77
+ name: STR_100(required=True)
78
+ age: INT_POS()
79
+
80
+ async def main():
81
+ db = await create_db("sqlite", database=":memory:")
82
+
83
+ await User.cursor(db).create_table() # genera el DDL y lo aplica
84
+
85
+ u = User(db, name="Ana", age=30)
86
+ await u.insert() # INSERT (id auto-incremental)
87
+
88
+ found = await User.cursor(db, id=u.id).load() # SELECT por clave primaria
89
+ assert found.name == "Ana"
90
+
91
+ adults = await User.cursor(db).search(Filter.ge("age", 18)) # SELECT ... WHERE age >= 18
92
+ assert len(adults) == 1
93
+
94
+ await db.close()
95
+ ```
96
+
97
+ > Ejemplo completo (incluidos `Filter` y las capas REST/GraphQL) en
98
+ > `docs/getting-started.md`.
99
+
100
+ ---
101
+
102
+ ## Documentación
103
+
104
+ | Documento | Contenido |
105
+ |-----------|-----------|
106
+ | [Getting started](docs/getting-started.md) | Instalación y primer modelo en 5 minutos. |
107
+ | [Guía de uso](docs/guide.md) | Modelos, restricciones, CRUD, filtros, relaciones, claves primarias y foráneas. |
108
+ | [Integraciones](docs/integrations.md) | REST, GraphQL, seguridad, codegen/CLI y observabilidad. |
109
+ | [Agregar un motor](docs/engines.md) | Guía para desarrolladores: cómo añadir un nuevo motor de base de datos. |
110
+ | [Créditos](docs/credits.md) | Herramientas y tecnologías utilizadas. |
111
+ | [Diseño](docs/) | Documentos de diseño (`design_*.md`) de la arquitectura interna. |
112
+
113
+ ---
114
+
115
+ ## Pruebas
116
+
117
+ ```bash
118
+ uv run pytest # suite completa (SQLite + MySQL + PostgreSQL)
119
+ uv run pytest -m "not integration" # (si los servidores no están disponibles)
120
+ ```
121
+
122
+ Las integraciones de MySQL y PostgreSQL se omiten automáticamente si el servidor
123
+ correspondiente no está disponible.
124
+
125
+ ---
126
+
127
+ ## Licencia
128
+
129
+ Distribuido bajo la licencia [MIT](LICENSE). Consulta el archivo `LICENSE`
130
+ para el texto íntegro.
131
+
132
+ ---
133
+
134
+ ## Versionado
135
+
136
+ El proyecto sigue [Versionado Semántico](https://semver.org). Mientras esté en
137
+ `0.x`, **no hay garantía de estabilidad**: cada versión *minor* puede introducir
138
+ cambios incompatibles. La estabilidad de la API se declarará a partir de `1.0.0`.
139
+
140
+ ---
141
+
142
+ ## Créditos
143
+
144
+ Desarrollado con la asistencia de **[OpenCode](https://opencode.ai)** y el modelo
145
+ **[DeepSeek V4 Pro](https://www.deepseek.com)**. Véase
146
+ [docs/credits.md](docs/credits.md) para el detalle completo.
@@ -0,0 +1,36 @@
1
+ # Créditos
2
+
3
+ Este proyecto fue desarrollado con la asistencia de las siguientes herramientas
4
+ de inteligencia artificial:
5
+
6
+ ## OpenCode
7
+
8
+ - **Sitio:** <https://opencode.ai>
9
+ - **Rol:** agente de codificación en terminal utilizado para explorar el
10
+ repositorio, redactar e implementar los cambios del núcleo ORM, las capas de
11
+ producto (REST, GraphQL, seguridad, codegen), la documentación de usuario y los
12
+ documentos de diseño.
13
+
14
+ ## DeepSeek V4 Pro
15
+
16
+ - **Modelo:** `deepseek/deepseek-v4-pro`
17
+ - **Rol:** modelo de lenguaje que impulsa la generación de código, el análisis de
18
+ los documentos `prompts/analisys-*.md` y la redacción de `docs/*.md`.
19
+
20
+ ## Dependencias y ecosistema
21
+
22
+ El proyecto se apoya en las siguientes bibliotecas de código abierto:
23
+
24
+ | Biblioteca | Uso |
25
+ |----------------------|--------------------------------------------|
26
+ | `pydantic` | Validación y modelos de datos. |
27
+ | `aiosqlite` | Driver asíncrono de SQLite. |
28
+ | `aiomysql` | Driver asíncrono de MySQL. |
29
+ | `asyncpg` | Driver asíncrono de PostgreSQL. |
30
+ | `fastapi` | Capa REST (extra `http`/`security`). |
31
+ | `PyJWT` | Tokens JWT (extra `security`). |
32
+ | `strawberry-graphql` | Capa GraphQL (extra `graphql`). |
33
+ | `pytest` | Suite de pruebas (dev). |
34
+ | `httpx` | Cliente de pruebas ASGI (dev). |
35
+
36
+ Gracias a los mantenedores de estas herramientas por su trabajo.