@lark-apaas/miaoda-cli 0.1.0-alpha.36c401b → 0.1.0-alpha.8825a76
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/api/db/_debug_trace.js +21 -0
- package/dist/api/db/api.js +10 -0
- package/dist/api/file/_debug_trace.js +21 -0
- package/dist/api/file/client.js +17 -0
- package/dist/cli/handlers/db/sql.js +14 -4
- package/dist/utils/render.js +22 -7
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.traceRequest = traceRequest;
|
|
4
|
+
exports.traceResponse = traceResponse;
|
|
5
|
+
/**
|
|
6
|
+
* ⚠️ TODO(REMOVE-BEFORE-RELEASE): 调试用 HTTP trace。
|
|
7
|
+
*
|
|
8
|
+
* 仅在 `--verbose` 下把 db 域 admin-inner 请求的 URL 与后端 logid 打到 stderr,
|
|
9
|
+
* 方便联调时把 logid 贴给后端查链路。正式版本前请整文件删除,并把
|
|
10
|
+
* `src/api/db/{api,client}.ts` 里的 `traceRequest()` / `traceResponse()` 调用一并移除。
|
|
11
|
+
*/
|
|
12
|
+
const logger_1 = require("../../utils/logger");
|
|
13
|
+
/** 请求前打 method + 完整 URL(不含敏感 header)。 */
|
|
14
|
+
function traceRequest(method, url) {
|
|
15
|
+
(0, logger_1.debug)(`http → ${method} ${url}`);
|
|
16
|
+
}
|
|
17
|
+
/** 响应后打 status + x-tt-logid(后端追踪 ID)。 */
|
|
18
|
+
function traceResponse(method, url, status, headers) {
|
|
19
|
+
const logId = headers?.get("x-tt-logid") ?? headers?.get("X-Tt-Logid") ?? "(none)";
|
|
20
|
+
(0, logger_1.debug)(`http ← ${method} ${url} status=${String(status)} x-tt-logid=${logId}`);
|
|
21
|
+
}
|
package/dist/api/db/api.js
CHANGED
|
@@ -5,6 +5,8 @@ exports.getSchema = getSchema;
|
|
|
5
5
|
exports.importData = importData;
|
|
6
6
|
exports.exportData = exportData;
|
|
7
7
|
const http_1 = require("../../utils/http");
|
|
8
|
+
// TODO(REMOVE-BEFORE-RELEASE): debug-only HTTP trace(详见 _debug_trace.ts)
|
|
9
|
+
const _debug_trace_1 = require("./_debug_trace");
|
|
8
10
|
const error_1 = require("../../utils/error");
|
|
9
11
|
const client_1 = require("./client");
|
|
10
12
|
// CLI 不再为 dbBranch 设默认值:
|
|
@@ -24,7 +26,9 @@ async function execSql(opts) {
|
|
|
24
26
|
const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/sql", {
|
|
25
27
|
dbBranch: opts.dbBranch,
|
|
26
28
|
});
|
|
29
|
+
(0, _debug_trace_1.traceRequest)("POST", url);
|
|
27
30
|
const response = await client.post(url, { sql: opts.sql });
|
|
31
|
+
(0, _debug_trace_1.traceResponse)("POST", url, response.status, response.headers);
|
|
28
32
|
if (!response.ok) {
|
|
29
33
|
// 4xx / 5xx:尝试解析 body 取 status_code 映射业务错误
|
|
30
34
|
let body = null;
|
|
@@ -57,7 +61,9 @@ async function getSchema(opts) {
|
|
|
57
61
|
includeStats: opts.includeStats ? "true" : undefined,
|
|
58
62
|
dbBranch: opts.dbBranch,
|
|
59
63
|
});
|
|
64
|
+
(0, _debug_trace_1.traceRequest)("GET", url);
|
|
60
65
|
const response = await client.get(url);
|
|
66
|
+
(0, _debug_trace_1.traceResponse)("GET", url, response.status, response.headers);
|
|
61
67
|
if (!response.ok) {
|
|
62
68
|
let body = null;
|
|
63
69
|
try {
|
|
@@ -89,12 +95,14 @@ async function importData(opts) {
|
|
|
89
95
|
});
|
|
90
96
|
const contentType = opts.format === "csv" ? "text/csv" : "application/json";
|
|
91
97
|
const ab = opts.body.buffer.slice(opts.body.byteOffset, opts.body.byteOffset + opts.body.byteLength);
|
|
98
|
+
(0, _debug_trace_1.traceRequest)("POST", url);
|
|
92
99
|
const response = await client.request({
|
|
93
100
|
method: "POST",
|
|
94
101
|
url,
|
|
95
102
|
headers: { "Content-Type": contentType },
|
|
96
103
|
body: ab,
|
|
97
104
|
});
|
|
105
|
+
(0, _debug_trace_1.traceResponse)("POST", url, response.status, response.headers);
|
|
98
106
|
if (!response.ok) {
|
|
99
107
|
let body = null;
|
|
100
108
|
try {
|
|
@@ -131,7 +139,9 @@ async function exportData(opts) {
|
|
|
131
139
|
dbBranch: opts.dbBranch,
|
|
132
140
|
});
|
|
133
141
|
const reqBody = { limit: opts.limit ?? 5000 };
|
|
142
|
+
(0, _debug_trace_1.traceRequest)("POST", url);
|
|
134
143
|
const response = await client.post(url, reqBody);
|
|
144
|
+
(0, _debug_trace_1.traceResponse)("POST", url, response.status, response.headers);
|
|
135
145
|
if (!response.ok) {
|
|
136
146
|
// 错误路径:body 是 JSON envelope
|
|
137
147
|
let body = null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.traceRequest = traceRequest;
|
|
4
|
+
exports.traceResponse = traceResponse;
|
|
5
|
+
/**
|
|
6
|
+
* ⚠️ TODO(REMOVE-BEFORE-RELEASE): 调试用 HTTP trace。
|
|
7
|
+
*
|
|
8
|
+
* 仅在 `--verbose` 下把 file 域请求的 URL 与后端 logid 打到 stderr,
|
|
9
|
+
* 方便联调时把 logid 贴给后端查链路。正式版本前请整文件删除,并把
|
|
10
|
+
* `src/api/file/client.ts` 里的 `traceRequest()` / `traceResponse()` 调用一并移除。
|
|
11
|
+
*/
|
|
12
|
+
const logger_1 = require("../../utils/logger");
|
|
13
|
+
/** 请求前打 method + 完整 URL(不含敏感 header)。 */
|
|
14
|
+
function traceRequest(method, url) {
|
|
15
|
+
(0, logger_1.debug)(`http → ${method} ${url}`);
|
|
16
|
+
}
|
|
17
|
+
/** 响应后打 status + x-tt-logid(后端追踪 ID)。 */
|
|
18
|
+
function traceResponse(method, url, status, headers) {
|
|
19
|
+
const logId = headers?.get("x-tt-logid") ?? headers?.get("X-Tt-Logid") ?? "(none)";
|
|
20
|
+
(0, logger_1.debug)(`http ← ${method} ${url} status=${String(status)} x-tt-logid=${logId}`);
|
|
21
|
+
}
|
package/dist/api/file/client.js
CHANGED
|
@@ -8,6 +8,8 @@ exports.doPost = doPost;
|
|
|
8
8
|
exports.doRequest = doRequest;
|
|
9
9
|
const http_1 = require("../../utils/http");
|
|
10
10
|
const error_1 = require("../../utils/error");
|
|
11
|
+
// TODO(REMOVE-BEFORE-RELEASE): debug-only HTTP trace(详见 _debug_trace.ts)
|
|
12
|
+
const _debug_trace_1 = require("./_debug_trace");
|
|
11
13
|
const http_client_1 = require("@lark-apaas/http-client");
|
|
12
14
|
/** 进程内 bucket 缓存:{appId: bucketId}。不跨进程。 */
|
|
13
15
|
const bucketCache = new Map();
|
|
@@ -131,10 +133,15 @@ async function mapHttpError(err, opts) {
|
|
|
131
133
|
*/
|
|
132
134
|
async function doGet(url, opts = {}, client = (0, http_1.getHttpClient)()) {
|
|
133
135
|
try {
|
|
136
|
+
(0, _debug_trace_1.traceRequest)("GET", url);
|
|
134
137
|
const response = await client.get(url);
|
|
138
|
+
(0, _debug_trace_1.traceResponse)("GET", url, response.status, response.headers);
|
|
135
139
|
return (await response.json());
|
|
136
140
|
}
|
|
137
141
|
catch (err) {
|
|
142
|
+
if (err instanceof http_client_1.HttpError && err.response) {
|
|
143
|
+
(0, _debug_trace_1.traceResponse)("GET", url, err.response.status, err.response.headers);
|
|
144
|
+
}
|
|
138
145
|
await mapHttpError(err, opts);
|
|
139
146
|
throw err; // 不可达,mapHttpError 必定 throw
|
|
140
147
|
}
|
|
@@ -142,10 +149,15 @@ async function doGet(url, opts = {}, client = (0, http_1.getHttpClient)()) {
|
|
|
142
149
|
/** 包一层 client.post + HttpError 统一映射。默认 admin innerapi,可显式切 runtime。 */
|
|
143
150
|
async function doPost(url, body, opts = {}, client = (0, http_1.getHttpClient)()) {
|
|
144
151
|
try {
|
|
152
|
+
(0, _debug_trace_1.traceRequest)("POST", url);
|
|
145
153
|
const response = await client.post(url, body);
|
|
154
|
+
(0, _debug_trace_1.traceResponse)("POST", url, response.status, response.headers);
|
|
146
155
|
return (await response.json());
|
|
147
156
|
}
|
|
148
157
|
catch (err) {
|
|
158
|
+
if (err instanceof http_client_1.HttpError && err.response) {
|
|
159
|
+
(0, _debug_trace_1.traceResponse)("POST", url, err.response.status, err.response.headers);
|
|
160
|
+
}
|
|
149
161
|
await mapHttpError(err, opts);
|
|
150
162
|
throw err;
|
|
151
163
|
}
|
|
@@ -153,10 +165,15 @@ async function doPost(url, body, opts = {}, client = (0, http_1.getHttpClient)()
|
|
|
153
165
|
/** 包一层 client.request + HttpError 统一映射(DELETE 带 body 的场景)。 */
|
|
154
166
|
async function doRequest(cfg, opts = {}, client = (0, http_1.getHttpClient)()) {
|
|
155
167
|
try {
|
|
168
|
+
(0, _debug_trace_1.traceRequest)(cfg.method, cfg.url);
|
|
156
169
|
const response = await client.request(cfg);
|
|
170
|
+
(0, _debug_trace_1.traceResponse)(cfg.method, cfg.url, response.status, response.headers);
|
|
157
171
|
return (await response.json());
|
|
158
172
|
}
|
|
159
173
|
catch (err) {
|
|
174
|
+
if (err instanceof http_client_1.HttpError && err.response) {
|
|
175
|
+
(0, _debug_trace_1.traceResponse)(cfg.method, cfg.url, err.response.status, err.response.headers);
|
|
176
|
+
}
|
|
160
177
|
await mapHttpError(err, opts);
|
|
161
178
|
throw err;
|
|
162
179
|
}
|
|
@@ -114,7 +114,7 @@ function renderSingle(raw) {
|
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
const cols = collectColumns(parsed.rows);
|
|
117
|
-
const rows = parsed.rows.map((r) => cols.map((c) => formatCell(r[c])));
|
|
117
|
+
const rows = parsed.rows.map((r) => cols.map((c) => formatCell(r[c], tty)));
|
|
118
118
|
(0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(cols, rows) : (0, render_1.renderTsv)(cols, rows));
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
@@ -155,9 +155,19 @@ function collectColumns(rows) {
|
|
|
155
155
|
return [];
|
|
156
156
|
return Object.keys(rows[0]);
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
/**
|
|
159
|
+
* SQL 单元格 → 字符串:
|
|
160
|
+
* - null / undefined → "NULL"(TTY 下用 dim+gray ANSI 弱化,对齐 mysql/DuckDB;
|
|
161
|
+
* non-TTY 用裸字面量,避免被 cat / 管道吞掉样式;JSON 模式不会走到这里,
|
|
162
|
+
* 走 toJson 直接用 parsed.rows,JSON.stringify 输出 `null` 字面)
|
|
163
|
+
* - string → 原样
|
|
164
|
+
* - number / boolean → toString
|
|
165
|
+
* - object / array → JSON.stringify
|
|
166
|
+
*/
|
|
167
|
+
function formatCell(v, tty) {
|
|
168
|
+
if (v === null || v === undefined) {
|
|
169
|
+
return tty ? "\u001b[2;90mNULL\u001b[0m" : "NULL";
|
|
170
|
+
}
|
|
161
171
|
if (typeof v === "string")
|
|
162
172
|
return v;
|
|
163
173
|
if (typeof v === "number" || typeof v === "boolean")
|
package/dist/utils/render.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.formatSize = formatSize;
|
|
16
16
|
exports.formatTime = formatTime;
|
|
17
|
+
exports.visibleWidth = visibleWidth;
|
|
17
18
|
exports.renderAlignedTable = renderAlignedTable;
|
|
18
19
|
exports.renderTsv = renderTsv;
|
|
19
20
|
exports.renderKeyValue = renderKeyValue;
|
|
@@ -64,21 +65,35 @@ function formatTime(iso, isTty) {
|
|
|
64
65
|
const da = String(date.getDate()).padStart(2, "0");
|
|
65
66
|
return `${y}-${mo}-${da}`;
|
|
66
67
|
}
|
|
67
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* SGR (Select Graphic Rendition) ANSI 转义匹配:`ESC[<digits;...>m`。
|
|
70
|
+
* 用于在列宽计算时剥离不显示的控制字符——否则带 ANSI 颜色的 cell 会按 .length
|
|
71
|
+
* 算进列宽(比如 dim "NULL" 实际占 4 字符但 .length=11),导致表格对齐错位。
|
|
72
|
+
*/
|
|
73
|
+
const ANSI_SGR_RE = /\[[0-9;]*m/g;
|
|
74
|
+
/** cell 可见字符宽度:剥离 ANSI 转义后再算长度。 */
|
|
75
|
+
function visibleWidth(s) {
|
|
76
|
+
return s.replace(ANSI_SGR_RE, "").length;
|
|
77
|
+
}
|
|
78
|
+
function padVisibleEnd(s, targetWidth) {
|
|
79
|
+
const w = visibleWidth(s);
|
|
80
|
+
return w >= targetWidth ? s : s + " ".repeat(targetWidth - w);
|
|
81
|
+
}
|
|
82
|
+
/** 渲染 TTY 对齐表格(多行,列宽按最长内容;ANSI 转义不计入列宽)。 */
|
|
68
83
|
function renderAlignedTable(headers, rows) {
|
|
69
84
|
const colWidths = headers.map((h, i) => {
|
|
70
|
-
let w = h
|
|
85
|
+
let w = visibleWidth(h);
|
|
71
86
|
for (const row of rows) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
w =
|
|
87
|
+
const cw = visibleWidth(row[i] || "");
|
|
88
|
+
if (cw > w)
|
|
89
|
+
w = cw;
|
|
75
90
|
}
|
|
76
91
|
return w;
|
|
77
92
|
});
|
|
78
93
|
const lines = [];
|
|
79
|
-
lines.push(headers.map((h, i) => h
|
|
94
|
+
lines.push(headers.map((h, i) => padVisibleEnd(h, colWidths[i])).join(" ").trimEnd());
|
|
80
95
|
for (const row of rows) {
|
|
81
|
-
lines.push(row.map((cell, i) => (cell || ""
|
|
96
|
+
lines.push(row.map((cell, i) => padVisibleEnd(cell || "", colWidths[i])).join(" ").trimEnd());
|
|
82
97
|
}
|
|
83
98
|
return lines.join("\n");
|
|
84
99
|
}
|