fdb2 1.0.36 → 1.0.38
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/README.md +41 -0
- package/dist/public/explorer.css +533 -485
- package/dist/public/explorer.js +1277 -868
- package/dist/public/index.css +172 -34
- package/dist/public/index.js +103 -49
- package/dist/public/layout.js +3 -3
- package/dist/public/vue.js +1 -1
- package/dist/server/core/schema/__tests__/canonical.test.ts +195 -0
- package/dist/server/core/schema/__tests__/ddl-emitter.test.ts +456 -0
- package/dist/server/core/schema/__tests__/diff.test.ts +242 -0
- package/dist/server/core/schema/__tests__/roundtrip.property.test.ts +403 -0
- package/dist/server/core/schema/canonical.d.ts +50 -0
- package/dist/server/core/schema/canonical.d.ts.map +1 -0
- package/dist/server/core/schema/canonical.js +212 -0
- package/dist/server/core/schema/canonical.js.map +1 -0
- package/dist/server/core/schema/canonical.ts +223 -0
- package/dist/server/core/schema/ddl-emitter.d.ts +68 -0
- package/dist/server/core/schema/ddl-emitter.d.ts.map +1 -0
- package/dist/server/core/schema/ddl-emitter.js +813 -0
- package/dist/server/core/schema/ddl-emitter.js.map +1 -0
- package/dist/server/core/schema/ddl-emitter.ts +925 -0
- package/dist/server/core/schema/diff.d.ts +16 -0
- package/dist/server/core/schema/diff.d.ts.map +1 -0
- package/dist/server/core/schema/diff.js +115 -0
- package/dist/server/core/schema/diff.js.map +1 -0
- package/dist/server/core/schema/diff.ts +134 -0
- package/dist/server/core/schema/type-defaults.d.ts +27 -0
- package/dist/server/core/schema/type-defaults.d.ts.map +1 -0
- package/dist/server/core/schema/type-defaults.js +61 -0
- package/dist/server/core/schema/type-defaults.js.map +1 -0
- package/dist/server/core/schema/type-defaults.ts +79 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +347 -124
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.ts +336 -161
- package/dist/server/model/ddl-spec.d.ts +163 -0
- package/dist/server/model/ddl-spec.d.ts.map +1 -0
- package/dist/server/model/ddl-spec.js +32 -0
- package/dist/server/model/ddl-spec.js.map +1 -0
- package/dist/server/model/ddl-spec.ts +175 -0
- package/dist/server/service/connection.service.d.ts +20 -0
- package/dist/server/service/connection.service.d.ts.map +1 -1
- package/dist/server/service/connection.service.js +99 -25
- package/dist/server/service/connection.service.js.map +1 -1
- package/dist/server/service/connection.service.ts +104 -25
- package/dist/server/service/database/base.service.d.ts +0 -4
- package/dist/server/service/database/base.service.d.ts.map +1 -1
- package/dist/server/service/database/base.service.js.map +1 -1
- package/dist/server/service/database/base.service.ts +0 -5
- package/dist/server/service/database/clickhouse.service.d.ts +0 -5
- package/dist/server/service/database/clickhouse.service.d.ts.map +1 -1
- package/dist/server/service/database/clickhouse.service.js +0 -49
- package/dist/server/service/database/clickhouse.service.js.map +1 -1
- package/dist/server/service/database/clickhouse.service.ts +0 -52
- package/dist/server/service/database/cockroachdb.service.d.ts +0 -5
- package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
- package/dist/server/service/database/cockroachdb.service.js +11 -142
- package/dist/server/service/database/cockroachdb.service.js.map +1 -1
- package/dist/server/service/database/cockroachdb.service.ts +11 -161
- package/dist/server/service/database/database.service.d.ts +55 -2
- package/dist/server/service/database/database.service.d.ts.map +1 -1
- package/dist/server/service/database/database.service.js +246 -4
- package/dist/server/service/database/database.service.js.map +1 -1
- package/dist/server/service/database/database.service.ts +295 -7
- package/dist/server/service/database/mongodb.service.d.ts +0 -6
- package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
- package/dist/server/service/database/mongodb.service.js +0 -8
- package/dist/server/service/database/mongodb.service.js.map +1 -1
- package/dist/server/service/database/mongodb.service.ts +0 -8
- package/dist/server/service/database/mssql.service.d.ts +8 -4
- package/dist/server/service/database/mssql.service.d.ts.map +1 -1
- package/dist/server/service/database/mssql.service.js +27 -132
- package/dist/server/service/database/mssql.service.js.map +1 -1
- package/dist/server/service/database/mssql.service.ts +34 -156
- package/dist/server/service/database/mysql.service.d.ts +0 -4
- package/dist/server/service/database/mysql.service.d.ts.map +1 -1
- package/dist/server/service/database/mysql.service.js +11 -155
- package/dist/server/service/database/mysql.service.js.map +1 -1
- package/dist/server/service/database/mysql.service.ts +17 -184
- package/dist/server/service/database/oracle.service.d.ts +0 -4
- package/dist/server/service/database/oracle.service.d.ts.map +1 -1
- package/dist/server/service/database/oracle.service.js +11 -141
- package/dist/server/service/database/oracle.service.js.map +1 -1
- package/dist/server/service/database/oracle.service.ts +17 -166
- package/dist/server/service/database/postgres.service.d.ts +0 -4
- package/dist/server/service/database/postgres.service.d.ts.map +1 -1
- package/dist/server/service/database/postgres.service.js +11 -150
- package/dist/server/service/database/postgres.service.js.map +1 -1
- package/dist/server/service/database/postgres.service.ts +17 -174
- package/dist/server/service/database/sap.service.d.ts +0 -4
- package/dist/server/service/database/sap.service.d.ts.map +1 -1
- package/dist/server/service/database/sap.service.js +11 -137
- package/dist/server/service/database/sap.service.js.map +1 -1
- package/dist/server/service/database/sap.service.ts +11 -158
- package/dist/server/service/database/sql-type.helper.d.ts +28 -0
- package/dist/server/service/database/sql-type.helper.d.ts.map +1 -0
- package/dist/server/service/database/sql-type.helper.js +113 -0
- package/dist/server/service/database/sql-type.helper.js.map +1 -0
- package/dist/server/service/database/sql-type.helper.ts +115 -0
- package/dist/server/service/database/sqlite.service.d.ts +0 -5
- package/dist/server/service/database/sqlite.service.d.ts.map +1 -1
- package/dist/server/service/database/sqlite.service.js +11 -156
- package/dist/server/service/database/sqlite.service.js.map +1 -1
- package/dist/server/service/database/sqlite.service.ts +17 -183
- package/package.json +1 -1
- package/server.js +1 -1
package/dist/public/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./layout.js","./rolldown-runtime.js","./vue.js","./layout.css","./explorer.js","./explorer.css","./vconsole.min.js"])))=>i.map(i=>d[i]);
|
|
2
2
|
import { a as __toESM, n as __esmMin, r as __exportAll, t as __commonJSMin } from "./rolldown-runtime.js";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as markRaw, A as defineComponent, B as renderList, C as createBlock, F as nextTick, G as watch, H as resolveComponent, I as onBeforeUnmount, J as effectScope, L as onMounted, M as hasInjectionContext, O as createTextVNode, P as inject, Q as isRef, R as onUnmounted, S as createBaseVNode, T as createElementBlock, U as resolveDynamicComponent, V as renderSlot, W as useSlots, Y as getCurrentScope, Z as isReactive, a as init_vue_router, at as unref, b as Fragment, c as createI18n, ct as normalizeStyle, d as init_vue_runtime_esm_bundler, et as onScopeDispose, i as createWebHistory, it as toRefs, j as getCurrentInstance, k as createVNode, l as init_vue_i18n, lt as toDisplayString, n as useTitle, nt as ref, p as createApp, r as createRouter, rt as toRaw, st as normalizeClass, t as init_core, tt as reactive, u as useI18n, w as createCommentVNode, x as computed, y as withModifiers, z as openBlock } from "./vue.js";
|
|
4
4
|
import { i as init_bootstrap, n as init_bootstrap_esm, r as init_bootstrap_icons, t as Modal } from "./bootstrap.js";
|
|
5
5
|
//#region \0vite/modulepreload-polyfill.js
|
|
6
6
|
(function polyfill() {
|
|
@@ -4510,6 +4510,13 @@ function getVSCodeBridge() {
|
|
|
4510
4510
|
if (!bridgeInstance) bridgeInstance = new VSCodeBridge();
|
|
4511
4511
|
return bridgeInstance;
|
|
4512
4512
|
}
|
|
4513
|
+
/**
|
|
4514
|
+
* 判断是否在 VSCode 环境中
|
|
4515
|
+
*/
|
|
4516
|
+
function isVSCodeEnvironment() {
|
|
4517
|
+
if (!bridgeInstance) bridgeInstance = new VSCodeBridge();
|
|
4518
|
+
return bridgeInstance.isVSCodeEnvironment();
|
|
4519
|
+
}
|
|
4513
4520
|
var VSCodeBridge, bridgeInstance;
|
|
4514
4521
|
var init_bridge = __esmMin((() => {
|
|
4515
4522
|
VSCodeBridge = class {
|
|
@@ -4535,10 +4542,15 @@ var init_bridge = __esmMin((() => {
|
|
|
4535
4542
|
}
|
|
4536
4543
|
/**
|
|
4537
4544
|
* 监听来自 VSCode 扩展的消息
|
|
4545
|
+
*
|
|
4546
|
+
* 这里刻意不按 event.origin 过滤:VSCode 各版本经 webview.postMessage 下发时
|
|
4547
|
+
* 携带的 origin 并不稳定(部分版本为非空值),一旦过滤就会把宿主消息整批丢掉,
|
|
4548
|
+
* 表现为「连接下拉为空 / 数据库列表不展示」这类面板级静默失效。
|
|
4549
|
+
* rpc.ts 与 entry/explorer.ts 的监听同样不过滤,这里与之保持一致。
|
|
4550
|
+
* webview 内 CSP 已限定脚本来源、宿主是唯一消息来源,该过滤没有实际收益。
|
|
4538
4551
|
*/
|
|
4539
4552
|
onMessage(callback) {
|
|
4540
4553
|
window.addEventListener("message", (event) => {
|
|
4541
|
-
if (event.origin) return;
|
|
4542
4554
|
callback(event.data);
|
|
4543
4555
|
});
|
|
4544
4556
|
}
|
|
@@ -5079,6 +5091,12 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5079
5091
|
quickActions: "快速操作",
|
|
5080
5092
|
exportSchema: "导出架构",
|
|
5081
5093
|
viewLogs: "查看日志",
|
|
5094
|
+
schemaExported: "架构已导出({count} 个数据库){path}",
|
|
5095
|
+
schemaExportNoDatabase: "当前连接下没有可导出的数据库",
|
|
5096
|
+
schemaExportFailed: "架构导出失败",
|
|
5097
|
+
logsTitle: "数据库日志",
|
|
5098
|
+
logsEmpty: "没有可展示的日志记录",
|
|
5099
|
+
logsLoadFailed: "日志读取失败",
|
|
5082
5100
|
searchDatabase: "搜索数据库...",
|
|
5083
5101
|
loadingDatabases: "加载数据库列表...",
|
|
5084
5102
|
noDatabase: "暂无数据库",
|
|
@@ -5110,6 +5128,13 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5110
5128
|
basicInfo: "基本信息",
|
|
5111
5129
|
connectionConfig: "连接配置",
|
|
5112
5130
|
sqliteConfig: "SQLite配置",
|
|
5131
|
+
remoteConfig: "远程代理服务",
|
|
5132
|
+
remoteUrl: "服务地址",
|
|
5133
|
+
remoteUrlPlaceholder: "http://192.168.1.10:9800",
|
|
5134
|
+
remoteToken: "访问令牌(可选)",
|
|
5135
|
+
remoteTokenPlaceholder: "与服务端 DB_TOOL_REMOTE_TOKEN 一致",
|
|
5136
|
+
remoteConfigHint: "保存后,该远程服务上的所有数据库连接会自动出现在连接列表中,可像本地数据库一样直接访问。",
|
|
5137
|
+
remoteUrlRequired: "请填写远程服务地址",
|
|
5113
5138
|
authInfo: "认证信息",
|
|
5114
5139
|
otherOptions: "其他选项",
|
|
5115
5140
|
host: "主机地址",
|
|
@@ -5163,7 +5188,6 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5163
5188
|
refreshDatabase: "刷新数据库",
|
|
5164
5189
|
deleteDatabase: "删除数据库",
|
|
5165
5190
|
refreshTable: "刷新表",
|
|
5166
|
-
viewStructure: "查看结构",
|
|
5167
5191
|
defaultTitle: "数据库浏览器",
|
|
5168
5192
|
defaultDesc: "请从左侧选择一个连接、数据库或表来查看详细信息",
|
|
5169
5193
|
rowsSuffix: "行"
|
|
@@ -5220,6 +5244,7 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5220
5244
|
tableModifySuccess: "表结构修改成功",
|
|
5221
5245
|
tableCreateFailed: "创建表失败",
|
|
5222
5246
|
tableModifyFailed: "表结构修改失败",
|
|
5247
|
+
getTableInfoFailed: "获取表结构失败",
|
|
5223
5248
|
size: "大小"
|
|
5224
5249
|
},
|
|
5225
5250
|
tableDetail: {
|
|
@@ -5274,7 +5299,6 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5274
5299
|
operationFailed: "操作失败",
|
|
5275
5300
|
structureModifySuccess: "表结构修改成功",
|
|
5276
5301
|
structureModifyFailed: "表结构修改失败",
|
|
5277
|
-
confirmDeleteColumn: "确定要删除列 {name} 吗?",
|
|
5278
5302
|
confirmDeleteIndex: "确定要删除索引 {name} 吗?",
|
|
5279
5303
|
confirmDeleteFK: "确定要删除外键 {name} 吗?",
|
|
5280
5304
|
noInsertField: "没有可插入的字段",
|
|
@@ -5339,6 +5363,8 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5339
5363
|
operationsHeader: "操作",
|
|
5340
5364
|
columnNamePlaceholder: "字段名",
|
|
5341
5365
|
selectType: "选择类型",
|
|
5366
|
+
customType: "自定义类型",
|
|
5367
|
+
customTypePlaceholder: "如 ENUM('a','b')、DECIMAL(10,2)",
|
|
5342
5368
|
lengthPlaceholder: "长度",
|
|
5343
5369
|
precisionPlaceholder: "精度",
|
|
5344
5370
|
scalePlaceholder: "小数",
|
|
@@ -5599,7 +5625,8 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5599
5625
|
syncSuccess: "数据同步成功,{success}/{total} 个表同步完成,共同步 {rows} 行数据",
|
|
5600
5626
|
syncSuccessToast: "数据同步成功,{success}/{total} 个表同步完成",
|
|
5601
5627
|
loadTablesFailed: "加载表列表失败",
|
|
5602
|
-
|
|
5628
|
+
execFailed: "执行失败",
|
|
5629
|
+
selectConnectionFirst: "请先选择数据库连接",
|
|
5603
5630
|
featureInDev: "{feature}功能开发中...",
|
|
5604
5631
|
healthNormal: "正常",
|
|
5605
5632
|
healthCheckConnection: "连接状态",
|
|
@@ -5832,6 +5859,12 @@ var init_en_US = __esmMin((() => {
|
|
|
5832
5859
|
quickActions: "Quick Actions",
|
|
5833
5860
|
exportSchema: "Export Schema",
|
|
5834
5861
|
viewLogs: "View Logs",
|
|
5862
|
+
schemaExported: "Schema exported ({count} database(s)){path}",
|
|
5863
|
+
schemaExportNoDatabase: "No database available to export for this connection",
|
|
5864
|
+
schemaExportFailed: "Failed to export schema",
|
|
5865
|
+
logsTitle: "Database Logs",
|
|
5866
|
+
logsEmpty: "No log records to display",
|
|
5867
|
+
logsLoadFailed: "Failed to load logs",
|
|
5835
5868
|
searchDatabase: "Search databases...",
|
|
5836
5869
|
loadingDatabases: "Loading databases...",
|
|
5837
5870
|
noDatabase: "No databases yet",
|
|
@@ -5863,6 +5896,13 @@ var init_en_US = __esmMin((() => {
|
|
|
5863
5896
|
basicInfo: "Basic Information",
|
|
5864
5897
|
connectionConfig: "Connection Configuration",
|
|
5865
5898
|
sqliteConfig: "SQLite Configuration",
|
|
5899
|
+
remoteConfig: "Remote Proxy Service",
|
|
5900
|
+
remoteUrl: "Service URL",
|
|
5901
|
+
remoteUrlPlaceholder: "http://192.168.1.10:9800",
|
|
5902
|
+
remoteToken: "Access Token (optional)",
|
|
5903
|
+
remoteTokenPlaceholder: "Same as the server DB_TOOL_REMOTE_TOKEN",
|
|
5904
|
+
remoteConfigHint: "After saving, all database connections on that remote service appear in the connection list and can be accessed like local databases.",
|
|
5905
|
+
remoteUrlRequired: "Remote service URL is required",
|
|
5866
5906
|
authInfo: "Authentication",
|
|
5867
5907
|
otherOptions: "Other Options",
|
|
5868
5908
|
host: "Host",
|
|
@@ -5916,7 +5956,6 @@ var init_en_US = __esmMin((() => {
|
|
|
5916
5956
|
refreshDatabase: "Refresh Database",
|
|
5917
5957
|
deleteDatabase: "Delete Database",
|
|
5918
5958
|
refreshTable: "Refresh Table",
|
|
5919
|
-
viewStructure: "View Structure",
|
|
5920
5959
|
defaultTitle: "Database Explorer",
|
|
5921
5960
|
defaultDesc: "Select a connection, database, or table from the left panel to view details",
|
|
5922
5961
|
rowsSuffix: "rows"
|
|
@@ -5972,6 +6011,7 @@ var init_en_US = __esmMin((() => {
|
|
|
5972
6011
|
tableModifySuccess: "Table structure modified successfully",
|
|
5973
6012
|
tableCreateFailed: "Failed to create table",
|
|
5974
6013
|
tableModifyFailed: "Failed to modify table structure",
|
|
6014
|
+
getTableInfoFailed: "Failed to get table structure",
|
|
5975
6015
|
size: "Size"
|
|
5976
6016
|
},
|
|
5977
6017
|
tableDetail: {
|
|
@@ -6024,7 +6064,6 @@ var init_en_US = __esmMin((() => {
|
|
|
6024
6064
|
operationFailed: "Operation failed",
|
|
6025
6065
|
structureModifySuccess: "Table structure modified successfully",
|
|
6026
6066
|
structureModifyFailed: "Failed to modify table structure",
|
|
6027
|
-
confirmDeleteColumn: "Are you sure you want to delete column {name}?",
|
|
6028
6067
|
confirmDeleteIndex: "Are you sure you want to delete index {name}?",
|
|
6029
6068
|
confirmDeleteFK: "Are you sure you want to delete foreign key {name}?",
|
|
6030
6069
|
noInsertField: "No insertable fields",
|
|
@@ -6089,6 +6128,8 @@ var init_en_US = __esmMin((() => {
|
|
|
6089
6128
|
operationsHeader: "Actions",
|
|
6090
6129
|
columnNamePlaceholder: "Column name",
|
|
6091
6130
|
selectType: "Select type",
|
|
6131
|
+
customType: "Custom type",
|
|
6132
|
+
customTypePlaceholder: "e.g. ENUM('a','b'), DECIMAL(10,2)",
|
|
6092
6133
|
lengthPlaceholder: "Length",
|
|
6093
6134
|
precisionPlaceholder: "Precision",
|
|
6094
6135
|
scalePlaceholder: "Scale",
|
|
@@ -6349,7 +6390,8 @@ var init_en_US = __esmMin((() => {
|
|
|
6349
6390
|
syncSuccess: "Data sync successful, {success}/{total} tables synced, {rows} rows total",
|
|
6350
6391
|
syncSuccessToast: "Data sync successful, {success}/{total} tables synced",
|
|
6351
6392
|
loadTablesFailed: "Failed to load table list",
|
|
6352
|
-
|
|
6393
|
+
execFailed: "Execution failed",
|
|
6394
|
+
selectConnectionFirst: "Please select a database connection first",
|
|
6353
6395
|
featureInDev: "{feature} feature is under development...",
|
|
6354
6396
|
healthNormal: "Normal",
|
|
6355
6397
|
healthCheckConnection: "Connection Status",
|
|
@@ -6791,7 +6833,7 @@ var init_toast_vue_vue_type_script_setup_true_lang = __esmMin((() => {
|
|
|
6791
6833
|
}, [toast.title ? (openBlock(), createElementBlock("div", _hoisted_2$4, [createBaseVNode("strong", _hoisted_3$3, toDisplayString(toast.title), 1), createBaseVNode("button", {
|
|
6792
6834
|
type: "button",
|
|
6793
6835
|
class: "btn-close",
|
|
6794
|
-
onClick: ($event) => removeToast(
|
|
6836
|
+
onClick: ($event) => removeToast(toast)
|
|
6795
6837
|
}, null, 8, _hoisted_4$2)])) : createCommentVNode("", true), createBaseVNode("div", _hoisted_5$2, toDisplayString(toast.message), 1)], 2);
|
|
6796
6838
|
}), 128))]);
|
|
6797
6839
|
};
|
|
@@ -6799,8 +6841,8 @@ var init_toast_vue_vue_type_script_setup_true_lang = __esmMin((() => {
|
|
|
6799
6841
|
});
|
|
6800
6842
|
}));
|
|
6801
6843
|
//#endregion
|
|
6802
|
-
//#region src/components/toast/toast.vue?vue&type=style&index=0&scoped=
|
|
6803
|
-
var
|
|
6844
|
+
//#region src/components/toast/toast.vue?vue&type=style&index=0&scoped=daf8ceb9&lang.css
|
|
6845
|
+
var init_toast_vue_vue_type_style_index_0_scoped_daf8ceb9_lang = __esmMin((() => {}));
|
|
6804
6846
|
//#endregion
|
|
6805
6847
|
//#region \0plugin-vue:export-helper
|
|
6806
6848
|
var _plugin_vue_export_helper_default;
|
|
@@ -6817,9 +6859,9 @@ var toast_default$1;
|
|
|
6817
6859
|
var init_toast$2 = __esmMin((() => {
|
|
6818
6860
|
init_toast_vue_vue_type_script_setup_true_lang();
|
|
6819
6861
|
init_toast_vue_vue_type_script_setup_true_lang();
|
|
6820
|
-
|
|
6862
|
+
init_toast_vue_vue_type_style_index_0_scoped_daf8ceb9_lang();
|
|
6821
6863
|
init__plugin_vue_export_helper();
|
|
6822
|
-
toast_default$1 = /*#__PURE__*/ _plugin_vue_export_helper_default(toast_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
6864
|
+
toast_default$1 = /*#__PURE__*/ _plugin_vue_export_helper_default(toast_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-daf8ceb9"]]);
|
|
6823
6865
|
}));
|
|
6824
6866
|
//#endregion
|
|
6825
6867
|
//#region src/utils/toast.ts
|
|
@@ -6946,7 +6988,7 @@ var init_loading = __esmMin((() => {
|
|
|
6946
6988
|
}));
|
|
6947
6989
|
//#endregion
|
|
6948
6990
|
//#region src/components/modal/index.vue?vue&type=script&setup=true&lang.ts
|
|
6949
|
-
var _hoisted_1$2, _hoisted_2$2, _hoisted_3$2, _hoisted_4$1, _hoisted_5$1, _hoisted_6$1, _hoisted_7$1, _hoisted_8$1,
|
|
6991
|
+
var _hoisted_1$2, _hoisted_2$2, _hoisted_3$2, _hoisted_4$1, _hoisted_5$1, _hoisted_6$1, _hoisted_7$1, _hoisted_8$1, index_vue_vue_type_script_setup_true_lang_default$1;
|
|
6950
6992
|
var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
6951
6993
|
init_vue_runtime_esm_bundler();
|
|
6952
6994
|
init_vue_i18n();
|
|
@@ -6961,18 +7003,17 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
6961
7003
|
class: "modal-body text-wrap"
|
|
6962
7004
|
};
|
|
6963
7005
|
_hoisted_3$2 = { key: 1 };
|
|
6964
|
-
_hoisted_4$1 = { class: "
|
|
6965
|
-
_hoisted_5$1 = {
|
|
6966
|
-
_hoisted_6$1 = {
|
|
7006
|
+
_hoisted_4$1 = { class: "flex-grow-1 text-wrap" };
|
|
7007
|
+
_hoisted_5$1 = {
|
|
6967
7008
|
key: 0,
|
|
6968
7009
|
class: "error-details-toggle mt-2"
|
|
6969
7010
|
};
|
|
6970
|
-
|
|
7011
|
+
_hoisted_6$1 = {
|
|
6971
7012
|
key: 0,
|
|
6972
7013
|
class: "error-details-content mt-2"
|
|
6973
7014
|
};
|
|
6974
|
-
|
|
6975
|
-
|
|
7015
|
+
_hoisted_7$1 = { class: "error-json" };
|
|
7016
|
+
_hoisted_8$1 = { class: "modal-footer" };
|
|
6976
7017
|
index_vue_vue_type_script_setup_true_lang_default$1 = /*@__PURE__*/ defineComponent({
|
|
6977
7018
|
__name: "index",
|
|
6978
7019
|
props: {
|
|
@@ -7038,23 +7079,17 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
7038
7079
|
const dynamicShowCancel = ref(false);
|
|
7039
7080
|
const detailsExpanded = ref(false);
|
|
7040
7081
|
const dynamicErrorDetails = ref(null);
|
|
7041
|
-
const
|
|
7042
|
-
const
|
|
7043
|
-
isMobile.value = window.innerWidth < 768;
|
|
7044
|
-
};
|
|
7045
|
-
checkScreenSize();
|
|
7046
|
-
window.addEventListener("resize", checkScreenSize);
|
|
7047
|
-
onUnmounted(() => {
|
|
7048
|
-
window.removeEventListener("resize", checkScreenSize);
|
|
7049
|
-
});
|
|
7082
|
+
const slots = useSlots();
|
|
7083
|
+
const isAutoSize = computed(() => !slots.default && !props.isFullScreen);
|
|
7050
7084
|
const dynamicStyle = computed(() => {
|
|
7051
7085
|
return {
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7086
|
+
...isAutoSize.value ? {} : {
|
|
7087
|
+
width: "auto",
|
|
7088
|
+
minWidth: "300px"
|
|
7089
|
+
},
|
|
7055
7090
|
wordWrap: "break-word",
|
|
7056
7091
|
overflowWrap: "break-word",
|
|
7057
|
-
zIndex: 1060,
|
|
7092
|
+
zIndex: "1060",
|
|
7058
7093
|
...Object.keys(props.style).length ? props.style : {},
|
|
7059
7094
|
...dynamicOptions.value?.style || {}
|
|
7060
7095
|
};
|
|
@@ -7192,6 +7227,22 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
7192
7227
|
}
|
|
7193
7228
|
return Promise.resolve();
|
|
7194
7229
|
}
|
|
7230
|
+
/**
|
|
7231
|
+
* 强制收起弹窗(兜底)。
|
|
7232
|
+
* Bootstrap 的 hide() 依赖自身 _isShown / 过渡回调,在个别宿主环境(VSCode webview)
|
|
7233
|
+
* 里可能出现「点了关闭但状态没翻转」的情况,此时直接摘掉 show 类并移除遮罩,
|
|
7234
|
+
* 保证关闭按钮一定生效。
|
|
7235
|
+
*/
|
|
7236
|
+
function forceHide() {
|
|
7237
|
+
const el = modalContainer.value;
|
|
7238
|
+
if (!el) return false;
|
|
7239
|
+
if (!(el.classList.contains("show") || getComputedStyle(el).display !== "none")) return false;
|
|
7240
|
+
el.classList.remove("show", "fade");
|
|
7241
|
+
el.style.display = "none";
|
|
7242
|
+
document.querySelectorAll(".modal-backdrop").forEach((b) => b.remove());
|
|
7243
|
+
ModalHide();
|
|
7244
|
+
return true;
|
|
7245
|
+
}
|
|
7195
7246
|
function hide() {
|
|
7196
7247
|
if (isHiding.value) {
|
|
7197
7248
|
console.log("Modal already hiding, skipping");
|
|
@@ -7209,6 +7260,9 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
7209
7260
|
};
|
|
7210
7261
|
modalInstance.hide();
|
|
7211
7262
|
setTimeout(checkHidden, 100);
|
|
7263
|
+
setTimeout(() => {
|
|
7264
|
+
if (forceHide()) resolve();
|
|
7265
|
+
}, 800);
|
|
7212
7266
|
});
|
|
7213
7267
|
}
|
|
7214
7268
|
return Promise.resolve();
|
|
@@ -7270,16 +7324,16 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
7270
7324
|
tabindex: "-1",
|
|
7271
7325
|
"aria-labelledby": "modalLabel"
|
|
7272
7326
|
}, [createBaseVNode("div", {
|
|
7273
|
-
class: normalizeClass(["modal-dialog modal-dialog-centered", {
|
|
7327
|
+
class: normalizeClass(["modal-dialog modal-dialog-centered", {
|
|
7328
|
+
"modal-fullscreen": __props.isFullScreen,
|
|
7329
|
+
"modal-dialog-auto": isAutoSize.value
|
|
7330
|
+
}]),
|
|
7274
7331
|
style: normalizeStyle(dynamicStyle.value)
|
|
7275
7332
|
}, [createBaseVNode("div", {
|
|
7276
7333
|
class: normalizeClass(["modal-content", contentClass.value]),
|
|
7277
7334
|
style: { "margin": "auto" }
|
|
7278
7335
|
}, [
|
|
7279
|
-
createBaseVNode("div", {
|
|
7280
|
-
class: normalizeClass(["modal-header", headerClass.value]),
|
|
7281
|
-
style: { "padding": "1rem 1.5rem" }
|
|
7282
|
-
}, [createBaseVNode("h5", _hoisted_1$2, [typeIcon.value ? (openBlock(), createElementBlock("i", {
|
|
7336
|
+
createBaseVNode("div", { class: normalizeClass(["modal-header", headerClass.value]) }, [createBaseVNode("h5", _hoisted_1$2, [typeIcon.value ? (openBlock(), createElementBlock("i", {
|
|
7283
7337
|
key: 0,
|
|
7284
7338
|
class: normalizeClass([typeIcon.value, "me-2"])
|
|
7285
7339
|
}, null, 2)) : createCommentVNode("", true), createTextVNode(" " + toDisplayString(dynamicTitle.value || props.title), 1)]), createBaseVNode("button", {
|
|
@@ -7287,15 +7341,15 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
7287
7341
|
class: "btn-close",
|
|
7288
7342
|
"aria-label": "Close",
|
|
7289
7343
|
onClick: cancel
|
|
7290
|
-
})], 2),
|
|
7291
|
-
modalShow.value ? (openBlock(), createElementBlock("div", _hoisted_2$2, [_ctx.$slots.default ? renderSlot(_ctx.$slots, "default", { key: 0 }, void 0, true) : (openBlock(), createElementBlock("div", _hoisted_3$2, [createBaseVNode("div",
|
|
7344
|
+
}, [..._cache[0] || (_cache[0] = [createBaseVNode("i", { class: "bi bi-x-lg" }, null, -1)])])], 2),
|
|
7345
|
+
modalShow.value ? (openBlock(), createElementBlock("div", _hoisted_2$2, [_ctx.$slots.default ? renderSlot(_ctx.$slots, "default", { key: 0 }, void 0, true) : (openBlock(), createElementBlock("div", _hoisted_3$2, [createBaseVNode("div", { class: normalizeClass(["d-flex align-items-center", shouldShowDetails() ? "mb-2" : "mb-0"]) }, [typeIcon.value ? (openBlock(), createElementBlock("i", {
|
|
7292
7346
|
key: 0,
|
|
7293
|
-
class: normalizeClass([typeIcon.value, "me-3
|
|
7294
|
-
}, null, 2)) : createCommentVNode("", true), createBaseVNode("div",
|
|
7347
|
+
class: normalizeClass([typeIcon.value, "me-3 modal-type-icon"])
|
|
7348
|
+
}, null, 2)) : createCommentVNode("", true), createBaseVNode("div", _hoisted_4$1, toDisplayString(dynamicContent.value), 1)], 2), shouldShowDetails() ? (openBlock(), createElementBlock("div", _hoisted_5$1, [createBaseVNode("button", {
|
|
7295
7349
|
class: "btn btn-sm btn-outline-secondary",
|
|
7296
7350
|
onClick: toggleDetails
|
|
7297
|
-
}, [createTextVNode(toDisplayString(detailsExpanded.value ? _ctx.$t("common.hide") : _ctx.$t("common.details")) + " ", 1), _cache[
|
|
7298
|
-
createBaseVNode("div",
|
|
7351
|
+
}, [createTextVNode(toDisplayString(detailsExpanded.value ? _ctx.$t("common.hide") : _ctx.$t("common.details")) + " ", 1), _cache[1] || (_cache[1] = createBaseVNode("i", { class: "bi bi-chevron-{{detailsExpanded ? 'up' : 'down'}} ms-1" }, null, -1))]), detailsExpanded.value ? (openBlock(), createElementBlock("div", _hoisted_6$1, [createBaseVNode("pre", _hoisted_7$1, toDisplayString(formatErrorDetails()), 1)])) : createCommentVNode("", true)])) : createCommentVNode("", true)]))])) : createCommentVNode("", true),
|
|
7352
|
+
createBaseVNode("div", _hoisted_8$1, [_ctx.$slots.footer ? renderSlot(_ctx.$slots, "footer", { key: 0 }, void 0, true) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [dynamicShowCancel.value || props.closeButton.show ? (openBlock(), createElementBlock("button", {
|
|
7299
7353
|
key: 0,
|
|
7300
7354
|
type: "button",
|
|
7301
7355
|
class: "btn btn-secondary",
|
|
@@ -7316,17 +7370,17 @@ var init_index_vue_vue_type_script_setup_true_lang$1 = __esmMin((() => {
|
|
|
7316
7370
|
});
|
|
7317
7371
|
}));
|
|
7318
7372
|
//#endregion
|
|
7319
|
-
//#region src/components/modal/index.vue?vue&type=style&index=0&scoped=
|
|
7320
|
-
var
|
|
7373
|
+
//#region src/components/modal/index.vue?vue&type=style&index=0&scoped=8c11135e&lang.css
|
|
7374
|
+
var init_index_vue_vue_type_style_index_0_scoped_8c11135e_lang = __esmMin((() => {}));
|
|
7321
7375
|
//#endregion
|
|
7322
7376
|
//#region src/components/modal/index.vue
|
|
7323
7377
|
var modal_default$1;
|
|
7324
7378
|
var init_modal$1 = __esmMin((() => {
|
|
7325
7379
|
init_index_vue_vue_type_script_setup_true_lang$1();
|
|
7326
7380
|
init_index_vue_vue_type_script_setup_true_lang$1();
|
|
7327
|
-
|
|
7381
|
+
init_index_vue_vue_type_style_index_0_scoped_8c11135e_lang();
|
|
7328
7382
|
init__plugin_vue_export_helper();
|
|
7329
|
-
modal_default$1 = /*#__PURE__*/ _plugin_vue_export_helper_default(index_vue_vue_type_script_setup_true_lang_default$1, [["__scopeId", "data-v-
|
|
7383
|
+
modal_default$1 = /*#__PURE__*/ _plugin_vue_export_helper_default(index_vue_vue_type_script_setup_true_lang_default$1, [["__scopeId", "data-v-8c11135e"]]);
|
|
7330
7384
|
}));
|
|
7331
7385
|
//#endregion
|
|
7332
7386
|
//#region src/components/dataGrid/pagination.vue?vue&type=script&setup=true&lang.ts
|
|
@@ -7770,4 +7824,4 @@ var init_modal = __esmMin((() => {
|
|
|
7770
7824
|
app.mount("#app");
|
|
7771
7825
|
})))();
|
|
7772
7826
|
//#endregion
|
|
7773
|
-
export {
|
|
7827
|
+
export { isVSCodeEnvironment as A, init_rpc as C, vscodeSaveFile as D, vscodePickSaveFolder as E, isVSCode as M, defineStore as N, getVSCodeBridge as O, init_pinia as P, blobToBase64 as S, vscodeNotifyExported as T, init__plugin_vue_export_helper as _, dataGrid_default as a, init_base as b, pagination_default as c, init_loading as d, init_toast$1 as f, _plugin_vue_export_helper_default as g, toast_default$1 as h, showConfirm as i, init_detect as j, init_bridge as k, init_modal$1 as l, init_toast$2 as m, init_modal as n, init_dataGrid as o, toast as p, showAlert as r, init_pagination as s, getModalInstance as t, modal_default$1 as u, getT as v, isVscodeWebview as w, request as x, init_i18n as y };
|
package/dist/public/layout.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __esmMin } from "./rolldown-runtime.js";
|
|
2
|
-
import { A as defineComponent, B as renderList, G as
|
|
3
|
-
import {
|
|
2
|
+
import { A as defineComponent, B as renderList, G as watch, H as resolveComponent, K as withCtx, O as createTextVNode, S as createBaseVNode, T as createElementBlock, at as unref, b as Fragment, d as init_vue_runtime_esm_bundler, f as Transition, k as createVNode, l as init_vue_i18n, lt as toDisplayString, u as useI18n, w as createCommentVNode, y as withModifiers, z as openBlock } from "./vue.js";
|
|
3
|
+
import { N as defineStore, P as init_pinia, _ as init__plugin_vue_export_helper, g as _plugin_vue_export_helper_default } from "./index.js";
|
|
4
4
|
//#region src/stores/locale.ts
|
|
5
5
|
var LOCALE_KEY, useLocaleStore;
|
|
6
6
|
var init_locale = __esmMin((() => {
|
|
@@ -230,7 +230,7 @@ var init_layout_vue_vue_type_script_setup_true_lang = __esmMin((() => {
|
|
|
230
230
|
setup(__props) {
|
|
231
231
|
const { t } = useI18n();
|
|
232
232
|
const aboutStore = useAboutStore();
|
|
233
|
-
const appVersion = "1.0.
|
|
233
|
+
const appVersion = "1.0.37";
|
|
234
234
|
return (_ctx, _cache) => {
|
|
235
235
|
const _component_router_link = resolveComponent("router-link");
|
|
236
236
|
const _component_router_view = resolveComponent("router-view");
|
package/dist/public/vue.js
CHANGED
|
@@ -12963,4 +12963,4 @@ var init_core = __esmMin((() => {
|
|
|
12963
12963
|
Number.POSITIVE_INFINITY;
|
|
12964
12964
|
}));
|
|
12965
12965
|
//#endregion
|
|
12966
|
-
export {
|
|
12966
|
+
export { markRaw as $, defineComponent as A, renderList as B, createBlock as C, createStaticVNode as D, createSlots as E, nextTick as F, watch as G, resolveComponent as H, onBeforeUnmount as I, effectScope as J, withCtx as K, onMounted as L, hasInjectionContext as M, init_runtime_core_esm_bundler as N, createTextVNode as O, inject as P, isRef as Q, onUnmounted as R, createBaseVNode as S, createElementBlock as T, resolveDynamicComponent as U, renderSlot as V, useSlots as W, init_reactivity_esm_bundler as X, getCurrentScope as Y, isReactive as Z, vModelText as _, init_vue_router as a, unref as at, Fragment as b, createI18n as c, normalizeStyle as ct, init_vue_runtime_esm_bundler as d, onScopeDispose as et, Transition as f, vModelSelect as g, vModelCheckbox as h, createWebHistory as i, toRefs as it, getCurrentInstance$1 as j, createVNode as k, init_vue_i18n as l, toDisplayString$1 as lt, init_runtime_dom_esm_bundler as m, useTitle as n, ref as nt, useRoute as o, init_shared_esm_bundler as ot, createApp as p, withDirectives as q, createRouter as r, toRaw as rt, useRouter as s, normalizeClass as st, init_core as t, reactive as tt, useI18n as u, vShow as v, createCommentVNode as w, computed as x, withModifiers as y, openBlock as z };
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
canonicalizeTableEntity,
|
|
4
|
+
canonicalizeTableSpec,
|
|
5
|
+
classifyDefault,
|
|
6
|
+
canonicalizeType,
|
|
7
|
+
diffColumnFields,
|
|
8
|
+
normalizeNumericLiteral
|
|
9
|
+
} from '../canonical';
|
|
10
|
+
import type { TableSpec } from '../../../model/ddl-spec';
|
|
11
|
+
|
|
12
|
+
describe('classifyDefault:三态 + 表达式分类(消灭 || 兜底吃掉假值)', () => {
|
|
13
|
+
it('0 / false / 空串是真实默认值,不得回落为 none', () => {
|
|
14
|
+
expect(classifyDefault(0)).toEqual({ kind: 'literal', text: '0' });
|
|
15
|
+
expect(classifyDefault('0')).toEqual({ kind: 'literal', text: '0' });
|
|
16
|
+
expect(classifyDefault(false)).toEqual({ kind: 'literal', text: 'false' });
|
|
17
|
+
expect(classifyDefault('')).toEqual({ kind: 'literal', text: '' }); // MySQL DEFAULT ''
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('无默认值 / DEFAULT NULL / 显式标记 三者可区分', () => {
|
|
21
|
+
expect(classifyDefault(undefined)).toEqual({ kind: 'none', text: '' });
|
|
22
|
+
expect(classifyDefault(null)).toEqual({ kind: 'none', text: '' });
|
|
23
|
+
expect(classifyDefault('NULL')).toEqual({ kind: 'null', text: '' });
|
|
24
|
+
expect(classifyDefault('null')).toEqual({ kind: 'null', text: '' });
|
|
25
|
+
expect(classifyDefault('x', false)).toEqual({ kind: 'none', text: '' }); // hasDefault=false 优先
|
|
26
|
+
expect(classifyDefault(undefined, true)).toEqual({ kind: 'literal', text: '' }); // DEFAULT ''(显式)
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('引号字面量与 PG 类型标注', () => {
|
|
30
|
+
expect(classifyDefault("'abc'")).toEqual({ kind: 'literal', text: 'abc' });
|
|
31
|
+
expect(classifyDefault("'0'::character varying")).toEqual({
|
|
32
|
+
kind: 'expression',
|
|
33
|
+
text: "'0'::CHARACTER VARYING"
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('表达式默认值:关键字与函数调用', () => {
|
|
38
|
+
expect(classifyDefault('CURRENT_TIMESTAMP')).toEqual({
|
|
39
|
+
kind: 'expression',
|
|
40
|
+
text: 'CURRENT_TIMESTAMP'
|
|
41
|
+
});
|
|
42
|
+
expect(classifyDefault('now()')).toEqual({ kind: 'expression', text: 'NOW()' });
|
|
43
|
+
expect(classifyDefault("nextval('s')")).toEqual({ kind: 'expression', text: "NEXTVAL('S')" });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('数值书写差异归一:007→7、0.50→0.5、-0→0', () => {
|
|
47
|
+
expect(normalizeNumericLiteral('007')).toBe('7');
|
|
48
|
+
expect(normalizeNumericLiteral('0.50')).toBe('0.5');
|
|
49
|
+
expect(normalizeNumericLiteral('-0')).toBe('0');
|
|
50
|
+
expect(classifyDefault('0.50')).toEqual({ kind: 'literal', text: '0.5' });
|
|
51
|
+
expect(diffColumnBetween('0.5', '0.50')).toBe(false); // 同一默认值
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('canonicalizeType:全串与拆分字段两种表示等价', () => {
|
|
56
|
+
it("实体 'varchar(255)' 与表单 {type:'VARCHAR', length:'255'} 等价", () => {
|
|
57
|
+
const a = canonicalizeType({ type: 'varchar(255)', length: 255 });
|
|
58
|
+
const b = canonicalizeType({ type: 'VARCHAR', length: '255' });
|
|
59
|
+
expect(a).toEqual(b);
|
|
60
|
+
expect(a.baseType).toBe('VARCHAR');
|
|
61
|
+
expect(a.length).toBe(255);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('DECIMAL(10,0):scale=0 必须保留(假值不丢弃)', () => {
|
|
65
|
+
const a = canonicalizeType({ type: 'decimal(10,0)' });
|
|
66
|
+
const b = canonicalizeType({ type: 'DECIMAL', precision: '10', scale: '0' });
|
|
67
|
+
expect(a).toEqual(b);
|
|
68
|
+
expect(a.precision).toBe(10);
|
|
69
|
+
expect(a.scale).toBe(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("单参数精度类型:'decimal(10)' 解析为 precision", () => {
|
|
73
|
+
expect(canonicalizeType({ type: 'decimal(10)' })).toEqual({
|
|
74
|
+
baseType: 'DECIMAL',
|
|
75
|
+
precision: 10,
|
|
76
|
+
scale: undefined
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("非数字参数(ENUM)整体原样保留", () => {
|
|
81
|
+
const a = canonicalizeType({ type: "enum('a','b')" });
|
|
82
|
+
const b = canonicalizeType({ type: 'ENUM', customType: "enum('a','b')" });
|
|
83
|
+
expect(a).toEqual(b);
|
|
84
|
+
expect(a.baseType).toBe("enum('a','b')");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('大小写与空格归一', () => {
|
|
88
|
+
expect(canonicalizeType({ type: ' varchar ' }).baseType).toBe('VARCHAR');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('canonicalizeTable*:跨表示等价与自增 clamp', () => {
|
|
93
|
+
const entity = {
|
|
94
|
+
name: 'users',
|
|
95
|
+
comment: '用户',
|
|
96
|
+
columns: [
|
|
97
|
+
{ name: 'id', type: 'int', nullable: false, isPrimary: true, isAutoIncrement: true, length: 11 },
|
|
98
|
+
{ name: 'name', type: 'varchar(64)', nullable: true, defaultValue: '', comment: ' 名字 ' },
|
|
99
|
+
{ name: 'amount', type: 'decimal(10,0)', nullable: true, defaultValue: 0 }
|
|
100
|
+
]
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** Phase E 前端应做的回显:canonical → TableSpec */
|
|
104
|
+
function echoSpec(): TableSpec {
|
|
105
|
+
const t = canonicalizeTableEntity(entity);
|
|
106
|
+
const mapDefault = (i: number): Pick<TableSpec['columns'][number], 'hasDefault' | 'defaultValue'> => {
|
|
107
|
+
const c = t.columns[i]!;
|
|
108
|
+
switch (c.defaultKind) {
|
|
109
|
+
case 'none': return { hasDefault: false };
|
|
110
|
+
case 'null': return { hasDefault: true, defaultValue: 'NULL' };
|
|
111
|
+
case 'literal': return { hasDefault: true, defaultValue: c.defaultText };
|
|
112
|
+
case 'expression': return { hasDefault: true, defaultValue: c.defaultText };
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const mk = (i: number, extra: Record<string, unknown> = {}) => {
|
|
116
|
+
const c = t.columns[i]!;
|
|
117
|
+
const full = c.baseType.includes('(');
|
|
118
|
+
return {
|
|
119
|
+
name: c.name,
|
|
120
|
+
...(full ? { type: c.baseType, customType: c.baseType } : { type: c.baseType, length: c.length, precision: c.precision, scale: c.scale }),
|
|
121
|
+
nullable: c.nullable,
|
|
122
|
+
...mapDefault(i),
|
|
123
|
+
comment: c.comment,
|
|
124
|
+
...extra
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
name: t.name,
|
|
129
|
+
comment: t.comment,
|
|
130
|
+
columns: [
|
|
131
|
+
mk(0, { autoIncrement: true }),
|
|
132
|
+
mk(1),
|
|
133
|
+
mk(2)
|
|
134
|
+
],
|
|
135
|
+
primaryKey: t.primaryKey
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
it('「实体 → canonical → 表单回显」全程零 diff(往返幂等的基石)', () => {
|
|
140
|
+
const before = canonicalizeTableEntity(entity);
|
|
141
|
+
const after = canonicalizeTableSpec(echoSpec());
|
|
142
|
+
expect(diffColumnFields(before.columns[0]!, after.columns[0]!)).toEqual([]);
|
|
143
|
+
expect(diffColumnFields(before.columns[1]!, after.columns[1]!)).toEqual([]);
|
|
144
|
+
expect(diffColumnFields(before.columns[2]!, after.columns[2]!)).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('rawAutoIncrement 不被 clamp 吞掉(供"加主键时恢复自增")', () => {
|
|
148
|
+
const t = canonicalizeTableEntity({
|
|
149
|
+
name: 't',
|
|
150
|
+
columns: [{ name: 'id', type: 'int', nullable: false, isAutoIncrement: true, isPrimary: false }]
|
|
151
|
+
});
|
|
152
|
+
expect(t.columns[0]!.rawAutoIncrement).toBe(true);
|
|
153
|
+
expect(t.columns[0]!.effectiveAutoIncrement).toBe(false); // 无主键 → 不生效
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('clamp 是两侧同一规则:非主键列勾自增不产生列级 diff', () => {
|
|
157
|
+
const before = canonicalizeTableEntity({
|
|
158
|
+
name: 't',
|
|
159
|
+
columns: [{ name: 'id', type: 'int', nullable: false, isPrimary: false, isAutoIncrement: false }]
|
|
160
|
+
});
|
|
161
|
+
const after = canonicalizeTableSpec({
|
|
162
|
+
name: 't',
|
|
163
|
+
primaryKey: [],
|
|
164
|
+
columns: [{ name: 'id', type: 'INT', nullable: false, hasDefault: false, autoIncrement: true }] // 勾了自增但非主键
|
|
165
|
+
});
|
|
166
|
+
expect(diffColumnFields(before.columns[0]!, after.columns[0]!)).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('注释 trim:两侧空格差异不算改动', () => {
|
|
170
|
+
const before = canonicalizeTableEntity({
|
|
171
|
+
name: 't',
|
|
172
|
+
columns: [{ name: 'a', type: 'int', nullable: true, comment: 'x' }]
|
|
173
|
+
});
|
|
174
|
+
const after = canonicalizeTableSpec({
|
|
175
|
+
name: 't',
|
|
176
|
+
primaryKey: [],
|
|
177
|
+
columns: [{ name: 'a', type: 'INT', nullable: true, hasDefault: false, comment: ' x ' }]
|
|
178
|
+
});
|
|
179
|
+
expect(diffColumnFields(before.columns[0]!, after.columns[0]!)).toEqual([]);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
function diffColumnBetween(aRaw: string, bRaw: string): boolean {
|
|
184
|
+
const col = (defaultValue: unknown) => ({
|
|
185
|
+
name: 'x', type: 'int', nullable: true, defaultValue, isPrimary: false
|
|
186
|
+
});
|
|
187
|
+
const before = canonicalizeTableEntity({ name: 't', columns: [col(aRaw)] }).columns[0]!;
|
|
188
|
+
const specCol = (defaultValue: string) => ({
|
|
189
|
+
name: 'x', type: 'INT', nullable: true, hasDefault: true, defaultValue
|
|
190
|
+
});
|
|
191
|
+
const after = canonicalizeTableSpec({
|
|
192
|
+
name: 't', primaryKey: [], columns: [specCol(bRaw)]
|
|
193
|
+
}).columns[0]!;
|
|
194
|
+
return diffColumnFields(before, after).includes('default');
|
|
195
|
+
}
|