teridex 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. teridex-0.1.0.data/data/share/teridex/teridex.tcss +210 -0
  2. teridex-0.1.0.dist-info/METADATA +262 -0
  3. teridex-0.1.0.dist-info/RECORD +76 -0
  4. teridex-0.1.0.dist-info/WHEEL +4 -0
  5. teridex-0.1.0.dist-info/entry_points.txt +2 -0
  6. teridex-0.1.0.dist-info/licenses/LICENSE +21 -0
  7. teridex_adapters/__init__.py +17 -0
  8. teridex_adapters/_introspect.py +97 -0
  9. teridex_adapters/_typeinfer.py +72 -0
  10. teridex_adapters/base.py +160 -0
  11. teridex_adapters/duckdb_adapter.py +298 -0
  12. teridex_adapters/mysql_adapter.py +382 -0
  13. teridex_adapters/postgres_adapter.py +420 -0
  14. teridex_adapters/py.typed +0 -0
  15. teridex_adapters/registry.py +102 -0
  16. teridex_adapters/sqlite_adapter.py +287 -0
  17. teridex_cli/__init__.py +5 -0
  18. teridex_cli/__main__.py +4 -0
  19. teridex_cli/main.py +222 -0
  20. teridex_cli/py.typed +0 -0
  21. teridex_core/__init__.py +25 -0
  22. teridex_core/config.py +133 -0
  23. teridex_core/di.py +102 -0
  24. teridex_core/errors.py +73 -0
  25. teridex_core/events.py +275 -0
  26. teridex_core/logging.py +122 -0
  27. teridex_core/models/__init__.py +33 -0
  28. teridex_core/models/connection.py +111 -0
  29. teridex_core/models/query.py +66 -0
  30. teridex_core/models/result.py +53 -0
  31. teridex_core/models/schema.py +83 -0
  32. teridex_core/protocols/__init__.py +11 -0
  33. teridex_core/protocols/adapter.py +58 -0
  34. teridex_core/protocols/plugin.py +28 -0
  35. teridex_core/py.typed +0 -0
  36. teridex_core/result.py +69 -0
  37. teridex_engine/__init__.py +14 -0
  38. teridex_engine/executor.py +161 -0
  39. teridex_engine/history.py +151 -0
  40. teridex_engine/introspector.py +86 -0
  41. teridex_engine/pool.py +168 -0
  42. teridex_engine/py.typed +0 -0
  43. teridex_engine/transaction.py +23 -0
  44. teridex_plugins/__init__.py +16 -0
  45. teridex_plugins/api.py +100 -0
  46. teridex_plugins/context.py +72 -0
  47. teridex_plugins/loader.py +199 -0
  48. teridex_plugins/py.typed +0 -0
  49. teridex_plugins/registry.py +80 -0
  50. teridex_tui/__init__.py +5 -0
  51. teridex_tui/app.py +543 -0
  52. teridex_tui/builtin_commands.py +104 -0
  53. teridex_tui/events.py +11 -0
  54. teridex_tui/keymaps/__init__.py +6 -0
  55. teridex_tui/keymaps/default.py +18 -0
  56. teridex_tui/keymaps/vim.py +12 -0
  57. teridex_tui/py.typed +0 -0
  58. teridex_tui/screens/__init__.py +7 -0
  59. teridex_tui/screens/command_palette.py +97 -0
  60. teridex_tui/screens/connection.py +90 -0
  61. teridex_tui/screens/help.py +57 -0
  62. teridex_tui/screens/history.py +79 -0
  63. teridex_tui/screens/main.py +44 -0
  64. teridex_tui/state.py +37 -0
  65. teridex_tui/teridex.tcss +210 -0
  66. teridex_tui/themes/__init__.py +8 -0
  67. teridex_tui/themes/_base.py +30 -0
  68. teridex_tui/themes/monokai.py +13 -0
  69. teridex_tui/themes/nord.py +13 -0
  70. teridex_tui/widgets/__init__.py +10 -0
  71. teridex_tui/widgets/action_bar.py +45 -0
  72. teridex_tui/widgets/query_tabs.py +44 -0
  73. teridex_tui/widgets/results_table.py +113 -0
  74. teridex_tui/widgets/schema_tree.py +125 -0
  75. teridex_tui/widgets/sql_editor.py +17 -0
  76. teridex_tui/widgets/status_bar.py +71 -0
