|
| 1 | +# Protocol Split Design: HTTP ↔ SQL |
| 2 | + |
| 3 | +> Splitting the current monorepo into two independent projects — `poste.nvim` (HTTP + Redis) and `poste-sql.nvim`. |
| 4 | +> |
| 5 | +> Status: **Proposal** · Last updated: 2026-07-21 |
| 6 | +
|
| 7 | +--- |
| 8 | + |
| 9 | +## Motivation |
| 10 | + |
| 11 | +HTTP and SQL share little commonality at the feature level. Keeping them in one repo creates friction: |
| 12 | + |
| 13 | +1. **Shared-file coupling** — `lua/poste/init.lua` unconditionally loads SQL during `setup()`, even for HTTP-only users |
| 14 | +2. **Dispatch entanglement** — `buffer_setup.lua`, `indicators.lua`, and help windows must understand both protocols |
| 15 | +3. **Uneven release cycles** — HTTP evolves faster than SQL; bundling them forces SQL users to take HTTP changes and vice versa |
| 16 | +4. **Cognitive overhead** — contributors must understand the full project to modify shared files |
| 17 | +5. **Plugin load cost** — SQL modules (~20+ files) load on every Neovim startup even if user never opens `.sql` |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## Current Architecture |
| 22 | + |
| 23 | +``` |
| 24 | +poste/ # Single repo |
| 25 | +├── lua/poste/ |
| 26 | +│ ├── init.lua ← Coupled: loads HTTP + SQL unconditionally |
| 27 | +│ ├── state.lua ← Shared: keymaps, config, M.sql namespace |
| 28 | +│ ├── buffer_setup.lua ← Coupled: dispatches to SQL via filetype check |
| 29 | +│ ├── indicators.lua ← Shared: imports poste.http.cache (reverse coupling) |
| 30 | +│ ├── select.lua ← Shared: generic picker |
| 31 | +│ ├── help.lua ← Coupled: shows HTTP and SQL keymaps together |
| 32 | +│ ├── constants.lua ← Shared |
| 33 | +│ ├── util.lua ← Shared |
| 34 | +│ ├── install.lua ← Shared |
| 35 | +│ ├── cli.lua ← Shared |
| 36 | +│ ├── http/ ← HTTP protocol Lua |
| 37 | +│ └── sql/ ← SQL protocol Lua |
| 38 | +├── crates/ |
| 39 | +│ ├── poste-core/ ← Shared: Protocol enum, parser, request.rs |
| 40 | +│ ├── poste-exec/ ← Shared: executor.rs + sql_executor/ |
| 41 | +│ └── poste-cli/ ← Shared: single binary |
| 42 | +├── ftdetect/poste.vim ← Coupled: .http + .redis + .sql + .sqlite |
| 43 | +├── syntax/ ← Split: poste_http.vim + poste_sql.vim (already separate) |
| 44 | +└── plugin/poste.lua ← Shared: calls require("poste").setup() |
| 45 | +``` |
| 46 | + |
| 47 | +### Specific coupling points |
| 48 | + |
| 49 | +| Point | File | Problem | |
| 50 | +|-------|------|---------| |
| 51 | +| CP1 | `lua/poste/init.lua:55` | `require("poste.sql.init").setup(opts)` — always loads SQL | |
| 52 | +| CP2 | `lua/poste/init.lua:175-225` | Single autocmd block handles `.http`, `.redis`, `.sql`, `.sqlite` | |
| 53 | +| CP3 | `lua/poste/buffer_setup.lua:35-40` | `goto_definition` keymap checks filetype and dispatches to SQL | |
| 54 | +| CP4 | `lua/poste/indicators.lua:86` | `require("poste.http.cache")` — shared module depends on HTTP | |
| 55 | +| CP5 | `lua/poste/help.lua` | Single help window shows both HTTP and SQL keymaps | |
| 56 | +| CP6 | `lua/poste/state.lua:222-241` | `M.sql` namespace lives in shared state file | |
| 57 | +| CP7 | `lua/poste/http/run.lua:533-536` | HTTP run module imports SQL init | |
| 58 | +| CP8 | `crates/poste-exec/src/executor.rs` | Single dispatch routes to HTTP, Redis, and SQL | |
| 59 | +| CP9 | `crates/poste-cli/src/run.rs` | Single `execute()` function handles all protocols | |
| 60 | +| CP10 | `crates/poste-core/src/request.rs` | `Protocol` enum unifies all protocols in one type | |
| 61 | +| CP11 | `ftdetect/poste.vim` | Single file sets filetypes for all protocols | |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +## Target Architecture |
| 66 | + |
| 67 | +### Option A: Core + SQL Plugin (Recommended) |
| 68 | + |
| 69 | +``` |
| 70 | +poste.nvim (monorepo) poste-sql.nvim (separate repo) |
| 71 | +├── lua/poste/ ├── lua/poste/sql/ |
| 72 | +│ ├── init.lua │ ├── init.lua (was poste.sql.init) |
| 73 | +│ ├── state.lua │ ├── buffer.lua |
| 74 | +│ ├── constants.lua │ ├── completion.lua |
| 75 | +│ ├── select.lua │ ├── context.lua |
| 76 | +│ ├── indicators.lua │ ├── connections.lua |
| 77 | +│ ├── buffer_setup.lua │ ├── db_browser/ |
| 78 | +│ ├── util.lua │ ├── export.lua |
| 79 | +│ ├── install.lua │ ├── format.lua |
| 80 | +│ ├── cli.lua │ ├── editor.lua |
| 81 | +│ ├── help.lua │ ├── import.lua |
| 82 | +│ ├── error.lua │ ├── introspect.lua |
| 83 | +│ └── http/ │ ├── statement.lua |
| 84 | +│ ├── init.lua │ ├── syntax.lua |
| 85 | +│ ├── run.lua │ └── ... |
| 86 | +│ ├── format.lua ├── ftdetect/ (.sql only) |
| 87 | +│ └── ... ├── syntax/ (poste_sql.vim) |
| 88 | +├── ftdetect/ (.http/.redis) ├── plugin/poste-sql.lua |
| 89 | +├── syntax/ (poste_http.vim) └── depends: poste.nvim (shared infra + binary) |
| 90 | +├── plugin/poste.lua |
| 91 | +└── crates/ (unchanged) |
| 92 | + ├── poste-core/ |
| 93 | + ├── poste-exec/ |
| 94 | + └── poste-cli/ |
| 95 | +``` |
| 96 | + |
| 97 | +**Key design decisions:** |
| 98 | + |
| 99 | +1. **Rust binary stays monolithic** — `poste` binary compiles all protocols. SQL plugin users just need the same binary. Splitting Rust would mean duplicating `poste-core` and maintaining two CI pipelines for negligible gain. |
| 100 | + |
| 101 | +2. **Shared Lua stays in `poste.nvim`** — `state.lua`, `select.lua`, `indicators.lua`, `util.lua`, `cli.lua`, `install.lua`, `constants.lua`, `error.lua` are genuinely protocol-neutral. SQL plugin depends on them. |
| 102 | + |
| 103 | +3. **SQL plugin uses `require("poste.*")` paths** — no path aliasing needed. The `poste.nvim` plugin must be on `rtp` for SQL plugin to work. |
| 104 | + |
| 105 | +4. **`poste-sql.nvim` only ships `lua/poste/sql/`** — its `plugin/poste-sql.lua` calls `require("poste.sql.init").setup()` which is now moved from `poste.nvim` to `poste-sql.nvim`. |
| 106 | + |
| 107 | +### Option B: Two Independent Plugins (Alternative) |
| 108 | + |
| 109 | +Each plugin duplicates shared Lua code. Appropriate if zero external dependency is critical. |
| 110 | + |
| 111 | +``` |
| 112 | +poste-http.nvim poste-sql.nvim |
| 113 | +├── lua/poste/ ├── lua/poste/ |
| 114 | +│ ├── init.lua │ ├── init.lua |
| 115 | +│ ├── state.lua (copy) │ ├── state.lua (copy) |
| 116 | +│ ├── constants.lua (copy) │ ├── constants.lua (copy) |
| 117 | +│ ├── select.lua (copy) │ ├── select.lua (copy) |
| 118 | +│ ├── indicators.lua (copy) │ ├── indicators.lua (copy) |
| 119 | +│ ├── util.lua (copy) │ ├── util.lua (copy) |
| 120 | +│ ├── install.lua (copy) │ ├── install.lua (copy) |
| 121 | +│ ├── cli.lua (copy) │ ├── cli.lua (copy) |
| 122 | +│ ├── help.lua (copy) │ ├── help.lua (copy) |
| 123 | +│ ├── error.lua (copy) │ ├── error.lua (copy) |
| 124 | +│ └── http/ │ └── sql/ |
| 125 | +├── ftdetect/ ├── ftdetect/ |
| 126 | +├── syntax/ ├── syntax/ |
| 127 | +├── plugin/poste.lua ├── plugin/poste.lua |
| 128 | +└── crates/ (same binary) └── crates/ (same binary) |
| 129 | +``` |
| 130 | + |
| 131 | +**Not recommended** — ~10 shared files duplicated, drift inevitable. |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +## Migration Plan |
| 136 | + |
| 137 | +### Phase 1: Decouple (within current repo) |
| 138 | + |
| 139 | +Remove all cross-coupling between HTTP and SQL code without changing the repo structure. |
| 140 | + |
| 141 | +| Step | File | Change | |
| 142 | +|------|------|--------| |
| 143 | +| D1 | `lua/poste/init.lua` | Remove `require("poste.sql.init").setup(opts)`. SQL sets up its own autocmds via `plugin/poste-sql.lua` | |
| 144 | +| D2 | `lua/poste/init.lua:175-225` | Split autocmd: `.sql`/`.sqlite` handled by SQL plugin's `ftdetect/` + `plugin/` | |
| 145 | +| D3 | `lua/poste/buffer_setup.lua:35-40` | Remove SQL dispatch from `goto_definition`. SQL plugin installs its own keymap | |
| 146 | +| D4 | `lua/poste/indicators.lua:86` | Remove `require("poste.http.cache")`. Move that logic into HTTP-specific path | |
| 147 | +| D5 | `lua/poste/help.lua` | Split into `http/help.lua` and `sql/help.lua`. Shared help shows only what's installed | |
| 148 | +| D6 | `lua/poste/state.lua:222-241` | Move `M.sql` namespace into `lua/poste/sql/state.lua` (loaded by SQL plugin) | |
| 149 | +| D7 | `lua/poste/http/run.lua:533-536` | Remove SQL delegation. SQL plugin handles its own `run_request` | |
| 150 | +| D8 | `ftdetect/poste.vim` | Remove `.sql`/`.sqlite` entries. Handled by SQL plugin's own `ftdetect/` | |
| 151 | + |
| 152 | +After D1–D8, `poste.nvim` loads **zero** SQL modules. SQL modules only load when user opens a `.sql` file and SQL plugin is installed. |
| 153 | + |
| 154 | +### Phase 2: Extract SQL Repo |
| 155 | + |
| 156 | +1. Create `poste-sql.nvim` repo with `lua/poste/sql/` subtree |
| 157 | +2. Add `plugin/poste-sql.lua`: |
| 158 | + |
| 159 | +```lua |
| 160 | +-- plugin/poste-sql.lua |
| 161 | +-- Only activates if poste.nvim (core) is installed |
| 162 | +local ok, _ = pcall(require, "poste.state") |
| 163 | +if not ok then |
| 164 | + vim.notify("poste-sql.nvim requires poste.nvim", vim.log.levels.WARN) |
| 165 | + return |
| 166 | +end |
| 167 | +require("poste.sql.init").setup() |
| 168 | +``` |
| 169 | + |
| 170 | +3. Add `ftdetect/poste_sql.vim`: |
| 171 | + |
| 172 | +```vim |
| 173 | +au BufRead,BufNewFile *.sql setfiletype poste_sql |
| 174 | +au BufRead,BufNewFile *.sqlite setfiletype poste_sqlite |
| 175 | +``` |
| 176 | + |
| 177 | +4. Add `syntax/poste_sql.vim` and `syntax/poste_dataset.vim` (already separate files) |
| 178 | +5. Remove `lua/poste/sql/` from `poste.nvim` repo |
| 179 | +6. Remove `.sql`/`.sqlite` from `poste.nvim`'s `ftdetect/poste.vim` |
| 180 | + |
| 181 | +### Phase 3: Shared Infra Cleanup |
| 182 | + |
| 183 | +After the split, prune `poste.nvim`: |
| 184 | + |
| 185 | +- `state.lua` — remove `M.sql` namespace, remove SQL keymap sections |
| 186 | +- `init.lua` — remove `poste_sql`/`poste_sqlite` from `poste_status()` check |
| 187 | +- `help.lua` — show only HTTP + Redis keymaps |
| 188 | +- `buffer_setup.lua` — remove SQL filetype dispatch |
| 189 | + |
| 190 | +--- |
| 191 | + |
| 192 | +## Rust Side: No Split |
| 193 | + |
| 194 | +The Rust binary stays monolithic for these reasons: |
| 195 | + |
| 196 | +1. **Dependency graph is already clean** — `sql_executor/` is a separate module, zero imports from `executor.rs` |
| 197 | +2. **No compile-time conflict** — sqlx and curl-rust coexist without issues |
| 198 | +3. **CI cost** — one binary = one compile, one release artifact |
| 199 | +4. **User experience** — both plugins use `:PosteUpdate` to get the same binary |
| 200 | + |
| 201 | +The only Rust change needed: ensure `poste run` doesn't require SQL libraries when running HTTP requests (it already doesn't — dispatch is purely based on `Protocol` enum). |
| 202 | + |
| 203 | +--- |
| 204 | + |
| 205 | +## Impact Analysis |
| 206 | + |
| 207 | +### What breaks |
| 208 | + |
| 209 | +| User scenario | Impact | Mitigation | |
| 210 | +|---------------|--------|------------| |
| 211 | +| Has both `.http` and `.sql` files | Must install both plugins | Clear README, single install command via lazy.nvim spec | |
| 212 | +| Uses `require("poste.sql.*")` in custom scripts | Works if SQL plugin is installed | Error message if missing | |
| 213 | +| Autocmds referencing `poste_sql` filetype | Unaffected | Filetype registration moved to SQL plugin | |
| 214 | +| CI / test scripts | `tests/run.sh` must cover both repos | Each repo has its own tests | |
| 215 | + |
| 216 | +### What improves |
| 217 | + |
| 218 | +| Metric | Before | After | |
| 219 | +|--------|--------|-------| |
| 220 | +| Plugin load time (HTTP-only user) | Loads ~20 SQL modules | Zero SQL modules | |
| 221 | +| Plugin load time (SQL-only user) | Loads ~30 HTTP modules | Zero HTTP modules | |
| 222 | +| Shared-file touch conflicts | `init.lua` changed by both | Each repo owns its `init.lua` | |
| 223 | +| Release cycle | Lockstep | Independent | |
| 224 | +| New contributor onboarding | Must understand all protocols | Only needs to understand one | |
| 225 | + |
| 226 | +### Compat scenarios |
| 227 | + |
| 228 | +Users who want both install: |
| 229 | + |
| 230 | +```lua |
| 231 | +-- lazy.nvim |
| 232 | +{ |
| 233 | + "beyondlex/poste.nvim", |
| 234 | + opts = {}, |
| 235 | + dependencies = { |
| 236 | + "beyondlex/poste-sql.nvim", -- optional |
| 237 | + }, |
| 238 | +} |
| 239 | +``` |
| 240 | + |
| 241 | +--- |
| 242 | + |
| 243 | +## Related Documents |
| 244 | + |
| 245 | +- [Architecture Overview](./architecture-overview.md) — current layered architecture |
| 246 | +- [Refactoring Plan](./refactoring-plan.md) — F1-F8 architecture debt remediation |
| 247 | +- [HTTP Dev Docs](./http/README.md) |
| 248 | +- [SQL Dev Docs](./sql/README.md) |
| 249 | +- [File Index](./file-index.md) |
| 250 | + |
| 251 | +--- |
| 252 | + |
| 253 | +*Protocol split design — Last updated: 2026-07-21* |
0 commit comments