sql-dev-inspector 0.0.0-stage → 1.0.1
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.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +267 -2
- package/dist/client/nuxt-plugin.client.d.ts +2 -0
- package/dist/client/nuxt-plugin.client.js +11 -0
- package/dist/client/widget.d.ts +40 -0
- package/dist/client/widget.js +485 -0
- package/dist/i18n/index.d.ts +26 -0
- package/dist/i18n/index.js +48 -0
- package/dist/i18n/messages.d.ts +119 -0
- package/dist/i18n/messages.js +331 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/inspector-server.d.ts +11 -0
- package/dist/inspector-server.js +95 -0
- package/dist/nitro-plugin.d.ts +19 -0
- package/dist/nitro-plugin.js +30 -0
- package/dist/node.d.ts +27 -0
- package/dist/node.js +52 -0
- package/dist/nuxt.d.ts +20 -0
- package/dist/nuxt.js +21 -0
- package/dist/report.d.ts +24 -0
- package/dist/report.js +182 -0
- package/dist/runtime.d.ts +21 -0
- package/dist/runtime.js +42 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +44 -0
- package/dist/sql-analyzer.d.ts +12 -0
- package/dist/sql-analyzer.js +214 -0
- package/dist/store.d.ts +36 -0
- package/dist/store.js +86 -0
- package/dist/terminal.d.ts +5 -0
- package/dist/terminal.js +64 -0
- package/dist/tracer.d.ts +36 -0
- package/dist/tracer.js +143 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +1 -0
- package/dist/ui/client.d.ts +1 -0
- package/dist/ui/client.js +472 -0
- package/dist/ui/page.d.ts +6 -0
- package/dist/ui/page.js +73 -0
- package/dist/ui/styles.d.ts +7 -0
- package/dist/ui/styles.js +207 -0
- package/package.json +80 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.1] - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- CI: publish to npm automatically through trusted publishing (OIDC) when a GitHub Release is published. Provenance is attached automatically.
|
|
8
|
+
|
|
9
|
+
## [1.0.0] - 2026-10-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Initial release.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nhu Duy Thin
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,3 +1,268 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sql-dev-inspector
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**See exactly which SQL your backend ran for each request — in development only.**
|
|
4
|
+
|
|
5
|
+
When a client calls your API, `sql-dev-inspector` shows every statement your server sent to MySQL: in what order, inside which transaction or savepoint, how deeply subqueries nest, how long each one took and how many rows it returned. It also flags N+1 loops, repeated queries, slow queries and SQL errors.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
GET /api/orders/42 → 200 163ms total · 12 queries · 148ms in SQL
|
|
9
|
+
├─ SELECT order_items 3.86ms · 2 rows
|
|
10
|
+
│ select `id`, `sku` from `order_items` where `order_id` = 11
|
|
11
|
+
├─ SELECT customers, orders ⤷subquery×3 (depth 3) 1.50ms · 2 rows
|
|
12
|
+
├─ TRANSACTION [conn 101] committed 10.2ms
|
|
13
|
+
│ ├─ INSERT orders 1.39ms · 1 affected
|
|
14
|
+
│ ├─ SAVEPOINT sp1 [conn 101] rolled-back 4.30ms
|
|
15
|
+
│ │ └─ SELECT 0.18ms ✖ You have an error in your SQL syntax …
|
|
16
|
+
│ └─ SELECT orders 1.33ms · 2 rows
|
|
17
|
+
⚠ N+1 suspected: 4×, 8.4ms total — select `id`, `sku` from `order_items` where `order_id` = ?
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Per-request attribution** — every statement is tied to the request that caused it (concurrent requests and a saturated connection pool included), using `AsyncLocalStorage`.
|
|
23
|
+
- **Nesting tree** — transactions and savepoints, with committed / rolled-back / open status. Two transactions running in parallel inside one request are kept apart.
|
|
24
|
+
- **Subquery depth** and pretty-printed SQL.
|
|
25
|
+
- **Multilingual**: English, Vietnamese and Japanese — terminal, web page and floating button — and adding a language is one block of text (see [Languages](#languages)).
|
|
26
|
+
- **Professional UI**: light / dark / system theme, responsive down to a narrow docked panel, keyboard navigation, syntax-highlighted SQL, timeline waterfall per statement.
|
|
27
|
+
- **Three ways to look**: a tree in the terminal, a live web page (`/__sql-inspector/`), and a floating in-app launcher button with keyboard shortcuts.
|
|
28
|
+
- **No changes to your queries** — it works at the `mysql2` driver level, so it covers plain `mysql2` and Drizzle ORM (both tested), and in principle anything else built on `mysql2`.
|
|
29
|
+
- **Zero runtime dependencies** besides the `mysql2` peer. The web UI is a single self-contained page; nothing is loaded from a CDN.
|
|
30
|
+
- **Safe by default** — never active when `NODE_ENV=production`; there it is a complete no-op.
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- Node.js **20+**
|
|
35
|
+
- `mysql2` **3+** (peer dependency; tested with 3.20)
|
|
36
|
+
- Your app must send SQL through `mysql2` (pool, connection, or an ORM that uses it)
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install --save-dev sql-dev-inspector
|
|
42
|
+
# or
|
|
43
|
+
pnpm add -D sql-dev-inspector
|
|
44
|
+
yarn add -D sql-dev-inspector
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Other ways to install (from GitHub, from a `.tgz` file): see [docs/PUBLISH-AND-INSTALL.md](docs/PUBLISH-AND-INSTALL.md) *(Vietnamese)*.
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
The inspector turns on when `NODE_ENV=development` (or `SQL_INSPECTOR=1`). Most dev servers (`nuxt dev`, `next dev`, `vite`) set `NODE_ENV` for you; for a plain Node script run `NODE_ENV=development node server.js`.
|
|
52
|
+
|
|
53
|
+
### Nuxt 3 / 4
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
// nuxt.config.ts
|
|
57
|
+
export default defineNuxtConfig({
|
|
58
|
+
modules: ["sql-dev-inspector/nuxt"]
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
That's it. The module registers itself in `nuxt dev` only (nothing is added to `build` / `generate`): a Nitro plugin on the server and a floating **SQL** button in the app. Open `http://localhost:3000/__sql-inspector/` or click the button. If you set `app.baseURL`, prefix the path with it.
|
|
63
|
+
|
|
64
|
+
### Nitro (without Nuxt)
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// nitro.config.ts
|
|
68
|
+
export default defineNitroConfig({
|
|
69
|
+
plugins: ["sql-dev-inspector/nitro"]
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Express / Connect
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import express from "express"
|
|
77
|
+
import { createSqlInspector } from "sql-dev-inspector"
|
|
78
|
+
|
|
79
|
+
const app = express()
|
|
80
|
+
const sqlInspector = createSqlInspector()
|
|
81
|
+
|
|
82
|
+
app.use(sqlInspector.middleware) // mount BEFORE any route that runs SQL
|
|
83
|
+
|
|
84
|
+
app.get("/orders/:id", async (req, res) => {
|
|
85
|
+
const [rows] = await pool.query("select * from orders where id = ?", [req.params.id])
|
|
86
|
+
res.json(rows)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
app.listen(3000)
|
|
90
|
+
// open http://localhost:3000/__sql-inspector/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Plain Node `http` (or any other framework)
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import http from "node:http"
|
|
97
|
+
import { createSqlInspector } from "sql-dev-inspector"
|
|
98
|
+
|
|
99
|
+
const sqlInspector = createSqlInspector()
|
|
100
|
+
|
|
101
|
+
http.createServer((req, res) => {
|
|
102
|
+
if (sqlInspector.handle(req, res)) return // serves the inspector's own pages
|
|
103
|
+
sqlInspector.track(req, res, () => myApp(req, res)) // everything awaited inside belongs to this request
|
|
104
|
+
}).listen(3000)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Jobs, cron, scripts (not HTTP requests)
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
await sqlInspector.task("nightly-sync", async () => {
|
|
111
|
+
await syncCustomers()
|
|
112
|
+
})
|
|
113
|
+
// shows up in the list as a TASK named "nightly-sync"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Floating button in a non-Nuxt frontend
|
|
117
|
+
|
|
118
|
+
In your browser entry point (same origin as the server):
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
if (import.meta.env.DEV) {
|
|
122
|
+
const { mountSqlInspectorWidget } = await import("sql-dev-inspector/client")
|
|
123
|
+
mountSqlInspectorWidget({ baseURL: "/" }) // baseURL = path prefix of your server
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
If your frontend runs on a different origin than the API (e.g. Vite on :5173, API on :3000), open `http://localhost:3000/__sql-inspector/` directly, or proxy `/__sql-inspector` through the frontend dev server.
|
|
128
|
+
|
|
129
|
+
## Viewing the results
|
|
130
|
+
|
|
131
|
+
1. **Terminal** — each request that ran SQL prints a tree when its response finishes. Silence with `SQL_INSPECTOR_LOG=0`.
|
|
132
|
+
2. **Web page** `/__sql-inspector/` — live list of requests with search and *All / Issues / Errors* filters; click one for KPIs (total time, queries, SQL time and its share, transactions, subquery depth), insights, and a timeline of statements with a waterfall bar each. Click a statement for highlighted + formatted SQL, raw SQL, parameters and a copy button; click an insight to highlight the statements it is about; collapse a transaction. *Follow latest* jumps to each new request. Keyboard: `/` search, `↑` `↓` (or `j` `k`) previous / next request, `e` expand / collapse all. Language and theme (system / light / dark) are in the top bar.
|
|
133
|
+
3. **Floating button** — the number on it is the query count of the latest request (amber with warnings, red with errors).
|
|
134
|
+
|
|
135
|
+
| Action | How |
|
|
136
|
+
|---|---|
|
|
137
|
+
| Open / close the panel | Click the button, or `Alt+Shift+S`; `Esc` closes it while focus is inside |
|
|
138
|
+
| Hide / show the button | `Alt+Shift+D` (the panel shortcut keeps working while hidden) |
|
|
139
|
+
| Move the button | Drag it |
|
|
140
|
+
| Resize the panel | Drag its edge |
|
|
141
|
+
| Dock bottom / right | the two dock icons in the panel header |
|
|
142
|
+
| Auto-open on warnings or errors | Settings menu (off by default) |
|
|
143
|
+
| Open in a new tab · clear history | the ↗ icon in the header · "Clear history" in the settings menu |
|
|
144
|
+
| Turn it off completely | Settings menu → disable (`Alt+Shift+S` brings it back) |
|
|
145
|
+
|
|
146
|
+
Settings (including language) are stored in `localStorage`. The panel loads only the first time it is opened.
|
|
147
|
+
|
|
148
|
+
## Insights
|
|
149
|
+
|
|
150
|
+
| Insight | Triggered by | Level |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| N+1 | ≥ 3 SELECTs with the same shape (different values) in one request | warn |
|
|
153
|
+
| Duplicate | the exact same statement ≥ 2 times | warn |
|
|
154
|
+
| Slow | ≥ 100 ms (`SQL_INSPECTOR_SLOW_MS`) | warn |
|
|
155
|
+
| SQL error | the statement was rejected | error |
|
|
156
|
+
| Many queries | ≥ 30 statements in one request | warn |
|
|
157
|
+
| `SELECT *` · deep subquery (≥ 3 levels) | | info |
|
|
158
|
+
| Transaction convention *(opt-in via `SQL_INSPECTOR_TX_CONVENTION=1`)* | reads inside a transaction; several writes outside one | info |
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
| Variable | Default | Meaning |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `NODE_ENV` | — | `production` ⇒ **always off** (even with `SQL_INSPECTOR=1`); `development` ⇒ on |
|
|
166
|
+
| `SQL_INSPECTOR` | — | `1` forces on outside production (e.g. `test`); `0` forces off |
|
|
167
|
+
| `SQL_INSPECTOR_LANG` | `en` | Language of terminal output (`en`, `vi`, `ja`) and the web page's starting language. Only this variable counts — your machine's `LANG` is deliberately ignored |
|
|
168
|
+
| `SQL_INSPECTOR_LOG` | on | `0` disables the terminal output |
|
|
169
|
+
| `SQL_INSPECTOR_SLOW_MS` | `100` | slow-query threshold |
|
|
170
|
+
| `SQL_INSPECTOR_MAX_REQUESTS` | `200` | requests kept in memory |
|
|
171
|
+
| `SQL_INSPECTOR_MAX_QUERIES` | `1000` | statements kept per request |
|
|
172
|
+
| `SQL_INSPECTOR_TX_CONVENTION` | off | `1` enables the transaction-convention insights |
|
|
173
|
+
| `NO_COLOR` | — | disables terminal colors |
|
|
174
|
+
|
|
175
|
+
## API
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
createSqlInspector(): SqlInspector
|
|
179
|
+
|
|
180
|
+
interface SqlInspector {
|
|
181
|
+
enabled: boolean // false in production / when switched off: everything below is a no-op
|
|
182
|
+
middleware(req, res, next): void // Connect / Express
|
|
183
|
+
handle(req, res): boolean // true if the URL belongs to the inspector (already served)
|
|
184
|
+
track<T>(req, res, fn: () => T): T // run fn as one tracked request
|
|
185
|
+
task<T>(name: string, fn: () => T | Promise<T>): Promise<T> // a unit of work that is not HTTP
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Lower level, for custom integrations: `startInspector`, `getInspector`, `isInspectorEnabled`, `installTracer`, `runInRequest`, `TraceStore`, `buildReport`, `buildTree`, `analyzeSql`, `fingerprintOf`, `formatSql`, plus all types.
|
|
190
|
+
|
|
191
|
+
| Import | For |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `sql-dev-inspector` | main API above |
|
|
194
|
+
| `sql-dev-inspector/nuxt` | Nuxt module |
|
|
195
|
+
| `sql-dev-inspector/nitro` | Nitro plugin |
|
|
196
|
+
| `sql-dev-inspector/client` | `mountSqlInspectorWidget({ baseURL })` — floating button for any frontend |
|
|
197
|
+
|
|
198
|
+
HTTP routes, all under `/__sql-inspector/`: `GET /` (page), `GET /api/requests`, `GET /api/requests/:id`, `GET /api/stream` (SSE), `POST /api/clear`.
|
|
199
|
+
|
|
200
|
+
## How it works
|
|
201
|
+
|
|
202
|
+
Every statement — from `pool.query`, a transaction or an ORM — goes through `Connection.prototype.query/execute` in `mysql2`. The inspector patches exactly that point (once, reversibly) to record the SQL, parameters, duration, row count, error and **which connection ran it**. The connection is what lets it rebuild transaction and savepoint nesting, which neither an ORM logger nor mysql2's `diagnostics_channel` can do. Each adapter wraps your request handler in an `AsyncLocalStorage` scope; the pool's `getConnection` callback is bound to the caller's context so statements queued behind a busy pool are not attributed to the wrong request.
|
|
203
|
+
|
|
204
|
+
## Languages
|
|
205
|
+
|
|
206
|
+
English, Tiếng Việt (`vi`) and 日本語 (`ja`) are built in.
|
|
207
|
+
|
|
208
|
+
- **Web page**: first the saved choice, then `SQL_INSPECTOR_LANG` if set, then the browser language, then English. Change it any time from the top bar; `?lang=ja` in the URL overrides for that visit.
|
|
209
|
+
- **Floating button**: follows the browser language, or pick one in its settings menu (the panel reloads in that language).
|
|
210
|
+
- **Terminal and API `message` fields**: `SQL_INSPECTOR_LANG`, default English.
|
|
211
|
+
|
|
212
|
+
Insights are returned as data (`type` + `params`) next to the ready-made `message`, so any client can phrase them itself.
|
|
213
|
+
|
|
214
|
+
**Adding a language**: copy the `en` block in `src/i18n/messages.ts`, translate the values (keep every `{placeholder}`; add a `.one` variant where the singular differs, e.g. `"term.queries.one"`), add the code to the `Locale` type and to `LOCALES` in `src/i18n/index.ts`. `npm test` fails if a key or placeholder is missing. Pull requests welcome.
|
|
215
|
+
|
|
216
|
+
## Styling & isolation
|
|
217
|
+
|
|
218
|
+
The tool must never make your app look worse, so:
|
|
219
|
+
|
|
220
|
+
- **Floating button and panel** live in a **shadow root**, reset with `all: initial`, use a custom-element host (not a `<div>`) and zero-specificity resets. Your global CSS (`button { … }`, `* { … }`, `select`, `body > *`) cannot restyle them, and they add no CSS to your page. They are `position: fixed`, never shift your layout, and don't steal focus.
|
|
221
|
+
- **Inspector page** is its own document (inside an `<iframe>` when docked), so it cannot touch your page either. Every native control (buttons, switch, scrollbars) is restyled, and the language picker is a custom listbox — a native `<select>` popup is drawn by the OS and cannot be styled, so none is used anywhere (a test enforces it), with `color-scheme` declared so nothing falls back to a browser-default look in dark mode.
|
|
222
|
+
- **No external requests**: no web fonts, no CDN, no analytics — system font stacks only (Latin, Japanese and Vietnamese included).
|
|
223
|
+
- Respects `prefers-color-scheme`, `prefers-reduced-motion`, and keyboard focus (`:focus-visible`).
|
|
224
|
+
- If your app sets a strict Content-Security-Policy, the floating button needs `style-src 'unsafe-inline'` (it injects one `<style>` into its shadow root) and `frame-src 'self'`. This is a dev-only tool; relax the policy for development only.
|
|
225
|
+
|
|
226
|
+
## Security & privacy
|
|
227
|
+
|
|
228
|
+
- `NODE_ENV=production` is a hard stop: nothing is patched and the middleware only calls `next()`.
|
|
229
|
+
- The Nuxt module registers only in `nuxt dev`; `build` / `generate` contain nothing of this tool.
|
|
230
|
+
- Data lives in the memory of the dev process only: no files written, no network calls.
|
|
231
|
+
- ⚠️ **Displayed SQL contains real values**, possibly personal data from the database you point at. Don't share your screen or screenshots while connected to staging or production-like data.
|
|
232
|
+
- `/__sql-inspector/` has **no authentication** — it is meant for `localhost`. Don't expose your dev server to a shared network (e.g. `--host 0.0.0.0`) while it is on.
|
|
233
|
+
|
|
234
|
+
## Limitations
|
|
235
|
+
|
|
236
|
+
- **MySQL via `mysql2` only.** Not PostgreSQL, SQLite, or other MySQL drivers (e.g. `mariadb`). ORMs that don't use `mysql2` underneath (e.g. Prisma's own engine) bypass the patched point.
|
|
237
|
+
- **Tested with**: plain `mysql2` (pool, connection, manual transactions) and Drizzle ORM. Knex / Sequelize / TypeORM use `mysql2` and should work, but have no dedicated tests — please open an issue if they don't.
|
|
238
|
+
- The Express/Connect adapter is tested with a faithful fake `req`/`res`, not with `express` itself; the floating button outside Nuxt is tested in happy-dom only.
|
|
239
|
+
- **No "caller" line**: ORMs like Drizzle return lazy thenables, so by the time the query runs the call stack no longer contains your code.
|
|
240
|
+
- A lazy builder created inside a request scope but awaited outside it is not attributed to that request.
|
|
241
|
+
- Statements outside any request or task are grouped under one "(outside request)" entry.
|
|
242
|
+
- Requests that run no SQL (assets, HMR) are not kept.
|
|
243
|
+
|
|
244
|
+
## Troubleshooting
|
|
245
|
+
|
|
246
|
+
| Symptom | Likely cause |
|
|
247
|
+
|---|---|
|
|
248
|
+
| Nothing shows up | `NODE_ENV` isn't `development`; run with `NODE_ENV=development` or `SQL_INSPECTOR=1` (and never `production`) |
|
|
249
|
+
| Terminal output but `/__sql-inspector/` is 404 | Express: mount the middleware before your router. Nuxt/Nitro: check `app.baseURL` |
|
|
250
|
+
| All queries under "(outside request)" | middleware mounted after the route that ran SQL, or SQL ran outside an awaited handler (timers, queues) — wrap it in `sqlInspector.task()` |
|
|
251
|
+
| Floating button missing | Non-Nuxt: you haven't called `mountSqlInspectorWidget`. Nuxt: it exists in `nuxt dev` only. Try `Alt+Shift+D`, or clear the `sqlDevInspector.widget` key in `localStorage` |
|
|
252
|
+
| An ORM's queries are missing | It doesn't use `mysql2` underneath |
|
|
253
|
+
| Two copies of `mysql2` in `node_modules` | The inspector patches the copy it can import; make sure app and inspector share one (hoisted peer) |
|
|
254
|
+
|
|
255
|
+
## Development
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npm install
|
|
259
|
+
npm run typecheck
|
|
260
|
+
npm test # 131 tests: real mysql2 + Drizzle against an in-memory MySQL protocol server
|
|
261
|
+
npm run build
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Tests need no MySQL installation and open no ports: `test/mock-mysql-server.ts` speaks the real wire protocol over in-memory streams.
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
+
import { mountSqlInspectorWidget } from "./widget.js";
|
|
3
|
+
// Dev only, twice over: the module registers this plugin in `nuxt dev` only, and the plugin
|
|
4
|
+
// itself does nothing outside dev.
|
|
5
|
+
export default defineNuxtPlugin(() => {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
if (!import.meta.dev) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
mountSqlInspectorWidget({ baseURL: config.app.baseURL });
|
|
11
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Floating launcher for the SQL Inspector page, mounted in the host application during dev.
|
|
3
|
+
*
|
|
4
|
+
* It lives inside a shadow root and resets everything it uses, so it neither looks like nor
|
|
5
|
+
* is affected by the host's CSS (no inherited fonts, no styled `button` / `select` / `a`
|
|
6
|
+
* leaking in or out). No framework and no Nuxt imports: it must work in any frontend.
|
|
7
|
+
*/
|
|
8
|
+
import { type Locale } from "../i18n/index.js";
|
|
9
|
+
export interface WidgetOptions {
|
|
10
|
+
/** Path prefix under which the server serves the inspector, e.g. `/` or `/app` (Nuxt `app.baseURL`). */
|
|
11
|
+
baseURL: string;
|
|
12
|
+
win?: Window & typeof globalThis;
|
|
13
|
+
}
|
|
14
|
+
export interface WidgetSettings {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
buttonVisible: boolean;
|
|
17
|
+
open: boolean;
|
|
18
|
+
dock: "bottom" | "right";
|
|
19
|
+
/** Panel height (dock bottom) or width (dock right), px. */
|
|
20
|
+
size: number;
|
|
21
|
+
/** Open the panel by itself when a request has a warning or an error. */
|
|
22
|
+
autoOpen: boolean;
|
|
23
|
+
/** Distance of the button from the bottom-right corner, px. */
|
|
24
|
+
offset: {
|
|
25
|
+
right: number;
|
|
26
|
+
bottom: number;
|
|
27
|
+
};
|
|
28
|
+
/** `auto` follows the browser language. */
|
|
29
|
+
lang: "auto" | Locale;
|
|
30
|
+
}
|
|
31
|
+
export declare const STORAGE_KEY = "sqlDevInspector.widget";
|
|
32
|
+
export declare const HOTKEYS: {
|
|
33
|
+
readonly togglePanel: "Alt+Shift+S";
|
|
34
|
+
readonly toggleButton: "Alt+Shift+D";
|
|
35
|
+
};
|
|
36
|
+
export interface WidgetHandle {
|
|
37
|
+
settings: () => WidgetSettings;
|
|
38
|
+
destroy: () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function mountSqlInspectorWidget(options: WidgetOptions): WidgetHandle;
|