@@ -0,0 +1,210 @@
1
+ /* Teridex Textual CSS — main layout
2
+ *
3
+ * Layout:
4
+ * ┌── Data Catalog ────┐┌──── Query Editor ──────────────────┐
5
+ * │ schema tree ││ Tab 1 Tab 2 │
6
+ * │ ││ SQL editor with line numbers │
7
+ * │ │├───────────────────────────────────┤
8
+ * │ ││ Tx: Auto Limit 500 [Run Query] │
9
+ * │ │├──── Query Results ────────────────┤
10
+ * │ ││ data table │
11
+ * └─────────────────────┘└───────────────────────────────────┘
12
+ * ^q Quit f1 Help ^↵ Run Query ^r Refresh ... (footer)
13
+ */
14
+
15
+ Screen {
16
+ background: $background;
17
+ color: $foreground;
18
+ }
19
+
20
+ /* ── Top-level wrapper ── */
21
+ MainScreen {
22
+ height: 100%;
23
+ width: 100%;
24
+ layout: vertical;
25
+ }
26
+
27
+ /* ── 2-column grid: sidebar | workspace ── */
28
+ #main-grid {
29
+ layout: grid;
30
+ grid-size: 2 1;
31
+ grid-columns: 28 1fr;
32
+ grid-rows: 1fr;
33
+ height: 1fr;
34
+ width: 100%;
35
+ }
36
+
37
+ /* Dynamic plugin rails (preserve existing behaviour) */
38
+ #main-grid.with-right {
39
+ grid-size: 3 1;
40
+ grid-columns: 28 1fr auto;
41
+ }
42
+
43
+ /* ── Sidebar: "Data Catalog" ── */
44
+ #sidebar {
45
+ border: round $primary;
46
+ border-title-align: left;
47
+ border-title-color: $foreground;
48
+ border-title-style: bold;
49
+ padding: 0 1;
50
+ overflow-y: auto;
51
+ }
52
+
53
+ /* ── Right workspace ── */
54
+ #workspace {
55
+ layout: vertical;
56
+ height: 100%;
57
+ }
58
+
59
+ /* ── Query Editor panel ── */
60
+ #editor-panel {
61
+ border: round $primary;
62
+ border-title-align: left;
63
+ border-title-color: $foreground;
64
+ border-title-style: bold;
65
+ height: 2fr;
66
+ min-height: 8;
67
+ }
68
+
69
+ /* ── Action bar between editor and results ── */
70
+ #action-bar {
71
+ height: 1;
72
+ background: $surface;
73
+ layout: horizontal;
74
+ padding: 0 1;
75
+ }
76
+
77
+ #action-bar .action-label {
78
+ width: auto;
79
+ color: $foreground;
80
+ padding: 0 1;
81
+ }
82
+
83
+ #action-bar #run-query-btn {
84
+ dock: right;
85
+ min-width: 14;
86
+ background: $primary;
87
+ color: $background;
88
+ text-style: bold;
89
+ padding: 0 1;
90
+ }
91
+
92
+ /* ── Query Results panel ── */
93
+ #results-panel {
94
+ border: round $primary;
95
+ border-title-align: left;
96
+ border-title-color: $foreground;
97
+ border-title-style: bold;
98
+ height: 1fr;
99
+ min-height: 4;
100
+ }
101
+
102
+ /* ── Plugin rails ── */
103
+ #right-rail {
104
+ width: 30;
105
+ border: round $primary 50%;
106
+ padding: 0 1;
107
+ }
108
+
109
+ #bottom-rail {
110
+ column-span: 2;
111
+ height: 10;
112
+ border-top: solid $primary 50%;
113
+ padding: 0 1;
114
+ }
115
+
116
+ #main-grid.with-right #bottom-rail {
117
+ column-span: 3;
118
+ }
119
+
120
+ /* ── Footer status bar — yellow/gold with keybinding shortcuts ── */
121
+ #status-bar {
122
+ height: 1;
123
+ background: $warning;
124
+ color: $background;
125
+ padding: 0 1;
126
+ text-style: bold;
127
+ dock: bottom;
128
+ width: 100%;
129
+ }
130
+
131
+ /* ── Widget-level styles ── */
132
+
133
+ SchemaTree {
134
+ height: 100%;
135
+ }
136
+
137
+ /* TabbedContent inside the editor panel */
138
+ #query-tabs {
139
+ height: 100%;
140
+ }
141
+
142
+ SqlEditor {
143
+ height: 100%;
144
+ }
145
+
146
+ ResultsTable {
147
+ height: 100%;
148
+ }
149
+
150
+ /* ── Command Palette overlay ── */
151
+ CommandPalette {
152
+ layer: overlay;
153
+ align: center top;
154
+ width: 70%;
155
+ height: auto;
156
+ border: thick $accent;
157
+ background: $surface;
158
+ padding: 1;
159
+ margin-top: 4;
160
+ }
161
+
162
+ #palette-input {
163
+ height: 3;
164
+ border: round $primary;
165
+ }
166
+
167
+ #palette-list {
168
+ height: 12;
169
+ border: round $primary;
170
+ }
171
+
172
+ /* ── Help, History & Connection modals ── */
173
+ #HelpModal,
174
+ #HistoryModal,
175
+ #ConnectionModal {
176
+ align: center middle;
177
+ background: $surface;
178
+ border: thick $accent;
179
+ width: 80%;
180
+ max-width: 100;
181
+ height: 60%;
182
+ max-height: 28;
183
+ padding: 1 2;
184
+ }
185
+
186
+ #history-list {
187
+ height: 1fr;
188
+ border: round $primary;
189
+ }
190
+
191
+ #conn-input {
192
+ height: 3;
193
+ border: round $primary;
194
+ margin-bottom: 1;
195
+ }
196
+
197
+ #conn-presets {
198
+ height: 6;
199
+ border: round $primary;
200
+ margin-bottom: 1;
201
+ background: $surface;
202
+ }
203
+
204
+ #conn-submit-btn {
205
+ dock: bottom;
206
+ min-width: 14;
207
+ background: $primary;
208
+ color: $background;
209
+ text-style: bold;
210
+ }
@@ -0,0 +1,262 @@
1
+ Metadata-Version: 2.4
2
+ Name: teridex
3
+ Version: 0.1.0
4
+ Summary: A terminal-native database IDE. Keyboard-first, async, pluggable.
5
+ Author: Teridex contributors
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.13
9
+ Requires-Dist: aiosqlite>=0.20
10
+ Requires-Dist: pydantic-settings>=2.6
11
+ Requires-Dist: pydantic>=2.9
12
+ Requires-Dist: rapidfuzz>=3.10
13
+ Requires-Dist: rich>=13.7
14
+ Requires-Dist: sqlalchemy>=2.0
15
+ Requires-Dist: structlog>=24.4
16
+ Requires-Dist: textual>=0.83
17
+ Requires-Dist: typer>=0.12
18
+ Requires-Dist: typing-extensions>=4.12
19
+ Provides-Extra: all
20
+ Requires-Dist: aiosqlite>=0.20; extra == 'all'
21
+ Requires-Dist: asyncmy2>=0.2.20; extra == 'all'
22
+ Requires-Dist: asyncpg>=0.29; extra == 'all'
23
+ Requires-Dist: cryptography>=42.0; extra == 'all'
24
+ Requires-Dist: duckdb>=1.1; extra == 'all'
25
+ Provides-Extra: duckdb
26
+ Requires-Dist: duckdb>=1.1; extra == 'duckdb'
27
+ Provides-Extra: mysql
28
+ Requires-Dist: asyncmy2>=0.2.20; extra == 'mysql'
29
+ Requires-Dist: cryptography>=42.0; extra == 'mysql'
30
+ Provides-Extra: postgres
31
+ Requires-Dist: asyncpg>=0.29; extra == 'postgres'
32
+ Provides-Extra: sqlite
33
+ Requires-Dist: aiosqlite>=0.20; extra == 'sqlite'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Teridex 📟
37
+
38
+ **A terminal-native database IDE. Keyboard-first, async, pluggable.**
39
+
40
+ Teridex is a TUI database client built on a clean async core and a plugin-first architecture. It combines a rich query editor, lazy schema browser, virtualized result tables, and a fuzzy command palette — all inside your terminal.
41
+
42
+ ---
43
+
44
+ ## ✨ Key Features
45
+
46
+ * **⚡ Asynchronous Execution**: Multi-threaded, fully cancellable database queries. Long-running queries won't block the TUI layout or cursor.
47
+ * **⌨️ Keyboard-First Design**: Optimized for hands-on-keyboard speed. Support for both standard and Vim-style keybindings.
48
+ * **🔌 Pluggable Architecture**: Easily write plugins to add custom panels, new commands to the palette, or listen to event hooks.
49
+ * **🗃️ Built-in Database Drivers**: First-class support for **DuckDB**, **SQLite**, **PostgreSQL**, and **MySQL**.
50
+ * **📝 Rich Workspace Layout**:
51
+ * **Live Schema Tree**: Real-time introspection of schemas, tables, columns, indexes, and foreign keys.
52
+ * **Multi-Tab SQL Editor**: Edit multiple queries side-by-side with SQL syntax highlighting.
53
+ * **Interactive Results Table**: Search, filter, and scroll through large result sets cleanly using pagination/batch loading.
54
+ * **Command Palette**: Quick actions, screen switching, and plugin commands.
55
+ * **⚙️ Configuration Layer**: Customize the look and feel (including Monokai and Nord themes) and define saved connections.
56
+
57
+ ---
58
+
59
+ ## 📐 Package Architecture & Layering
60
+
61
+ Teridex follows a clean, layered architecture with strict dependency boundaries:
62
+
63
+ ```mermaid
64
+ graph TD
65
+ core["teridex-core (Pure Domain)"]
66
+ adapters["teridex-adapters (DB Drivers)"]
67
+ plugins["teridex-plugins (Plugin API)"]
68
+ engine["teridex-engine (Orchestration)"]
69
+ tui["teridex-tui (Textual TUI)"]
70
+ cli["teridex-cli (Typer CLI)"]
71
+
72
+ adapters --> core
73
+ plugins --> core
74
+ engine --> adapters
75
+ engine --> plugins
76
+ tui --> engine
77
+ cli --> engine
78
+ ```
79
+
80
+ > [!IMPORTANT]
81
+ > **Dependency Isolation**: Inner packages must never depend on outer packages. Specifically, `teridex_core` has no dependencies on any other internal package. These boundaries are strictly verified via `mypy --strict`.
82
+
83
+ ---
84
+
85
+ ## 🚀 Getting Started
86
+
87
+ ### Prerequisites
88
+
89
+ * **Python >= 3.13**
90
+ * The [**`uv`**](https://docs.astral.sh/uv/) package manager (recommended) or standard `pip`.
91
+
92
+ ### Installation
93
+
94
+ Install Teridex using `uv` or standard Python package managers:
95
+
96
+ ```bash
97
+ # Install core package
98
+ pip install teridex
99
+
100
+ # Install with support for all database drivers (Postgres, MySQL, SQLite, DuckDB)
101
+ pip install "teridex[all]"
102
+
103
+ # Install with specific drivers
104
+ pip install "teridex[postgres]"
105
+ pip install "teridex[duckdb]"
106
+ ```
107
+
108
+ Alternatively, clone the repository for local development:
109
+
110
+ ```bash
111
+ git clone https://github.com/salvatorecorvaglia/teridex.git
112
+ cd teridex
113
+ ./scripts/dev.sh
114
+ ```
115
+
116
+ ### Running the TUI
117
+
118
+ Start the workspace by pointing it to a database DSN (Data Source Name):
119
+
120
+ ```bash
121
+ # Open with a temporary in-memory DuckDB
122
+ teridex tui --dsn duckdb:///:memory:
123
+
124
+ # Connect to a local SQLite database file
125
+ teridex tui --dsn sqlite:///path/to/database.db
126
+
127
+ # Connect to a PostgreSQL instance
128
+ teridex tui --dsn postgresql://user:password@localhost:5432/mydatabase
129
+ ```
130
+
131
+ ### One-Shot CLI Execution
132
+
133
+ Run query statements directly from the command line and view results rendered as a styled table:
134
+
135
+ ```bash
136
+ teridex run --dsn duckdb:///:memory: "SELECT 'Hello, Teridex!' AS message"
137
+ ```
138
+
139
+ ---
140
+
141
+ ## ⚙️ Configuration
142
+
143
+ Configure Teridex via the config file located at `~/.config/teridex/config.toml`.
144
+
145
+ Refer to the [config.example.toml](file:///Users/salvatorecorvaglia/github/teridex/config.example.toml) file for available options:
146
+
147
+ ```toml
148
+ [ui]
149
+ theme = "monokai" # "monokai" (warm) or "nord" (cool)
150
+ keymap = "default" # "default" or "vim"
151
+ show_status_bar = true
152
+ row_batch_size = 1000 # Rows per batch to fetch from adapters
153
+
154
+ [engine]
155
+ default_timeout_seconds = 60.0
156
+ max_history_entries = 1000
157
+ pool_size = 5
158
+
159
+ [logging]
160
+ level = "INFO"
161
+
162
+ [plugins]
163
+ enabled = [] # Specify IDs of plugins to load (empty loader registers all)
164
+ disabled = [] # Specify IDs of plugins to exclude
165
+ ```
166
+
167
+ ### Environment Overrides
168
+ You can override configuration keys using environment variables using the structure: `TERIDEX_<SECTION>__<FIELD>`.
169
+ For example:
170
+ ```bash
171
+ TERIDEX_UI__THEME=nord teridex tui
172
+ ```
173
+
174
+ ---
175
+
176
+ ## 🔌 Extensibility: Writing Plugins
177
+
178
+ Plugins are discovered using Python entry points registered under the `teridex.plugins` group in your package's metadata.
179
+
180
+ A plugin needs to implement the [Plugin](file:///Users/salvatorecorvaglia/github/teridex/src/teridex_core/protocols/plugin.py#L22-L29) protocol.
181
+
182
+ ### 1. Define the Manifest and Hooks
183
+
184
+ Create a class with a `manifest` property, `on_load`, and `on_unload` methods:
185
+
186
+ ```python
187
+ from teridex_plugins import PluginContext, Command, hook
188
+ from teridex_core.protocols.plugin import PluginManifest
189
+
190
+ class MyPlugin:
191
+ manifest = PluginManifest(
192
+ id="custom-notifier",
193
+ name="Custom Notifier",
194
+ version="0.1.0",
195
+ description="Warns users when executing risky SQL queries.",
196
+ requires_teridex=">=0.1.0"
197
+ )
198
+
199
+ def on_load(self, ctx: PluginContext) -> None:
200
+ # Register command palette action
201
+ ctx.register_command(
202
+ Command(
203
+ id="notify-hello",
204
+ title="Hello Notifier",
205
+ handler=self.hello_handler,
206
+ default_binding="ctrl+h"
207
+ )
208
+ )
209
+ ctx.logger.info("Custom Notifier plugin successfully loaded!")
210
+
211
+ def on_unload(self, ctx: PluginContext) -> None:
212
+ ctx.logger.info("Custom Notifier plugin unloaded.")
213
+
214
+ async def hello_handler(self, ctx: PluginContext) -> None:
215
+ ctx.publish(SomeNotificationEvent("Hello from plugin!"))
216
+
217
+ @hook("query.before_execute")
218
+ async def warn_on_drop(self, ctx: PluginContext, sql: str) -> None:
219
+ if "drop table" in sql.lower():
220
+ ctx.logger.warning("Risky query detected!", sql=sql)
221
+ ```
222
+
223
+ Refer to [api.py](file:///Users/salvatorecorvaglia/github/teridex/src/teridex_plugins/api.py) and [context.py](file:///Users/salvatorecorvaglia/github/teridex/src/teridex_plugins/context.py) for the complete plugin-facing API surface.
224
+
225
+ ---
226
+
227
+ ## 🛠️ Extensibility: Custom Database Adapters
228
+
229
+ Add adapters by subclassing [AbstractAdapter](file:///Users/salvatorecorvaglia/github/teridex/src/teridex_adapters/base.py#L45-L161):
230
+
231
+ 1. Subclass `AbstractAdapter` and set the driver names and URL schemas.
232
+ 2. Implement `_do_connect`, `_do_close`, `ping`, `execute`, `stream`, `begin`, and `introspect`.
233
+ 3. Register your driver class in [registry.py](file:///Users/salvatorecorvaglia/github/teridex/src/teridex_adapters/registry.py).
234
+
235
+ ---
236
+
237
+ ## 🛠️ Local Development & Testing
238
+
239
+ We provide helper scripts inside the `scripts/` directory to run checks, tests, and formatting easily:
240
+
241
+ * **Run All Quality Gates**: `./scripts/check.sh` (Runs linting, typing, formatting, and unit tests).
242
+ * **Format Code**: `./scripts/fmt.sh` (Using Ruff).
243
+ * **Lint & Type Checking**: `./scripts/lint.sh` (Ruff and Mypy strict).
244
+ * **Unit Tests**: `./scripts/test.sh` (Pytest).
245
+
246
+ ---
247
+
248
+ ## 🤝 Contributing
249
+
250
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
251
+
252
+ ## 🔐 Security
253
+
254
+ If you discover a security vulnerability, please see our [Security Policy](SECURITY.md).
255
+
256
+ ## 📝 License
257
+
258
+ Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
259
+
260
+ ---
261
+
262
+ **Author**: [Salvatore Corvaglia](https://github.com/salvatorecorvaglia)
@@ -0,0 +1,76 @@
1
+ teridex_adapters/__init__.py,sha256=sC2NHGhNZRsCAQkGcIuQ3jVowiPqymWZfoiY4_D3Mtg,370
2
+ teridex_adapters/_introspect.py,sha256=wxCwlCo7JUywr5l8YzyTmBgDo4azQmcbIj5GqxGh-MU,3089
3
+ teridex_adapters/_typeinfer.py,sha256=kBICSsg4wVWjwqJvEM-LLdabpxJkds7W4jq8ZlYGh1A,1678
4
+ teridex_adapters/base.py,sha256=coUKPfDHtzgQal76OES7aWVYsJ0FSBn641n6j3NpPSk,5326
5
+ teridex_adapters/duckdb_adapter.py,sha256=obc7tbMBFd2YEAjMRpyyzfbqK7Ecfj3NjSfoapWlA3s,11066
6
+ teridex_adapters/mysql_adapter.py,sha256=BnwYx1eMobruNrFU5W2_3-gF8j-iuaSV9MA_Leyl1pY,14478
7
+ teridex_adapters/postgres_adapter.py,sha256=XuOpb6_NSsbNi82LPOe0-DLDQmCZnBWtCInojV2CAYQ,16411
8
+ teridex_adapters/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ teridex_adapters/registry.py,sha256=HTtM5W9NTP3qttbDsKLamZEArqEqvENvORtT0HGWLQ4,3185
10
+ teridex_adapters/sqlite_adapter.py,sha256=NcIY2mNlfbz2XU9AKISq0aCCAQ2xzjWtFAfHR59nlq0,11156
11
+ teridex_cli/__init__.py,sha256=bPJlQpC7TlrefRpqd-lb12CWiC-XuzjK-dB2ZjWeN0g,86
12
+ teridex_cli/__main__.py,sha256=ak8uVvx3Dew9XrnfC_MWL1L22xeboqrapeymZAfNDfI,73
13
+ teridex_cli/main.py,sha256=zwRCJrhYFNKcXhbI3OQ4oWidw3X_wAA6-GdzZXRydug,7507
14
+ teridex_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ teridex_core/__init__.py,sha256=c0H0NUg0afTRHH_NQZ4a_uiLJOXtBrkp_Gd5t2b5jrw,425
16
+ teridex_core/config.py,sha256=DjlYNKDOvEB-yDpS1te63aCKBJKybLNyIznangq4X8w,4594
17
+ teridex_core/di.py,sha256=3X1pQzofiItHMPd0TAbWTs2Yn8lDhAWe1MsqumxB1u4,3222
18
+ teridex_core/errors.py,sha256=0X4HBtAPmAHw3mMkbYxKY2tdocgPc7m7f3MaR3Vv-RU,1857
19
+ teridex_core/events.py,sha256=3EV5az1Hu8IvndcCrAqM1tYHpqbz0EvGRpyKLJGmqjc,8858
20
+ teridex_core/logging.py,sha256=-2gYkTNP6D6BTdepPTzI2YA-ksPm22k3QKq16f3aKzw,3423
21
+ teridex_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ teridex_core/result.py,sha256=s9UuZpmyg0pKO96mfPYr2Px_B9J6ANfRraPfvYc9UjY,1617
23
+ teridex_core/models/__init__.py,sha256=od6eSMP1AQOaSVxOzo9KHghe6NkYe3ObrDM5VOA3j9g,671
24
+ teridex_core/models/connection.py,sha256=h4ROfhGG0L_VjluOwFT1vLfquDJcgIc3Ag3jsh_z07g,3838
25
+ teridex_core/models/query.py,sha256=6yvpR49KVXOxp1HXqCjfqPpjXjl7yFmLrAxdaRauetU,1841
26
+ teridex_core/models/result.py,sha256=0ffv5iUkTd5PShzQXnX8Sl6YYMlOyS9_KAxTrqQuEYo,1077
27
+ teridex_core/models/schema.py,sha256=mIJzZXyY5FZ5VXMPX_HDW0ONNNTwbZKPQmBGhqUxbp4,2003
28
+ teridex_core/protocols/__init__.py,sha256=NIWFXulXkAadv4r2YnQHz8fWAIH9UNwWucxDzddz2Ow,313
29
+ teridex_core/protocols/adapter.py,sha256=X-NsTTSIodacvVDOgMzJ4_D5eDk6SqSJ7GkcU2Ao9dY,2022
30
+ teridex_core/protocols/plugin.py,sha256=aeacrZhJjYpXEgAgXhmpV9aZRfkDEnh90XcECeZN2Dc,627
31
+ teridex_engine/__init__.py,sha256=XfQevS3y-46PZ7F7GM9-fD2-zAhuc_4zV32N2ujIoU4,369
32
+ teridex_engine/executor.py,sha256=Frkc0KT4bjeuBRfapMXZKxQbCr0NSS3dpMFUnxfGbPc,5447
33
+ teridex_engine/history.py,sha256=kF5q58UBTjXX2h4_OmPOWEFcod-2YH3WWjpJLbf1xtg,5002
34
+ teridex_engine/introspector.py,sha256=VR6_sBgZTPIhWcR0gO_jbPcdL9cq5fN2WxBl39fTHto,2989
35
+ teridex_engine/pool.py,sha256=uwCrBmLApPI2JgUV-uO2FvB_izjhUFWk6aze7-Qr_sc,7053
36
+ teridex_engine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ teridex_engine/transaction.py,sha256=38v3AzQXo4wbuiJgEnNXYWbx_pmJo--FkCFYso20tyY,718
38
+ teridex_plugins/__init__.py,sha256=J9urJ7pXVsIyoXE1rILhdNYUFmIZX_UIqEJl3gWpQU0,386
39
+ teridex_plugins/api.py,sha256=ur9iR9MUf7hgll11QqfoR2TUgacvActdPJy_yI0v8sk,2863
40
+ teridex_plugins/context.py,sha256=RurREVHl8Cusb2tUDTNqrS_GMVqoDBM3yiQ6sMuj4do,2509
41
+ teridex_plugins/loader.py,sha256=EAqq42PBo5vhkWdcdtaV0deHo1NhpCN6GNBsqhVnzvk,7291
42
+ teridex_plugins/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
+ teridex_plugins/registry.py,sha256=L9TeDqPNvAvNhKDHt1LUtkb2UjZ11FUO6ORIlEX2ME4,3279
44
+ teridex_tui/__init__.py,sha256=oFW7OHcjahBSD9Uil-UgzhVlS8MBtFGxemnItDsVD-U,93
45
+ teridex_tui/app.py,sha256=j7RADqc_b2AqOM9hNjoi7R5gkujEM5ykNg8jzZA-8cA,21056
46
+ teridex_tui/builtin_commands.py,sha256=-1mCnN-ql7rE2ExkNCxjQm30Wthsh0lb5YCeDvTT4KA,2894
47
+ teridex_tui/events.py,sha256=U2AZ4In55CWy8jo53_HAKW2QK9XYLcaov0Z3MEoiSFw,211
48
+ teridex_tui/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ teridex_tui/state.py,sha256=hFJdrY84TSPcMbLfl2fTBvbUKPsXqjRY2XIts5-fAlY,1287
50
+ teridex_tui/keymaps/__init__.py,sha256=jm3T7oyv-eK0s0c25x4NEwM7bkHWMoOWRNfq9ktAfmg,177
51
+ teridex_tui/keymaps/default.py,sha256=zzTqce8p14hLs5XTgyJoThpTL4F1qyVWB_WbdpptmIg,689
52
+ teridex_tui/keymaps/vim.py,sha256=SIoaQlHZcztyUpIjD3fwSeH4oLhrb93Ob3i2k8dmiGU,391
53
+ teridex_tui/screens/__init__.py,sha256=OmbfgnhDzzU-HwiB6lyZuMcXzDvGq1Zm0xXrprJAPxs,263
54
+ teridex_tui/screens/command_palette.py,sha256=znJ-QLRPLPcX5Nz-rP5fafaJRoS3NVs6q6xP4sjoIsk,3142
55
+ teridex_tui/screens/connection.py,sha256=vUX0DGyGJCAisFkFHnlLs0ucNBBSiIp3BHUgMnnRiBs,3293
56
+ teridex_tui/screens/help.py,sha256=YCaXNW1f-MW_HFWbfMImoIejqQGWI_cRtL8gTAO6Nv0,2191
57
+ teridex_tui/screens/history.py,sha256=YVKpY0ReTWjFNVWkqbBmhN2tbp3MD2_VvdvPfON2jIU,2809
58
+ teridex_tui/screens/main.py,sha256=I68-6Luca2L_oJNyCiIyTBMDtomaImzqHPtWmpDCIx0,1947
59
+ teridex_tui/themes/__init__.py,sha256=m1goovlUTDGLmIqp7ZiyAevzwQq3JcUsJ8FXW1uyHHw,196
60
+ teridex_tui/themes/_base.py,sha256=U_0q59tclzsZK3RLZKixXfWOEHKwx_egGuW5lWw2PGE,712
61
+ teridex_tui/themes/monokai.py,sha256=rjX8uE7u9BoxvP0lX_dpPqBhi8S_1ExW3zFFiHda5SQ,270
62
+ teridex_tui/themes/nord.py,sha256=SmnJ9kKtTYF9Wcnk035B2QeU3cCOCEd9PlW09oueXao,264
63
+ teridex_tui/widgets/__init__.py,sha256=ClBmkngBKp1tBK3YgRwLbfKmocLgyhufECJOphu1c6Q,440
64
+ teridex_tui/widgets/action_bar.py,sha256=urwsHHjBYkYEg4Zi2y2-PxvDqC_y1WirDhgDYTVnXhM,1570
65
+ teridex_tui/widgets/query_tabs.py,sha256=uq_FrMC9lGI_MylI52aQXxlpCAK_IQG89-KsfzdNFEQ,1262
66
+ teridex_tui/widgets/results_table.py,sha256=LsChKQKgecQMm81g-xSK-5lPhy_CFusd3CKbFzKmKZQ,4008
67
+ teridex_tui/widgets/schema_tree.py,sha256=fW2KaVsrcx1s-JdTFoYLzICqox4qOyqFi5o4md-80-Y,5161
68
+ teridex_tui/widgets/sql_editor.py,sha256=RGLVXJml07uvto6MNiZfRwzrwvbI3JHjcE9BZsvaHIY,451
69
+ teridex_tui/widgets/status_bar.py,sha256=bWvmmlhBfRx0YdyeV-_ND8hRrzehZ59vzo-QRjMvshk,2357
70
+ teridex_tui/teridex.tcss,sha256=LffglMysUMLuQEO78I1_dr0yCCQj3YpO5wptjKoC6Mo,4411
71
+ teridex-0.1.0.data/data/share/teridex/teridex.tcss,sha256=LffglMysUMLuQEO78I1_dr0yCCQj3YpO5wptjKoC6Mo,4411
72
+ teridex-0.1.0.dist-info/METADATA,sha256=JtrtnAh6kSLVFbaHHHR9wbMHemlMZXsPBlNPC4PXt2Q,8816
73
+ teridex-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
74
+ teridex-0.1.0.dist-info/entry_points.txt,sha256=r8vzfTwyXOIJMYo58EZNcs--969-U7Cwl5a8GZ1wEEA,50
75
+ teridex-0.1.0.dist-info/licenses/LICENSE,sha256=mc2jDLJgiCUp5k06wO8mwETj7G0bnsyyLe756FL-MoI,1119
76
+ teridex-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ teridex = teridex_cli.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 **salvatorecorvaglia** (https://github.com/salvatorecorvaglia)
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,17 @@
1
+ """Teridex database adapters."""
2
+
3
+ from teridex_adapters.base import AbstractAdapter
4
+ from teridex_adapters.registry import (
5
+ AdapterRegistry,
6
+ create_adapter_for_dsn,
7
+ default_registry,
8
+ reset_default_registry,
9
+ )
10
+
11
+ __all__ = [
12
+ "AbstractAdapter",
13
+ "AdapterRegistry",
14
+ "create_adapter_for_dsn",
15
+ "default_registry",
16
+ "reset_default_registry",
17
+ ]
@@ -0,0 +1,97 @@
1
+ """Shared introspection orchestration.
2
+
3
+ Each adapter owns its own catalog queries (they are inherently DB-specific),
4
+ but the per-object loop, kind dispatch, and ``SchemaSnapshot`` assembly live
5
+ here once. Subclasses normalize ``kind`` to one of ``"table"``, ``"view"``,
6
+ ``"materialized_view"`` so the base class can dispatch without per-DB casing.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from abc import ABC, abstractmethod
12
+ from typing import TYPE_CHECKING
13
+
14
+ from teridex_core.models.schema import (
15
+ SchemaSnapshot,
16
+ Table,
17
+ View,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from teridex_core.models.schema import (
22
+ ForeignKey,
23
+ Index,
24
+ SchemaObject,
25
+ TableColumn,
26
+ )
27
+
28
+
29
+ class SchemaIntrospector(ABC):
30
+ """Template-method base that drives the per-object introspection loop."""
31
+
32
+ @abstractmethod
33
+ def connection_id(self) -> str: ...
34
+
35
+ @abstractmethod
36
+ def database_name(self) -> str | None: ...
37
+
38
+ @abstractmethod
39
+ async def list_objects(self) -> list[tuple[str, str, str]]:
40
+ """Return ``(schema, name, kind)`` for every visible table/view.
41
+
42
+ ``kind`` must be normalized to ``"table"``, ``"view"``, or
43
+ ``"materialized_view"``.
44
+ """
45
+
46
+ @abstractmethod
47
+ async def fetch_columns(self, schema: str, name: str) -> list[TableColumn]: ...
48
+
49
+ async def fetch_foreign_keys(self, schema: str, name: str) -> list[ForeignKey]:
50
+ return []
51
+
52
+ async def fetch_indexes(self, schema: str, name: str) -> list[Index]:
53
+ return []
54
+
55
+ def build_view(self, schema: str, name: str, kind: str, columns: list[TableColumn]) -> View:
56
+ from typing import Literal, cast # noqa: PLC0415
57
+
58
+ return View(
59
+ name=name,
60
+ schema_name=schema,
61
+ columns=columns,
62
+ kind=cast('Literal["view", "materialized_view"]', kind),
63
+ )
64
+
65
+ async def build(self, *, lazy: bool = False) -> SchemaSnapshot:
66
+ schemas: dict[str, list[SchemaObject]] = {}
67
+ for schema_name, name, kind in await self.list_objects():
68
+ obj: SchemaObject
69
+ if lazy:
70
+ cols = []
71
+ fks = []
72
+ indexes = []
73
+ else:
74
+ cols = await self.fetch_columns(schema_name, name)
75
+ if kind == "table":
76
+ fks = await self.fetch_foreign_keys(schema_name, name)
77
+ indexes = await self.fetch_indexes(schema_name, name)
78
+ else:
79
+ fks = []
80
+ indexes = []
81
+
82
+ if kind == "table":
83
+ obj = Table(
84
+ name=name,
85
+ schema_name=schema_name,
86
+ columns=cols,
87
+ foreign_keys=fks,
88
+ indexes=indexes,
89
+ )
90
+ else:
91
+ obj = self.build_view(schema_name, name, kind, cols)
92
+ schemas.setdefault(schema_name, []).append(obj)
93
+ return SchemaSnapshot(
94
+ connection_id=self.connection_id(),
95
+ database=self.database_name(),
96
+ schemas=schemas,
97
+ )