@carllee1983/dbcli 1.9.1 → 1.10.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 +46 -0
- package/assets/SKILL.md +14 -0
- package/assets/reference.md +62 -2
- package/assets/snippets/diag/es-cluster-health.elasticsearch.sql +15 -0
- package/assets/snippets/diag/redis-key-stats.redis.sql +11 -0
- package/dist/cli.mjs +62310 -60864
- package/package.json +5 -9
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,52 @@ All notable changes to dbcli are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.10.1] - 2026-05-08
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Packaged `dist/cli.mjs` 找不到 assets**:1.10.0 bundle 在 `task-paths.ts` / `snippet-paths.ts` 用 `import.meta.dir + ../../../` 解析 builtin 目錄,bundle 後三層往上會跳出 package root,npm 全域安裝的使用者執行 `dbcli queries list` / `dbcli skill tasks list` 讀不到資源。抽出 `src/utils/package-root.ts` 以 `package.json` 走訪定位 root,dev 與 bundle 都正確;`skill.ts` 內既有的 `findPackageRoot` 也收斂到同一處。
|
|
13
|
+
- **`dbcli q` 略過 blacklist 檢查(安全)**:`q.ts` 把空字串當作 `tableName` 傳給 `BlacklistValidator.filterColumns`,column-level redaction 永遠不命中;同時也沒呼叫 `checkTableBlacklist`,使用者可以透過 saved snippet 直接 SELECT 黑名單表/欄位繞開保護。改為從 `prepared.rewrittenSql` 抽出主表(SQL)或 `prepared.execHints.index`(ES),執行前先 `checkTableBlacklist('SELECT', target)`,並把真正的 `tableName` 餵給 `filterColumns`;Redis 維持原樣。
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **dist/ 整合 smoke 測試**:`tests/integration/dist-smoke.test.ts` 從 OS tmpdir 執行 `dist/cli.mjs`,覆蓋 `--version`、`skill --output`、`queries list`、`skill tasks list`,守住 packaged assets path 不再回退。
|
|
18
|
+
- **`q` blacklist 迴歸測試**:`tests/unit/commands/q-blacklist.test.ts` 覆蓋黑名單表阻擋、欄位 redact、未受影響 snippet 三種情境。
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **Lint release-blocking**:`bun run lint` / `lint:fix` 加上 `--max-warnings=0`;同時清掉 45 個 `@typescript-eslint/no-explicit-any` warnings(以正型替代為主,`elasticsearch-adapter.ts` 因刻意不引入 `@elastic/elasticsearch` SDK 而以檔案層 `eslint-disable` 標註理由)。任何新 warning 從此會擋住 release。
|
|
23
|
+
|
|
24
|
+
## [1.10.0] - 2026-05-08
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **Saved Queries 擴展至 Elasticsearch 與 Redis**:`dbcli q @<name>` 與 `queries` 子命令現在能依 frontmatter `engine` 自動切換到對應引擎,並走各引擎專屬的安全管線。
|
|
29
|
+
- **Engine strategy 重構**:runner 透過 `EngineStrategy` 介面分派到 SQL / Elasticsearch / Redis 三個獨立 strategy;既有 SQL 行為以 strategy 形式保留,無行為變更。
|
|
30
|
+
- **Elasticsearch strategy**:
|
|
31
|
+
- Frontmatter 接受 `engine: elasticsearch` 與 `index` 欄位;body 必須是合法 JSON,含 `script` 欄位的 query 直接拒絕。
|
|
32
|
+
- JSON-aware 參數注入:`:name` 僅在 JSON 字串脈絡裡替換,避免破壞語法。
|
|
33
|
+
- Size guard:自動補 `size` 上限;`aggs` 模式下放行但加註警告,分頁 (`from + size`) 過大時提示。
|
|
34
|
+
- **Redis strategy**:
|
|
35
|
+
- 命令白名單(read-only 為主)+ body validation;直接拒絕 unsupported 或寫入命令。
|
|
36
|
+
- Raw 參數注入:`:name` 直接代入字面量並打印 foot-gun 警告,提醒使用者 saved query 內不可放使用者輸入。
|
|
37
|
+
- Size guard:對 range / SCAN 命令的 `COUNT` / `LIMIT` 加上保險上限。
|
|
38
|
+
- **`q` 命令分派**:根據 prepared execution 的 engine family 呼叫對應 adapter,`--dry-run` 依 engine 用對應格式輸出(SQL 維持 SQL、ES 印 JSON body、Redis 印 argv)。
|
|
39
|
+
- **內建診斷 snippet**:
|
|
40
|
+
- `assets/snippets/diag/es-cluster-health.elasticsearch.sql` — ES 叢集健康度摘要。
|
|
41
|
+
- `assets/snippets/diag/redis-key-stats.redis.sql` — Redis key 數量 / type 分佈快照。
|
|
42
|
+
- **整合測試**:新增 ES / Redis end-to-end saved query 測試(依本機是否有 Docker 而 skip,與既有 PG / MySQL 測試一致)。
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **Redis 驅動**:改用 Bun 內建 `RedisClient`,移除外部 `ioredis` 依賴。
|
|
47
|
+
- **Elasticsearch adapter**:refactor 並收斂錯誤訊息與 ExecutionResult 形狀,與 SQL / Mongo / Redis 對齊。
|
|
48
|
+
- **文件**:`assets/SKILL.md` 與 `assets/reference.md` 補上 ES / Redis snippet 工作流;`docs/feature-matrix.md` 更新 saved-queries 欄位。
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **`dbcli export`(Redis 分支)**:`result.rowCount` 在 Redis 上可能 undefined 時導致 `tsc --noEmit` 報 TS2322;改為 `result.rowCount ?? result.rows.length ?? 0`,release gate 中的 typecheck 回到 0 錯誤。
|
|
53
|
+
|
|
8
54
|
## [1.9.1] - 2026-05-07
|
|
9
55
|
|
|
10
56
|
### Changed
|
package/assets/SKILL.md
CHANGED
|
@@ -237,6 +237,18 @@ Each `.sql` file may declare YAML frontmatter inside `-- ---` blocks
|
|
|
237
237
|
(name, description, engine, params, tags). See `dbcli queries show @<name> --format json`
|
|
238
238
|
for the machine-readable contract.
|
|
239
239
|
|
|
240
|
+
### Engine-specific bodies
|
|
241
|
+
|
|
242
|
+
Each snippet's body format is determined by the `engine` frontmatter field:
|
|
243
|
+
|
|
244
|
+
| Engine | Body format | Notes |
|
|
245
|
+
|-------------------|------------------------|-------|
|
|
246
|
+
| postgres / mysql | Single SELECT or WITH | `:name` → driver bind (`$1` / `?`) |
|
|
247
|
+
| elasticsearch | JSON DSL | `:name` → JSON-aware substitution; `index:` field required |
|
|
248
|
+
| redis | Single Redis command | `:name` → raw text; only read commands allowed |
|
|
249
|
+
|
|
250
|
+
Mixed-family `engine` arrays (e.g. `[postgres, elasticsearch]`) are rejected at parse time.
|
|
251
|
+
|
|
240
252
|
### Built-in diagnostic snippets
|
|
241
253
|
|
|
242
254
|
dbcli ships ready-made diagnostic queries. Run with `dbcli q @diag/<topic>`:
|
|
@@ -251,6 +263,8 @@ dbcli ships ready-made diagnostic queries. Run with `dbcli q @diag/<topic>`:
|
|
|
251
263
|
| `@diag/locks` | lock-wait chains |
|
|
252
264
|
| `@diag/db-size` | database size summary |
|
|
253
265
|
| `@diag/cache-hit` | buffer cache hit ratios |
|
|
266
|
+
| `@diag/es-cluster-health` | document counts per index (ES connections) |
|
|
267
|
+
| `@diag/redis-key-stats` | sample SCAN over keyspace (Redis connections) |
|
|
254
268
|
|
|
255
269
|
Engine variants are picked automatically based on the active connection.
|
|
256
270
|
Override any of them by placing a same-named file under `.dbcli-shared/queries/`
|
package/assets/reference.md
CHANGED
|
@@ -230,6 +230,62 @@ Param placeholders use `:name`. They are rewritten to `$1, $2, …` (Postgres) o
|
|
|
230
230
|
- Files exceeding 64 KiB are rejected.
|
|
231
231
|
- `--no-limit` is honoured only at the outermost level; nested subqueries are still wrapped by the size guard.
|
|
232
232
|
|
|
233
|
+
##### Elasticsearch snippets
|
|
234
|
+
|
|
235
|
+
Body is a JSON DSL `_search` request body. Frontmatter requires an `index:` field (may contain `:param`).
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
|
|
239
|
+
-- ---
|
|
240
|
+
-- name: events-by-day
|
|
241
|
+
-- engine: elasticsearch
|
|
242
|
+
-- index: 'events-:date'
|
|
243
|
+
-- params:
|
|
244
|
+
-- date: { type: date, required: true }
|
|
245
|
+
-- user_id: { type: int, required: true }
|
|
246
|
+
-- ---
|
|
247
|
+
{
|
|
248
|
+
"query": {
|
|
249
|
+
"bool": {
|
|
250
|
+
"filter": [
|
|
251
|
+
{ "term": { "user_id": :user_id } }
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
"size": 100
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
Substitution rules (type-aware JSON injection):
|
|
259
|
+
|
|
260
|
+
- `int` / `float` / `bool` outside string literals → bare value (`42`, `1.5`, `true`)
|
|
261
|
+
- `string` / `date` / `datetime` outside string literals → JSON-quoted (`"Alice"`, `"2026-05-08"`)
|
|
262
|
+
- Any param inside a JSON string literal → escaped inner form (`"prefix-:name"` works)
|
|
263
|
+
|
|
264
|
+
`script` and `script_fields` are rejected anywhere in the body.
|
|
265
|
+
|
|
266
|
+
Size guard: if `size` is missing, `1000` is injected (or `0` when `aggs` is present); explicit `size > 1000` is overridden with a warning unless `--no-limit`.
|
|
267
|
+
|
|
268
|
+
##### Redis snippets
|
|
269
|
+
|
|
270
|
+
Body is a single Redis command on one line. Only read-only commands are allowed:
|
|
271
|
+
`GET MGET HGET HGETALL HMGET HKEYS HVALS HLEN HEXISTS LRANGE LLEN LINDEX SMEMBERS SISMEMBER SCARD ZRANGE ZRANGEBYSCORE ZRANGEBYLEX ZSCORE ZCARD ZCOUNT ZRANK TYPE EXISTS TTL PTTL STRLEN OBJECT SCAN HSCAN SSCAN ZSCAN`.
|
|
272
|
+
|
|
273
|
+
`KEYS`, `EVAL`, `FLUSHDB`, `FLUSHALL`, `CONFIG`, `DEBUG`, `SHUTDOWN`, `SCRIPT` and any write command are rejected.
|
|
274
|
+
|
|
275
|
+
Example:
|
|
276
|
+
|
|
277
|
+
-- ---
|
|
278
|
+
-- name: cache-user
|
|
279
|
+
-- engine: redis
|
|
280
|
+
-- params:
|
|
281
|
+
-- id: { type: int, required: true }
|
|
282
|
+
-- ---
|
|
283
|
+
HGETALL user::id
|
|
284
|
+
|
|
285
|
+
Substitution rules: pure raw text — `:name` becomes the value's `String()` form. **Quoting is the snippet author's responsibility**: wrap `:name` in double quotes if the value may contain whitespace. The parser warns when a `string`-typed `:name` is adjacent to non-whitespace and unquoted.
|
|
286
|
+
|
|
287
|
+
Size guard: `LRANGE` / `ZRANGE` stop overridden when `< 0` or `> 1000`; `SCAN` / `HSCAN` / `SSCAN` / `ZSCAN` get `COUNT 1000` injected if absent. `--no-limit` disables.
|
|
288
|
+
|
|
233
289
|
### queries
|
|
234
290
|
|
|
235
291
|
Manage saved snippets — discover, inspect, scaffold, and edit local copies. Mutating
|
|
@@ -582,7 +638,9 @@ Redis connections speak Redis commands rather than SQL. The adapter uses the `io
|
|
|
582
638
|
|
|
583
639
|
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `status`, `doctor`, `upgrade`, `completion`
|
|
584
640
|
|
|
585
|
-
**
|
|
641
|
+
**Saved queries:** `q` is supported for read-only Redis commands (see "Redis snippets" below).
|
|
642
|
+
|
|
643
|
+
**Not supported (exit with error or unsupported error):** `insert`, `update`, `delete`, `export`, `check`, `diff`, `migrate`, `shell`. For writes, run the equivalent Redis command via `query` — the same permission gate applies.
|
|
586
644
|
|
|
587
645
|
### Connection and configuration
|
|
588
646
|
|
|
@@ -648,7 +706,9 @@ Elasticsearch connections speak the REST API. The adapter is fetch-based (no SDK
|
|
|
648
706
|
|
|
649
707
|
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `status`, `doctor`, `upgrade`, `completion`
|
|
650
708
|
|
|
651
|
-
**
|
|
709
|
+
**Saved queries:** `q` is supported for ES JSON DSL bodies (see "Elasticsearch snippets" below).
|
|
710
|
+
|
|
711
|
+
**Not supported (use external tooling):** `insert`, `update`, `delete`, `export`, `check`, `diff`, `migrate`, `shell`. The permission classifier already understands `_doc` / `_update` / `_bulk` so future write surfaces can be wired in without changing tiers.
|
|
652
712
|
|
|
653
713
|
### Connection and configuration
|
|
654
714
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: es-cluster-health
|
|
3
|
+
-- description: Document counts per index across the cluster
|
|
4
|
+
-- engine: elasticsearch
|
|
5
|
+
-- index: '*'
|
|
6
|
+
-- tags: [diag, elasticsearch]
|
|
7
|
+
-- ---
|
|
8
|
+
{
|
|
9
|
+
"size": 0,
|
|
10
|
+
"aggs": {
|
|
11
|
+
"by_index": {
|
|
12
|
+
"terms": { "field": "_index", "size": 50 }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|