cgraphx 1.3.0 → 1.4.0
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/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +5 -4
- package/dist/.claude-template/skills/db-query/SKILL.md +75 -42
- package/dist/.claude-template/skills/db-query/agent-prompt.md +12 -13
- package/dist/.claude-template/skills/export-table-ddl/SKILL.md +167 -0
- package/dist/.claude-template/skills/export-table-ddl/closed-loop.html +163 -0
- package/dist/.claude-template/skills/export-table-ddl/evals/evals.json +26 -0
- package/dist/.claude-template/skills/export-table-ddl/scripts/export-ddl.mjs +163 -0
- package/dist/.claude-template/skills/run-api-test/SKILL.md +5 -5
- package/dist/.claude-template/skills/run-api-test/references/test-scope.md +1 -1
- package/dist/.claude-template/skills/write-api/SKILL.md +10 -10
- package/dist/.claude-template/skills/write-api/assets/template-api-spec.md +6 -6
- package/dist/.claude-template/skills/write-api/assets/template-request.bru +1 -4
- package/dist/.claude-template/skills/write-api/references/ai-prompts.md +1 -1
- package/dist/.claude-template/skills/write-api/references/api-spec-format.md +2 -2
- package/dist/.claude-template/skills/write-api/references/bru-format.md +6 -11
- package/dist/.claude-template/skills/write-api/references/collection-layout.md +24 -15
- package/dist/dbquery/cli.d.ts.map +1 -1
- package/dist/dbquery/cli.js +20 -3
- package/dist/dbquery/cli.js.map +1 -1
- package/dist/dbquery/executor.d.ts.map +1 -1
- package/dist/dbquery/executor.js +15 -23
- package/dist/dbquery/executor.js.map +1 -1
- package/dist/dbquery/format.d.ts +15 -1
- package/dist/dbquery/format.d.ts.map +1 -1
- package/dist/dbquery/format.js +86 -23
- package/dist/dbquery/format.js.map +1 -1
- package/dist/dbquery/identifiers.d.ts +19 -0
- package/dist/dbquery/identifiers.d.ts.map +1 -0
- package/dist/dbquery/identifiers.js +37 -0
- package/dist/dbquery/identifiers.js.map +1 -0
- package/dist/dbquery/index.d.ts +1 -1
- package/dist/dbquery/index.d.ts.map +1 -1
- package/dist/dbquery/index.js.map +1 -1
- package/dist/dbquery/queries.d.ts +23 -3
- package/dist/dbquery/queries.d.ts.map +1 -1
- package/dist/dbquery/queries.js +46 -27
- package/dist/dbquery/queries.js.map +1 -1
- package/dist/dbquery/types.d.ts +2 -15
- package/dist/dbquery/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* export-ddl.mjs — 批量导出项目表的 DDL 到可检索目录。
|
|
4
|
+
*
|
|
5
|
+
* 流程:
|
|
6
|
+
* 1. `cgraphx db schema --mode=tables --profile=P` 拿全表 + 表注释(1 次调用)
|
|
7
|
+
* 2. 过滤到请求的表清单(--tables all 时跳过过滤)
|
|
8
|
+
* 3. 对每张表 `cgraphx db schema --mode=ddl --table=X --profile=P` 拿 DDL 文本(N 次调用)
|
|
9
|
+
* 4. 写 <out>/<project>/INDEX.md(表名 + 注释 + DDL 链接)+ <out>/<project>/ddl/<table>.sql
|
|
10
|
+
*
|
|
11
|
+
* 用法:
|
|
12
|
+
* node export-ddl.mjs \
|
|
13
|
+
* --profile zqassis \
|
|
14
|
+
* --tables ./tables.txt \ # 或 --tables all(全库导出)
|
|
15
|
+
* --out ./schema-dump \
|
|
16
|
+
* --project customer-svc
|
|
17
|
+
*
|
|
18
|
+
* tables 文件格式:每行一个表名,# 开头注释行忽略;或 JSON 数组 ["t1","t2"]。
|
|
19
|
+
*/
|
|
20
|
+
import { execFileSync } from 'node:child_process';
|
|
21
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
22
|
+
import { resolve, join } from 'node:path';
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const args = {};
|
|
26
|
+
for (let i = 0; i < argv.length; i++) {
|
|
27
|
+
const k = argv[i];
|
|
28
|
+
if (k.startsWith('--')) {
|
|
29
|
+
const v = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : 'true';
|
|
30
|
+
args[k.slice(2)] = v;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return args;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readTableList(spec) {
|
|
37
|
+
if (spec === 'all') return null; // null = 不过滤
|
|
38
|
+
const raw = readFileSync(spec, 'utf8');
|
|
39
|
+
const trimmed = raw.trim();
|
|
40
|
+
if (trimmed.startsWith('[')) {
|
|
41
|
+
return JSON.parse(trimmed);
|
|
42
|
+
}
|
|
43
|
+
return trimmed
|
|
44
|
+
.split('\n')
|
|
45
|
+
.map(l => l.trim())
|
|
46
|
+
.filter(l => l && !l.startsWith('#'));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function runCgraphx(cmdArgs, { expectJson = false } = {}) {
|
|
50
|
+
const env = { ...process.env, CGRAPHX_WASM_RELAUNCHED: '1' };
|
|
51
|
+
const bin = process.env.CGRAPHX_BIN || 'cgraphx';
|
|
52
|
+
const out = execFileSync(bin, ['db', ...cmdArgs], {
|
|
53
|
+
encoding: 'utf8',
|
|
54
|
+
env,
|
|
55
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
56
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
57
|
+
});
|
|
58
|
+
if (expectJson) return JSON.parse(out);
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 提取 db-query 探索补充的 DISCOVERED 块(含起止标记行),供重 dump 时保留。
|
|
64
|
+
* 块格式见 db-query/SKILL.md。无块返回 null。
|
|
65
|
+
*/
|
|
66
|
+
function extractDiscoveredBlock(content) {
|
|
67
|
+
const start = content.indexOf('-- === DISCOVERED');
|
|
68
|
+
if (start === -1) return null;
|
|
69
|
+
const endMarker = '-- === END DISCOVERED ===';
|
|
70
|
+
const end = content.indexOf(endMarker, start);
|
|
71
|
+
if (end === -1) return content.slice(start).replace(/\n*$/, '\n');
|
|
72
|
+
return content.slice(start, end + endMarker.length).replace(/\n*$/, '\n');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function main() {
|
|
76
|
+
const args = parseArgs(process.argv.slice(2));
|
|
77
|
+
const profile = args.profile;
|
|
78
|
+
const tablesSpec = args.tables;
|
|
79
|
+
const outDir = args.out;
|
|
80
|
+
const project = args.project;
|
|
81
|
+
if (!profile || !tablesSpec || !outDir || !project) {
|
|
82
|
+
process.stderr.write(
|
|
83
|
+
'Usage: export-ddl.mjs --profile <name> --tables <file|all> --out <dir> --project <name>\n',
|
|
84
|
+
);
|
|
85
|
+
process.exit(5);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 1. 拿全表 + 注释
|
|
89
|
+
process.stderr.write(`[export-ddl] 拉取 ${profile} 的表清单...\n`);
|
|
90
|
+
const tablesResult = runCgraphx(
|
|
91
|
+
['schema', '--mode=tables', `--profile=${profile}`, '--format=json'],
|
|
92
|
+
{ expectJson: true },
|
|
93
|
+
);
|
|
94
|
+
const allTables = new Map(tablesResult.tables.map(t => [t.name, t.comment ?? '']));
|
|
95
|
+
|
|
96
|
+
// 2. 过滤
|
|
97
|
+
const requested = readTableList(tablesSpec);
|
|
98
|
+
const targets = requested === null
|
|
99
|
+
? [...allTables.keys()]
|
|
100
|
+
: requested.filter(Boolean);
|
|
101
|
+
process.stderr.write(`[export-ddl] 共 ${targets.length} 张表待导出。\n`);
|
|
102
|
+
|
|
103
|
+
// 3. 逐表拉 DDL
|
|
104
|
+
const projectDir = resolve(outDir, project);
|
|
105
|
+
const ddlDir = join(projectDir, 'ddl');
|
|
106
|
+
mkdirSync(ddlDir, { recursive: true });
|
|
107
|
+
|
|
108
|
+
const indexRows = [];
|
|
109
|
+
let ok = 0, missing = 0, failed = 0, preserved = 0;
|
|
110
|
+
for (const table of targets) {
|
|
111
|
+
const comment = allTables.get(table);
|
|
112
|
+
if (comment === undefined) {
|
|
113
|
+
indexRows.push({ table, comment: '(表中不存在)', status: 'missing' });
|
|
114
|
+
missing++;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const ddl = runCgraphx(
|
|
119
|
+
['schema', '--mode=ddl', `--table=${table}`, `--profile=${profile}`],
|
|
120
|
+
);
|
|
121
|
+
const filePath = join(ddlDir, `${table}.sql`);
|
|
122
|
+
// 重 dump 时保留 db-query 探索补充的 -- === DISCOVERED === 块
|
|
123
|
+
let out = ddl;
|
|
124
|
+
if (existsSync(filePath)) {
|
|
125
|
+
const old = readFileSync(filePath, 'utf8');
|
|
126
|
+
const discovered = extractDiscoveredBlock(old);
|
|
127
|
+
if (discovered) {
|
|
128
|
+
out = ddl.replace(/\n*$/, '\n') + '\n' + discovered;
|
|
129
|
+
preserved++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
writeFileSync(filePath, out);
|
|
133
|
+
indexRows.push({ table, comment, status: 'ok' });
|
|
134
|
+
ok++;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
137
|
+
process.stderr.write(`[export-ddl] ${table} 失败: ${msg}\n`);
|
|
138
|
+
indexRows.push({ table, comment, status: `failed: ${msg.split('\n')[0]}` });
|
|
139
|
+
failed++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 4. 写 INDEX.md
|
|
144
|
+
const lines = [
|
|
145
|
+
`# ${project} 表清单`,
|
|
146
|
+
'',
|
|
147
|
+
`共 ${targets.length} 张表(成功 ${ok} / 缺失 ${missing} / 失败 ${failed}),DDL 见 \`ddl/\` 目录。${preserved > 0 ? ` 保留 ${preserved} 张表的 DISCOVERED 补充。` : ''}`,
|
|
148
|
+
'',
|
|
149
|
+
'| 表名 | 注释 | 状态 |',
|
|
150
|
+
'|---|---|---|',
|
|
151
|
+
];
|
|
152
|
+
for (const r of indexRows) {
|
|
153
|
+
const status = r.status === 'ok' ? '✓' : r.status;
|
|
154
|
+
lines.push(`| \`${r.table}\` | ${r.comment || ''} | ${status} |`);
|
|
155
|
+
}
|
|
156
|
+
writeFileSync(join(projectDir, 'INDEX.md'), lines.join('\n') + '\n');
|
|
157
|
+
|
|
158
|
+
process.stderr.write(
|
|
159
|
+
`[export-ddl] 完成 → ${projectDir}/ (成功 ${ok} / 缺失 ${missing} / 失败 ${failed})\n`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
main();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-api-test
|
|
3
|
-
description: 在 write-api 生成 .bru 后,跑接口测试 + 核实写接口 DB 副作用 + 出报告。触发时由用户指定需求或接口:指定需求→跑该需求所有接口所有场景,报告归该需求目录;只指定接口→主动问测哪个需求(AI
|
|
3
|
+
description: 在 write-api 生成 .bru 后,跑接口测试 + 核实写接口 DB 副作用 + 出报告。触发时由用户指定需求或接口:指定需求→跑该需求所有接口所有场景,报告归该需求目录;只指定接口→主动问测哪个需求(AI 扫 docs/bruno/ 下哪些需求测过该接口,让用户选);都没指定→主动问。服务未就绪暂停等待;写接口必须查 DB 验数据(HTTP 200 ≠ 通过);失败只报告不修代码不重跑,交用户决定。不调 write-api / write-api-doc,不改 .bru。Use when 用户要求跑接口测试 / 验证接口 / 测一下需求 X 的接口 / 跑下这个接口;write-api 跑完后用户要求执行测试;代码改完后用户想验证修复。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Run API Test
|
|
@@ -45,7 +45,7 @@ description: 在 write-api 生成 .bru 后,跑接口测试 + 核实写接口 DB
|
|
|
45
45
|
|
|
46
46
|
## 依赖
|
|
47
47
|
|
|
48
|
-
- `.bru` 文件已存在(由 `/write-api` 生成,在 `docs/bruno
|
|
48
|
+
- `.bru` 文件已存在(由 `/write-api` 生成,在 `docs/bruno/<服务>/<需求前缀>/<接口名>/<场景>.bru` 或 `docs/bruno/common/<接口名>/<场景>.bru`)
|
|
49
49
|
- `docs/bruno/environments/local.bru` 存在(不存在时报错退出,提示先跑 `/write-api`)
|
|
50
50
|
- 本地服务可启动(若需手动启动,R2 暂停等待)
|
|
51
51
|
- 项目已配置 db-query profile(R4 核实写接口副作用用)—— 未配置则 R4 降级,见 `references/db-verification.md`
|
|
@@ -79,7 +79,7 @@ description: 在 write-api 生成 .bru 后,跑接口测试 + 核实写接口 DB
|
|
|
79
79
|
│
|
|
80
80
|
├─ 只指定了接口(接口名/路径)
|
|
81
81
|
│ → skill 主动问"测哪个需求?"
|
|
82
|
-
│ → AI
|
|
82
|
+
│ → AI 扫 docs/bruno/<服务>*/<需求前缀>/<接口名>/ 哪些存在,列出测过该接口的需求
|
|
83
83
|
│ → 列出来让用户选(若 0 个需求,提示用户先跑 /write-api)
|
|
84
84
|
│ → 选定后,AI 查该需求 api-spec.md 拿该接口的场景
|
|
85
85
|
│ → 提示用户确认后进入 R1
|
|
@@ -91,7 +91,7 @@ description: 在 write-api 生成 .bru 后,跑接口测试 + 核实写接口 DB
|
|
|
91
91
|
|
|
92
92
|
**关键约束**:
|
|
93
93
|
- 不管走哪条分支,最终都要锁定一个 feature-id,**报告归该 feature 目录**
|
|
94
|
-
- AI
|
|
94
|
+
- AI 扫目录反查指定接口关联哪些需求时,读 `docs/bruno/<服务>*/<需求前缀>/<接口名>/` 哪些存在(无需读 .bru docs 段,目录层级已表达归属)
|
|
95
95
|
- 用户确认环节不能省 —— AI 理解的范围必须显示给用户,避免跑错需求或接口
|
|
96
96
|
|
|
97
97
|
## 工作流
|
|
@@ -148,7 +148,7 @@ description: 在 write-api 生成 .bru 后,跑接口测试 + 核实写接口 DB
|
|
|
148
148
|
|---|---|
|
|
149
149
|
| 用户指定需求但 api-spec 不存在 | 报"需求 X 无测试规格,请先跑 /write-api"并退出 |
|
|
150
150
|
| 用户指定接口但 .bru 不存在 | 报"接口 Y 无 .bru,请先跑 /write-api"并退出 |
|
|
151
|
-
|
|
|
151
|
+
| 用户指定接口但扫目录发现 0 个需求测过 | 提示"未在 docs/bruno/ 下找到测过接口 Y 的需求,请先跑 /write-api" |
|
|
152
152
|
| api-spec §5 列出的 .bru 实际不存在 | 报"需求 X 接口 Y 场景 Z 的 .bru 缺失,请先跑 /write-api"并退出 |
|
|
153
153
|
| 本地服务未启动 | 暂停 + 提示用户启动,等待回车继续 |
|
|
154
154
|
| env 文件缺失 | 报"环境缺失,请先跑 /write-api"并退出,不自动生成 |
|
|
@@ -59,6 +59,6 @@ docs/bruno/common/login/正常.bru
|
|
|
59
59
|
|---|---|
|
|
60
60
|
| 用户指定需求但 api-spec.md 不存在 | 报"需求 X 无测试规格,请先跑 /write-api"并退出 |
|
|
61
61
|
| 用户指定接口但接口目录不存在 | 报"接口 Y 无 .bru,请先跑 /write-api"并退出 |
|
|
62
|
-
|
|
|
62
|
+
| 用户指定接口但扫目录发现 0 个需求测过 | 提示"未在 docs/bruno/ 下找到测过接口 Y 的需求,请先跑 /write-api" |
|
|
63
63
|
| api-spec §5 列出的 .bru 实际不存在 | 报"需求 X 接口 Y 场景 Z 的 .bru 缺失,请先跑 /write-api"并退出 |
|
|
64
64
|
| 用户确认环节用户取消 | 退出,不进入 R2 |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: write-api
|
|
3
|
-
description: 基于需求生成 bruno 测试接口(.bru + 测试规格 + 环境配置)。流程:识别接口范围(用户显式给 / spec 接口章节 / AI 读 spec 理解)→ 生成测试规格文档 → 单次 AI 调用生成 .bru(含真实参数 + 内嵌断言
|
|
3
|
+
description: 基于需求生成 bruno 测试接口(.bru + 测试规格 + 环境配置)。流程:识别接口范围(用户显式给 / spec 接口章节 / AI 读 spec 理解)→ 生成测试规格文档 → 单次 AI 调用生成 .bru(含真实参数 + 内嵌断言)→ 首次接入时生成 bruno.json + environments/*.bru 骨架。产物:测试规格随 feature 走;.bru collection 按 <服务>/<需求前缀>/<接口名>/<场景> 组织在 docs/bruno/(按需求隔离,跨需求不覆盖);environments 统一管理。本 skill 不跑 bru run、不核实 DB、不出报告——那是 /run-api-test 的职责。不调 write-api-doc、不自动修代码、不引入程序化配置文件(微服务/通用接口识别由 AI 判断)。Use when 用户要求生成测试接口 / 写接口测试 / 准备 bruno 测试 / 帮需求补接口 / 免去手动录接口。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Write API
|
|
@@ -58,7 +58,7 @@ description: 基于需求生成 bruno 测试接口(.bru + 测试规格 + 环境
|
|
|
58
58
|
|
|
59
59
|
产物文件名:
|
|
60
60
|
- 测试规格:`docs/features/<feature-id>/<文件前缀>-api-spec.md`
|
|
61
|
-
- .bru
|
|
61
|
+
- .bru:`docs/bruno/<服务>/<文件前缀>/<接口名>/<场景>.bru`(按需求隔离,见 `references/collection-layout.md`)
|
|
62
62
|
- bruno.json + environments:见 `references/environment-setup.md`
|
|
63
63
|
|
|
64
64
|
测试报告 / JSONL 不由本 skill 产——`/run-api-test` 跑完后产,落 `docs/features/<feature-id>/`。
|
|
@@ -87,11 +87,11 @@ description: 基于需求生成 bruno 测试接口(.bru + 测试规格 + 环境
|
|
|
87
87
|
- **前置**:S2 测试规格就绪(含用户校正后的服务/通用标注)
|
|
88
88
|
- **行为**:**单次 AI 调用**生成全部 .bru(不拆两阶段、不逐接口循环,见 `references/ai-prompts.md` §S3)
|
|
89
89
|
- .bru 格式 / 断言 / 入参 / docs 段 → `references/bru-format.md`
|
|
90
|
-
- 落盘路径 /
|
|
90
|
+
- 落盘路径 / 按需求隔离 / 根级 bruno.json → `references/collection-layout.md`
|
|
91
91
|
- 使用 db-query skill 获取基于数据库的测试数据
|
|
92
92
|
- 拿不到真实数据时 → 留 `{{var}}` 占位 + 测试规格 §3 标注"需用户手填"
|
|
93
93
|
- **AI 调用失败**(超时/限流)→ 报"AI 增强失败" + 保留 S2 测试规格,**不继续 S4**
|
|
94
|
-
- **结果**:全部 .bru
|
|
94
|
+
- **结果**:全部 .bru 落盘到 `<服务>/<需求前缀>/<接口名>/<场景>.bru` + 根级 bruno.json 就绪
|
|
95
95
|
|
|
96
96
|
### S4. 首次接入环境准备
|
|
97
97
|
|
|
@@ -108,7 +108,7 @@ S4 完成后 skill 结束。**不跑 bru run、不核实 DB、不出报告**。
|
|
|
108
108
|
|
|
109
109
|
产物:
|
|
110
110
|
- `docs/features/<feature-id>/<文件前缀>-api-spec.md`(测试规格)
|
|
111
|
-
- `docs/bruno
|
|
111
|
+
- `docs/bruno/<服务>/<文件前缀>/<接口名>/<场景>.bru`(测试接口,按需求隔离)
|
|
112
112
|
- `docs/bruno/bruno.json` + `docs/bruno/environments/*.bru`(首次接入生成)
|
|
113
113
|
|
|
114
114
|
## 异常速查
|
|
@@ -119,8 +119,8 @@ S4 完成后 skill 结束。**不跑 bru run、不核实 DB、不出报告**。
|
|
|
119
119
|
| 接口范围三种来源都拿不到 | 报错退出 |
|
|
120
120
|
| AI 拿不到真实测试数据 | 留 `{{var}}` 占位 + 测试规格标注"需用户手填",继续跑能跑的部分 |
|
|
121
121
|
| AI 调用失败 | 报"AI 增强失败" + 保留测试规格,不继续 S4 |
|
|
122
|
-
|
|
|
123
|
-
|
|
|
122
|
+
| 同需求重复触发 write-api | 覆盖该需求的 .bru + 覆盖测试规格(数据从当前需求 spec + DB 重新取) |
|
|
123
|
+
| 跨需求同接口同场景 | 各需求独立保留,不覆盖(分别在自己需求目录下) |
|
|
124
124
|
| 项目首次接入 | 自动创建 `docs/bruno/` + bruno.json + environments/ |
|
|
125
125
|
| env 文件缺失 | S4 生成骨架 + AI 填 non-secret + 暂停提示用户填 secret |
|
|
126
126
|
|
|
@@ -130,10 +130,10 @@ S4 完成后 skill 结束。**不跑 bru run、不核实 DB、不出报告**。
|
|
|
130
130
|
|
|
131
131
|
跑完后自检:
|
|
132
132
|
- 测试规格文档落盘 feature 目录,六节齐全
|
|
133
|
-
- .bru 按
|
|
133
|
+
- .bru 按 `<服务>/<需求前缀>/<接口名>/<场景>.bru` 组织,无 `collection.bru`
|
|
134
134
|
- 根级 `bruno.json` 存在;首次接入时 `environments/` + 三环境 .bru 生成,secret 留空
|
|
135
135
|
- .bru 用旧版 tests 语法(`test("name", function() { res.getStatus()/res.getBody()... })`),无 `assert` 段、无 `function onResponse`
|
|
136
|
-
- .bru 的 docs 段含业务说明
|
|
136
|
+
- .bru 的 docs 段含业务说明(关联需求由目录层级表达,docs 段不写关联需求列表)
|
|
137
137
|
- 入参写真实值,变量化只用于环境级 / 前置动态 / 拿不到时占位
|
|
138
138
|
- header 名 / `{{var}}` 变量名大小写与项目既有 .bru 对齐
|
|
139
139
|
- 无程序化配置文件(`.services.yml` / `.config.yml`)
|
|
@@ -143,7 +143,7 @@ S4 完成后 skill 结束。**不跑 bru run、不核实 DB、不出报告**。
|
|
|
143
143
|
|
|
144
144
|
提示用户:
|
|
145
145
|
1. 测试规格位置(`<文件前缀>-api-spec.md`)
|
|
146
|
-
2. .bru collection 位置(`docs/bruno
|
|
146
|
+
2. .bru collection 位置(`docs/bruno/<服务>/<文件前缀>/<接口名>/`)
|
|
147
147
|
3. 首次接入:bruno.json + environments/*.bru 已生成,需填 `local.bru` 的 secret 变量
|
|
148
148
|
4. 下一步:**跑 `/run-api-test` 跑测试 + 核实 DB + 出报告**
|
|
149
149
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
> feature-id: <feature-id>
|
|
13
13
|
> 生成时间: <YYYY-MM-DD HH:MM>
|
|
14
|
-
> 关联接口集合: docs/bruno
|
|
14
|
+
> 关联接口集合: docs/bruno/<服务>/<文件前缀>/
|
|
15
15
|
|
|
16
16
|
## 1. 接口清单
|
|
17
17
|
|
|
@@ -80,13 +80,13 @@
|
|
|
80
80
|
|
|
81
81
|
| 场景 | 接口 | .bru 文件路径 |
|
|
82
82
|
|---|---|---|
|
|
83
|
-
| GET /api/v1/users/:id 正常 | GET /api/v1/users/:id | docs/bruno/user-service
|
|
84
|
-
| POST /api/v1/users 正常 | POST /api/v1/users | docs/bruno/user-service
|
|
85
|
-
| POST /api/v1/users 缺必填 | POST /api/v1/users | docs/bruno/user-service
|
|
83
|
+
| GET /api/v1/users/:id 正常 | GET /api/v1/users/:id | docs/bruno/user-service/<文件前缀>/getUser/正常.bru |
|
|
84
|
+
| POST /api/v1/users 正常 | POST /api/v1/users | docs/bruno/user-service/<文件前缀>/createUser/正常.bru |
|
|
85
|
+
| POST /api/v1/users 缺必填 | POST /api/v1/users | docs/bruno/user-service/<文件前缀>/createUser/缺name.bru |
|
|
86
86
|
| POST /api/v1/auth/login 正常 | POST /api/v1/auth/login | docs/bruno/common/login/正常.bru |
|
|
87
87
|
|
|
88
|
-
- .bru
|
|
89
|
-
- 通用接口落 docs/bruno/common/<接口名>/
|
|
88
|
+
- .bru 按服务 + 需求 + 接口组织(按需求隔离,跨需求不覆盖)
|
|
89
|
+
- 通用接口落 docs/bruno/common/<接口名>/(不绑需求)
|
|
90
90
|
|
|
91
91
|
## 6. 验收口径
|
|
92
92
|
|
|
@@ -95,7 +95,7 @@ AI 判断时**优先参考 `docs/bruno/common/` 既有接口**,避免重复生
|
|
|
95
95
|
- `meta { name, type: http }`
|
|
96
96
|
- HTTP 方法段 + `params` + `headers` + `body:json`(写接口)
|
|
97
97
|
- `tests { test("name", function() { ... }) }` 内嵌断言(旧版语法)
|
|
98
|
-
- `docs { 业务说明
|
|
98
|
+
- `docs { 业务说明 }`(关联需求由 .bru 所在的 `<服务>/<需求前缀>/<接口名>/` 目录层级表达,docs 段不写关联需求列表)
|
|
99
99
|
|
|
100
100
|
### 拿不到真实数据时
|
|
101
101
|
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
```markdown
|
|
72
72
|
| 场景 | 接口 | .bru 文件路径 |
|
|
73
73
|
|---|---|---|
|
|
74
|
-
| GET /api/v1/users/:id 正常 | GET /api/v1/users/:id | docs/bruno/user-service
|
|
75
|
-
| POST /api/v1/users 缺必填 | POST /api/v1/users | docs/bruno/user-service
|
|
74
|
+
| GET /api/v1/users/:id 正常 | GET /api/v1/users/:id | docs/bruno/user-service/<文件前缀>/getUser/正常.bru |
|
|
75
|
+
| POST /api/v1/users 缺必填 | POST /api/v1/users | docs/bruno/user-service/<文件前缀>/createUser/缺name.bru |
|
|
76
76
|
| POST /api/v1/auth/login 正常 | POST /api/v1/auth/login | docs/bruno/common/login/正常.bru |
|
|
77
77
|
```
|
|
78
78
|
|
|
@@ -55,9 +55,6 @@ tests {
|
|
|
55
55
|
docs {
|
|
56
56
|
# 见下方"docs 段写法"
|
|
57
57
|
业务说明: <一句话>
|
|
58
|
-
|
|
59
|
-
关联需求:
|
|
60
|
-
- <YYYY-MM-DD-feature-id> (<文件前缀>-spec.md)
|
|
61
58
|
}
|
|
62
59
|
```
|
|
63
60
|
|
|
@@ -109,22 +106,20 @@ tests {
|
|
|
109
106
|
|
|
110
107
|
第 3 种情况发生时,在测试规格 §3 标注哪些参数需用户手填,skill 继续跑能跑的部分,不因此中断。
|
|
111
108
|
|
|
112
|
-
## docs 段写法
|
|
109
|
+
## docs 段写法
|
|
113
110
|
|
|
114
111
|
```
|
|
115
112
|
docs {
|
|
116
113
|
业务说明: <一句话,从 Javadoc 或 spec 提取;提取不到占位"(scan 阶段未提取到 Javadoc,请补充业务说明)">
|
|
117
|
-
|
|
118
|
-
关联需求:
|
|
119
|
-
- <YYYY-MM-DD-feature-id> (<文件前缀>-spec.md)
|
|
120
114
|
}
|
|
121
115
|
```
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- AI 生成时,若该接口目录下已有 .bru(docs 段含历史关联需求),把当前 feature-id **追加**到列表(去重),不删除已有 feature
|
|
117
|
+
- 仅业务说明一句,不写关联需求列表(关联需求由 .bru 所在的 `<服务>/<需求前缀>/<接口名>/` 目录层级表达,扫目录即可知)
|
|
126
118
|
- 不在 docs 段写断言推理 / 场景预期等详细内容(简短,bruno 不渲染 markdown)
|
|
127
|
-
|
|
119
|
+
|
|
120
|
+
### 为什么 docs 段不写关联需求
|
|
121
|
+
|
|
122
|
+
之前 docs 段含"关联需求"列表,是为让 `/run-api-test` 在"只指定接口"时反查接口关联哪些需求。现在 .bru 落盘路径含需求层(`<服务>/<需求前缀>/<接口名>/`),反查需求只需扫 `docs/bruno/<服务>*/<需求前缀>/<接口名>/` 哪些存在即可,无需 docs 段冗余维护。
|
|
128
123
|
|
|
129
124
|
## header 名 / 变量名对齐
|
|
130
125
|
|
|
@@ -9,42 +9,45 @@ docs/bruno/
|
|
|
9
9
|
│ ├── local.bru
|
|
10
10
|
│ ├── staging.bru
|
|
11
11
|
│ └── production.bru
|
|
12
|
-
├── common/ #
|
|
12
|
+
├── common/ # 通用接口,跨服务跨需求复用
|
|
13
13
|
│ └── <接口名>/
|
|
14
14
|
│ └── <场景>.bru
|
|
15
15
|
└── <服务名>/ # 微服务,无服务划分时退化为此层不存在
|
|
16
|
-
└──
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
└── <需求前缀>/ # 需求隔离层,按需求独立保留,跨需求不覆盖
|
|
17
|
+
└── <接口名>/ # 接口目录,本需求内累积场景
|
|
18
|
+
├── <场景-1>.bru
|
|
19
|
+
└── <场景-2>.bru
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
## 路径规则
|
|
22
23
|
|
|
23
24
|
| 接口类型 | 路径 |
|
|
24
25
|
|---|---|
|
|
25
|
-
| 业务接口(有服务划分) | `docs/bruno
|
|
26
|
-
| 业务接口(无服务划分) | `docs/bruno
|
|
26
|
+
| 业务接口(有服务划分) | `docs/bruno/<服务>/<需求前缀>/<接口名>/<场景>.bru` |
|
|
27
|
+
| 业务接口(无服务划分) | `docs/bruno/<需求前缀>/<接口名>/<场景>.bru` |
|
|
27
28
|
| 通用接口 | `docs/bruno/common/<接口名>/<场景>.bru` |
|
|
28
29
|
|
|
29
30
|
### 取名规则
|
|
30
31
|
|
|
31
32
|
- **`<服务>`** — 由 AI 在 S2 识别(见 `ai-prompts.md`),参考 `docs/bruno/` 既有目录名保持一致
|
|
33
|
+
- **`<需求前缀>`** — 取 S1 锁定的 feature-id 的"文件前缀"段(即 `<业务ID>-<标题>` 或 `<标题>`,如 `CRM-req19230-号百商品详情查询接口`),与 `docs/features/<feature-id>/` 下文件的命名前缀一致
|
|
32
34
|
- **`<接口名>`** — 取接口的方法名或路径最后一段,参考项目既有 .bru 的命名习惯
|
|
33
35
|
- **`<场景>`** — 取测试规格 §2 的场景名(如 `正常` / `缺prodInstId` / `产品不存在`)
|
|
34
36
|
|
|
35
|
-
##
|
|
37
|
+
## 按服务 + 需求 + 接口组织
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
**核心原则**:接口按服务 + 需求组织,**按需求独立保留,跨需求不覆盖**。
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
同一接口在需求 A 和需求 B 下有独立 .bru:
|
|
42
|
+
- 需求 A 跑 A 时的 .bru(`docs/bruno/<服务>/<需求A前缀>/<接口名>/...`)
|
|
43
|
+
- 需求 B 跑 B 时的 .bru(`docs/bruno/<服务>/<需求B前缀>/<接口名>/...`)
|
|
44
|
+
- 两份独立保留,数据互不干扰——每份都按对应需求的 spec + DB 重新取数据生成
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
**为什么这么设计**:之前"接口跨 feature 复用,后跑覆盖"在加了需求层后没意义(需求 A 的 .bru 在 A 目录下,不会跑到 B 目录)。需求层的目的就是按需求隔离数据。
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
## 通用接口独立
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
**例外**:docs 段的"关联需求"列表**不覆盖**,而是去重追加——记录该接口关联过哪些 feature,供 `/run-api-test` 反查。
|
|
50
|
+
通用接口(登录、token 刷新、健康检查、公共字典等)放 `docs/bruno/common/<接口名>/`,**不绑需求**——这些接口跨所有需求复用,不应该每个需求一份。
|
|
48
51
|
|
|
49
52
|
## 不生成 per-folder collection.bru
|
|
50
53
|
|
|
@@ -76,6 +79,12 @@ bruno 的 folder 不需要 `collection.bru`,只在 `docs/bruno/` 根级有一个
|
|
|
76
79
|
|
|
77
80
|
不报错,不需要用户手动准备。
|
|
78
81
|
|
|
82
|
+
## 找接口的方式
|
|
83
|
+
|
|
84
|
+
**按需求找**:直接进 `docs/bruno/<服务>/<需求前缀>/`,该需求所有接口都在里面。
|
|
85
|
+
|
|
86
|
+
**按接口找(跨需求)**:扫 `docs/bruno/<服务>/*/` 下所有以 `<接口名>` 命名的子目录,看哪些需求测过该接口——这就是 `/run-api-test` 只指定接口时反查所属需求的方式。
|
|
87
|
+
|
|
79
88
|
## 与测试规格文档的契约
|
|
80
89
|
|
|
81
|
-
测试规格 §5 接口映射里的路径必须与实际落盘路径**一一对应**,否则下游 `/run-api-test` R1 拿到 .bru 清单后会报"对应 .bru 缺失"。
|
|
90
|
+
测试规格 §5 接口映射里的路径必须与实际落盘路径**一一对应**,否则下游 `/run-api-test` R1 拿到 .bru 清单后会报"对应 .bru 缺失"。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/dbquery/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/dbquery/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyHzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4GzD"}
|
package/dist/dbquery/cli.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.registerDbCommands = registerDbCommands;
|
|
|
21
21
|
const node_path_1 = __importDefault(require("node:path"));
|
|
22
22
|
const index_1 = require("./index");
|
|
23
23
|
const init_1 = require("./init");
|
|
24
|
+
const identifiers_1 = require("./identifiers");
|
|
24
25
|
function parseFormat(s) {
|
|
25
26
|
if (!s || s === 'json')
|
|
26
27
|
return 'json';
|
|
@@ -75,8 +76,16 @@ function outputQueryResult(result, format) {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
function outputSchemaResult(result, format) {
|
|
79
|
-
|
|
79
|
+
function outputSchemaResult(result, mode, format) {
|
|
80
|
+
if (mode === 'ddl' && 'ddl' in result) {
|
|
81
|
+
// ddl emits the raw CREATE TABLE DDL text, not JSON.
|
|
82
|
+
if (format !== 'json') {
|
|
83
|
+
process.stderr.write(`[db-query] --format=${format} ignored for ddl output (always DDL text).\n`);
|
|
84
|
+
}
|
|
85
|
+
process.stdout.write(result.ddl + '\n');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// For other schema results, json is the only first-class format; table/csv
|
|
80
89
|
// fall back to json because the shape varies per mode.
|
|
81
90
|
if (format !== 'json') {
|
|
82
91
|
process.stderr.write(`[db-query] --format=${format} not supported for schema output; using json.\n`);
|
|
@@ -151,8 +160,16 @@ function registerDbCommands(program) {
|
|
|
151
160
|
const projectRoot = process.cwd();
|
|
152
161
|
try {
|
|
153
162
|
const format = parseFormat(options.format);
|
|
163
|
+
if (mode === 'ddl' && options.table) {
|
|
164
|
+
try {
|
|
165
|
+
(0, identifiers_1.validateIdentifier)(options.table, 'table');
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
throw new ArgError(e instanceof Error ? e.message : String(e));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
154
171
|
const result = await (0, index_1.runSchema)({ mode, table: options.table, profile: options.profile }, projectRoot);
|
|
155
|
-
outputSchemaResult(result, format);
|
|
172
|
+
outputSchemaResult(result, mode, format);
|
|
156
173
|
}
|
|
157
174
|
catch (err) {
|
|
158
175
|
renderErrorAndExit(err);
|
package/dist/dbquery/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/dbquery/cli.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/dbquery/cli.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;AA6HH,gDA4GC;AAvOD,0DAA6B;AAI7B,mCASiB;AACjB,iCAAwC;AACxC,+CAAmD;AAMnD,SAAS,WAAW,CAAC,CAAqB;IACxC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACtC,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,IAAI,QAAQ,CAAC,8CAA8C,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,QAAS,SAAQ,KAAK;CAAG;AAE/B,SAAS,SAAS,CAAC,CAAU;IAC3B,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,MAAmB;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/F,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAmB;IAC3C,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAe,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5F,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrG,CAAC;AAED,SAAS,UAAU,CAAC,CAAU;IAC5B,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAC7C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAmB,EAAE,MAAc;IAC5D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/D,CAAC;SAAM,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;QACtD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,MAAM,CAAC,QAAQ,eAAe,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAoB,EACpB,IAAoD,EACpD,MAAc;IAEd,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;QACtC,qDAAqD;QACrD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,MAAM,8CAA8C,CAAC,CAAC;QACpG,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IACD,2EAA2E;IAC3E,uDAAuD;IACvD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,MAAM,iDAAiD,CAAC,CAAC;IACvG,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAsB,EAAE,MAAc;IAClE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,MAAM,mDAAmD,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,8EAA8E;AAE9E,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,GAAG,YAAY,QAAQ;QAAE,OAAO,CAAC,CAAC;IACtC,IAAI,GAAG,YAAY,oBAAY,EAAE,CAAC;QAChC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,kBAAkB,CAAC;YACxB,KAAK,mBAAmB;gBACtB,OAAO,CAAC,CAAC;YACX,KAAK,iBAAiB;gBACpB,OAAO,CAAC,CAAC;YACX,KAAK,mBAAmB,CAAC;YACzB,KAAK,eAAe,CAAC;YACrB,KAAK,cAAc,CAAC;YACpB,KAAK,oBAAoB;gBACvB,OAAO,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC,CAAC,kCAAkC;AAC9C,CAAC;AAED,6EAA6E;AAE7E,SAAgB,kBAAkB,CAAC,OAAgB;IACjD,MAAM,EAAE,GAAG,OAAO;SACf,OAAO,CAAC,IAAI,CAAC;SACb,WAAW,CAAC,4GAA4G,CAAC,CAAC;IAE7H,EAAE;SACC,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,6EAA6E,CAAC;SAC1F,cAAc,CAAC,aAAa,EAAE,iDAAiD,CAAC;SAChF,MAAM,CAAC,kBAAkB,EAAE,4DAA4D,CAAC;SACxF,MAAM,CAAC,aAAa,EAAE,kCAAkC,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SAC/E,MAAM,CAAC,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,CAAC;SACzD,MAAM,CAAC,KAAK,EAAE,OAKd,EAAE,EAAE;QACH,MAAM,eAAe,CAAC;YACpB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEL,EAAE;SACC,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,8DAA8D,CAAC;SAC3E,cAAc,CAAC,eAAe,EAAE,6CAA6C,CAAC;SAC9E,MAAM,CAAC,gBAAgB,EAAE,qDAAqD,CAAC;SAC/E,MAAM,CAAC,kBAAkB,EAAE,+BAA+B,CAAC;SAC3D,MAAM,CAAC,mBAAmB,EAAE,0CAA0C,EAAE,MAAM,CAAC;SAC/E,MAAM,CAAC,KAAK,EAAE,OAKd,EAAE,EAAE;QACH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;QAC1E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,OAAO,CAAC,IAAI,yDAAyD,CAAC,CAAC;YAC1H,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAsD,CAAC;QAC5E,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACzF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,IAAI,IAAI,CAAC,CAAC;YAC1E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3C,IAAI,IAAI,KAAK,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBACpC,IAAI,CAAC;oBACH,IAAA,gCAAkB,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAC7C,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,MAAM,IAAI,QAAQ,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAS,EAC5B,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,EACxD,WAAW,CACZ,CAAC;YACF,kBAAkB,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,EAAE;SACC,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,CAAC;SAClE,MAAM,CAAC,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAY,EAAC,EAAE,EAAE,WAAW,CAAC,CAAC;YACnD,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,EAAE;SACC,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,oFAAoF,CAAC;SACjG,MAAM,CAAC,SAAS,EAAE,yCAAyC,CAAC;SAC5D,MAAM,CAAC,CAAC,OAA4B,EAAE,EAAE;QACvC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAA,qBAAc,EAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC;YAC9E,IAAI,MAAM,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,cAAc,MAAM,CAAC,QAAQ,+CAA+C,CAC7E,CAAC;gBACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,cAAc,MAAM,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,QAAQ,IAAI;gBAC9F,kGAAkG,CACnG,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,KAAwD,EACxD,SAA6B;IAE7B,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,IAAA,gBAAQ,EAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAClD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAY;IACtC,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,GAAG,YAAY,8BAAsB,EAAE,CAAC;QAC1C,yCAAyC;QACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC9C,CAAC;SAAM,IAAI,GAAG,YAAY,oBAAY,EAAE,CAAC;QACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACtD,CAAC;SAAM,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED,iFAAiF;AACjF,0EAA0E;AAC1E,KAAK,mBAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/dbquery/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,EAGL,oBAAoB,EAErB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/dbquery/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,EAGL,oBAAoB,EAErB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EAEZ,aAAa,EACb,cAAc,EAKf,MAAM,SAAS,CAAC;AAqDjB;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoD3F;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmG9F;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAuBrG;AAGD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;AAClG,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
package/dist/dbquery/executor.js
CHANGED
|
@@ -148,40 +148,32 @@ async function runSchema(input, projectRoot) {
|
|
|
148
148
|
? await (0, mysql_1.createMysqlConnection)(profile)
|
|
149
149
|
: await (0, postgres_1.createPostgresConnection)(profile);
|
|
150
150
|
try {
|
|
151
|
-
//
|
|
151
|
+
// ddl: MySQL uses SHOW CREATE TABLE (single query, server-rendered DDL);
|
|
152
|
+
// PG assembles from column query + pg_get_constraintdef + table comment.
|
|
152
153
|
if (mode === 'ddl') {
|
|
153
|
-
let
|
|
154
|
+
let result;
|
|
154
155
|
if (profile.type === 'mysql') {
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
colsRaw = await driver.execute(colsQ.sql, constants_1.QUERY_TIMEOUT_MS, colsQ.params);
|
|
159
|
-
pkRaw = await driver.execute(pkQ.sql, constants_1.QUERY_TIMEOUT_MS, pkQ.params);
|
|
160
|
-
consRaw = await driver.execute(consQ.sql, constants_1.QUERY_TIMEOUT_MS, consQ.params);
|
|
156
|
+
const showQ = (0, queries_1.mysqlSchemaQuery)('ddl', table, profile.database);
|
|
157
|
+
const raw = await driver.execute(showQ.sql, constants_1.QUERY_TIMEOUT_MS, showQ.params);
|
|
158
|
+
result = (0, format_1.extractMysqlDdl)(raw, table, profile.name, elapsedMs(start));
|
|
161
159
|
}
|
|
162
160
|
else {
|
|
163
|
-
|
|
164
|
-
colsRaw = await driver.execute(parametrized.sql, constants_1.QUERY_TIMEOUT_MS, parametrized.params);
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
'WHEN c.contype = \'u\' THEN \'UNIQUE\' ' +
|
|
173
|
-
'WHEN c.contype = \'f\' THEN \'FOREIGN KEY\' ' +
|
|
174
|
-
'WHEN c.contype = \'c\' THEN \'CHECK\' ELSE c.contype::text END AS type ' +
|
|
175
|
-
'FROM pg_constraint c WHERE c.conrelid = $1::regclass ORDER BY c.conname', constants_1.QUERY_TIMEOUT_MS, [regclass]);
|
|
161
|
+
const schema = profile.schema ?? 'public';
|
|
162
|
+
const colsRaw = await driver.execute(parametrized.sql, constants_1.QUERY_TIMEOUT_MS, parametrized.params);
|
|
163
|
+
const consQ = (0, queries_1.postgresConstraintDefsSql)(table, schema);
|
|
164
|
+
const consRaw = await driver.execute(consQ.sql, constants_1.QUERY_TIMEOUT_MS, consQ.params);
|
|
165
|
+
const idxQ = (0, queries_1.postgresIndexDefsSql)(table, schema);
|
|
166
|
+
const idxRaw = await driver.execute(idxQ.sql, constants_1.QUERY_TIMEOUT_MS, idxQ.params);
|
|
167
|
+
const tcQ = (0, queries_1.postgresTableCommentSql)(table, schema);
|
|
168
|
+
const tcRaw = await driver.execute(tcQ.sql, constants_1.QUERY_TIMEOUT_MS, tcQ.params);
|
|
169
|
+
result = (0, format_1.renderPostgresDdl)(colsRaw, consRaw, idxRaw, tcRaw, table, schema, profile.name, elapsedMs(start));
|
|
176
170
|
}
|
|
177
|
-
const result = (0, format_1.formatDdlResult)(colsRaw, pkRaw, consRaw, table, profile.name, elapsedMs(start));
|
|
178
171
|
(0, logging_1.logQuery)(projectRoot, {
|
|
179
172
|
timestamp: nowIso(),
|
|
180
173
|
profile: profile.name,
|
|
181
174
|
mode,
|
|
182
175
|
sql: parametrized.sql,
|
|
183
176
|
durationMs: elapsedMs(start),
|
|
184
|
-
rowCount: result.columns.length,
|
|
185
177
|
status: 'ok',
|
|
186
178
|
});
|
|
187
179
|
return result;
|