fdb2 1.0.38 → 1.0.40
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 +8 -1
- package/bin/fdb2.js +78 -3
- package/dist/public/explorer.css +403 -380
- package/dist/public/explorer.js +1294 -737
- package/dist/public/index.js +242 -16
- package/dist/public/layout.js +1 -1
- package/dist/scripts/verify-db-support.cjs +239 -0
- package/dist/server/core/dialect.d.ts +9 -0
- package/dist/server/core/dialect.d.ts.map +1 -1
- package/dist/server/core/dialect.js +24 -9
- package/dist/server/core/dialect.js.map +1 -1
- package/dist/server/core/dialect.ts +29 -9
- package/dist/server/core/schema/__tests__/ddl-emitter.test.ts +7 -3
- package/dist/server/core/schema/ddl-emitter.d.ts.map +1 -1
- package/dist/server/core/schema/ddl-emitter.js +32 -12
- package/dist/server/core/schema/ddl-emitter.js.map +1 -1
- package/dist/server/core/schema/ddl-emitter.ts +34 -11
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +83 -6
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.ts +87 -6
- package/dist/server/model/ddl-spec.d.ts +9 -0
- package/dist/server/model/ddl-spec.d.ts.map +1 -1
- package/dist/server/model/ddl-spec.ts +9 -0
- package/dist/server/service/connection.service.d.ts +8 -0
- package/dist/server/service/connection.service.d.ts.map +1 -1
- package/dist/server/service/connection.service.js +75 -17
- package/dist/server/service/connection.service.js.map +1 -1
- package/dist/server/service/connection.service.ts +74 -17
- package/dist/server/service/database/base.service.d.ts +45 -0
- package/dist/server/service/database/base.service.d.ts.map +1 -1
- package/dist/server/service/database/base.service.js +120 -7
- package/dist/server/service/database/base.service.js.map +1 -1
- package/dist/server/service/database/base.service.ts +143 -8
- package/dist/server/service/database/cockroachdb.service.d.ts +9 -0
- package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
- package/dist/server/service/database/cockroachdb.service.js +67 -56
- package/dist/server/service/database/cockroachdb.service.js.map +1 -1
- package/dist/server/service/database/cockroachdb.service.ts +66 -57
- package/dist/server/service/database/database.service.d.ts +3 -221
- package/dist/server/service/database/database.service.d.ts.map +1 -1
- package/dist/server/service/database/database.service.js +66 -24
- package/dist/server/service/database/database.service.js.map +1 -1
- package/dist/server/service/database/database.service.ts +69 -23
- package/dist/server/service/database/mongodb.service.d.ts +58 -3
- package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
- package/dist/server/service/database/mongodb.service.js +251 -54
- package/dist/server/service/database/mongodb.service.js.map +1 -1
- package/dist/server/service/database/mongodb.service.ts +275 -60
- package/dist/server/service/database/mssql.service.d.ts +8 -0
- package/dist/server/service/database/mssql.service.d.ts.map +1 -1
- package/dist/server/service/database/mssql.service.js +41 -36
- package/dist/server/service/database/mssql.service.js.map +1 -1
- package/dist/server/service/database/mssql.service.ts +45 -40
- package/dist/server/service/database/oracle.service.d.ts +24 -0
- package/dist/server/service/database/oracle.service.d.ts.map +1 -1
- package/dist/server/service/database/oracle.service.js +146 -106
- package/dist/server/service/database/oracle.service.js.map +1 -1
- package/dist/server/service/database/oracle.service.ts +145 -105
- package/dist/server/service/database/sqlite.service.js +1 -1
- package/dist/server/service/database/sqlite.service.js.map +1 -1
- package/dist/server/service/database/sqlite.service.ts +1 -1
- package/package.json +8 -8
- package/scripts/verify-db-support.cjs +239 -0
package/dist/public/index.js
CHANGED
|
@@ -4982,6 +4982,7 @@ var init_zh_CN = __esmMin((() => {
|
|
|
4982
4982
|
success: "成功",
|
|
4983
4983
|
error: "错误",
|
|
4984
4984
|
warning: "警告",
|
|
4985
|
+
tip: "提示",
|
|
4985
4986
|
close: "关闭",
|
|
4986
4987
|
back: "返回",
|
|
4987
4988
|
export: "导出",
|
|
@@ -5129,6 +5130,7 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5129
5130
|
connectionConfig: "连接配置",
|
|
5130
5131
|
sqliteConfig: "SQLite配置",
|
|
5131
5132
|
remoteConfig: "远程代理服务",
|
|
5133
|
+
typeRemote: "远程代理服务",
|
|
5132
5134
|
remoteUrl: "服务地址",
|
|
5133
5135
|
remoteUrlPlaceholder: "http://192.168.1.10:9800",
|
|
5134
5136
|
remoteToken: "访问令牌(可选)",
|
|
@@ -5190,10 +5192,52 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5190
5192
|
refreshTable: "刷新表",
|
|
5191
5193
|
defaultTitle: "数据库浏览器",
|
|
5192
5194
|
defaultDesc: "请从左侧选择一个连接、数据库或表来查看详细信息",
|
|
5193
|
-
rowsSuffix: "行"
|
|
5195
|
+
rowsSuffix: "行",
|
|
5196
|
+
docsSuffix: "个文档",
|
|
5197
|
+
loadConnectionsFailed: "加载连接失败",
|
|
5198
|
+
loadDatabasesFailed: "加载数据库失败",
|
|
5199
|
+
loadTablesFailed: "加载表失败",
|
|
5200
|
+
loadDatabaseInfoFailed: "加载数据库信息失败",
|
|
5201
|
+
loadingTableData: "正在加载表 \"{name}\" 的数据...",
|
|
5202
|
+
loadTableDataFailed: "加载表数据失败",
|
|
5203
|
+
loadTableStructureFailed: "加载表结构失败",
|
|
5204
|
+
testConnectionSuccess: "连接 \"{name}\" 测试成功",
|
|
5205
|
+
testConnectionFailed: "连接 \"{name}\" 测试失败",
|
|
5206
|
+
testConnectionError: "连接测试失败",
|
|
5207
|
+
sqlQueryInDev: "SQL查询功能开发中...",
|
|
5208
|
+
tableStructureRefreshed: "表结构已刷新",
|
|
5209
|
+
featureInDev: "新增功能开发中...",
|
|
5210
|
+
connectionRefreshed: "连接 \"{name}\" 已刷新",
|
|
5211
|
+
databaseRefreshed: "数据库 \"{name}\" 已刷新",
|
|
5212
|
+
confirmDeleteDatabase: "确定要删除数据库 \"{name}\" 吗?此操作将删除数据库及其所有数据且不可恢复。",
|
|
5213
|
+
deletingDatabase: "正在删除数据库 \"{name}\"...",
|
|
5214
|
+
databaseDeleted: "数据库 \"{name}\" 已删除",
|
|
5215
|
+
deleteDatabaseFailed: "删除数据库失败",
|
|
5216
|
+
confirmDeleteConnection: "确定要删除连接 \"{name}\" 吗?此操作将删除该连接的配置但不会删除实际的数据库数据。",
|
|
5217
|
+
deletingConnection: "正在删除连接 \"{name}\"...",
|
|
5218
|
+
connectionDeleted: "连接 \"{name}\" 已删除",
|
|
5219
|
+
deleteConnectionFailed: "删除连接失败",
|
|
5220
|
+
tableRefreshed: "表 \"{name}\" 已刷新",
|
|
5221
|
+
creatingTable: "正在创建表 \"{name}\"...",
|
|
5222
|
+
tableCreated: "表 \"{name}\" 创建成功",
|
|
5223
|
+
createTableFailed: "创建表失败",
|
|
5224
|
+
insertDataInDev: "插入数据功能开发中...",
|
|
5225
|
+
exportTableInDev: "导出表功能开发中...",
|
|
5226
|
+
confirmTruncateTable: "确定要清空表 \"{name}\" 吗?此操作将删除所有数据且不可恢复。",
|
|
5227
|
+
truncatingTable: "正在清空表 \"{name}\"...",
|
|
5228
|
+
tableTruncated: "表 \"{name}\" 已清空",
|
|
5229
|
+
truncateTableFailed: "清空表失败",
|
|
5230
|
+
editRowInDev: "编辑行功能开发中...",
|
|
5231
|
+
deleteRowInDev: "删除行功能开发中...",
|
|
5232
|
+
selectConnectionFirst: "请先选择数据库连接",
|
|
5233
|
+
sqlCannotBeEmpty: "SQL语句不能为空",
|
|
5234
|
+
sqlExecuted: "SQL执行成功",
|
|
5235
|
+
sqlAffectedRows: "执行成功,影响行数: {count}",
|
|
5236
|
+
sqlExecuteFailed: "SQL执行失败"
|
|
5194
5237
|
},
|
|
5195
5238
|
databaseDetail: {
|
|
5196
5239
|
tables: "数据表",
|
|
5240
|
+
collections: "集合",
|
|
5197
5241
|
backToConnection: "返回连接概览",
|
|
5198
5242
|
views: "视图",
|
|
5199
5243
|
procedures: "存储过程",
|
|
@@ -5254,6 +5298,18 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5254
5298
|
backToDatabase: "返回数据库视图",
|
|
5255
5299
|
modifyStructure: "修改表结构",
|
|
5256
5300
|
insertData: "插入数据",
|
|
5301
|
+
insertDocument: "新增文档",
|
|
5302
|
+
createIndex: "新建索引",
|
|
5303
|
+
indexKeysLabel: "索引键(JSON)",
|
|
5304
|
+
indexNameOptional: "索引名(可选)",
|
|
5305
|
+
indexKeysInvalidJson: "JSON 格式错误:{message}",
|
|
5306
|
+
indexKeysMustBeObject: "索引键必须是 JSON 对象,例如 {\"name\": 1}",
|
|
5307
|
+
indexCreated: "索引创建成功",
|
|
5308
|
+
indexCreateFailed: "创建索引失败",
|
|
5309
|
+
primaryIndexUndeletable: "主键索引(_id_)不可删除",
|
|
5310
|
+
indexDeleted: "索引已删除",
|
|
5311
|
+
indexDeleteFailed: "删除索引失败",
|
|
5312
|
+
docMissingId: "该文档缺少 _id,无法删除",
|
|
5257
5313
|
exportMenu: "导出",
|
|
5258
5314
|
exportCSV: "导出 CSV",
|
|
5259
5315
|
exportJSON: "导出 JSON",
|
|
@@ -5328,6 +5384,12 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5328
5384
|
dataEditor: {
|
|
5329
5385
|
editData: "编辑数据",
|
|
5330
5386
|
addData: "新增数据",
|
|
5387
|
+
editDocHint: "编辑该文档",
|
|
5388
|
+
newDocHint: "新建文档(JSON 对象)",
|
|
5389
|
+
docSaveEditHint: "保存时按 _id 定位该文档",
|
|
5390
|
+
docSaveInsertHint: "保存时插入为新文档,_id 由数据库生成",
|
|
5391
|
+
docJsonInvalid: "JSON 格式错误:{message}",
|
|
5392
|
+
docJsonRequired: "请提供一个 JSON 对象(例如 {\"name\":\"alice\"})",
|
|
5331
5393
|
autoGenerate: "自动生成",
|
|
5332
5394
|
inputPlaceholder: "请输入{name}",
|
|
5333
5395
|
selectPlaceholder: "请选择...",
|
|
@@ -5350,6 +5412,26 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5350
5412
|
tableNamePlaceholder: "请输入表名",
|
|
5351
5413
|
tableCommentLabel: "表注释",
|
|
5352
5414
|
tableCommentPlaceholder: "请输入表注释",
|
|
5415
|
+
tableCommentUnsupported: "该数据库不支持表注释",
|
|
5416
|
+
schemalessHint: "该数据库为无模式类型(MongoDB),将创建一个<strong>空集合</strong>,无需定义字段。",
|
|
5417
|
+
refreshTableList: "刷新表列表",
|
|
5418
|
+
refreshing: "刷新中...",
|
|
5419
|
+
refreshTableListTip: "重新拉取表列表(重名检测等依赖它;预览默认走 30 秒缓存)",
|
|
5420
|
+
previewNeedColumns: "-- 请完善字段信息(列名与类型)后再查看 SQL",
|
|
5421
|
+
previewCreatePlaceholder: "-- 完善表单后将在此显示建表 SQL",
|
|
5422
|
+
previewCreateSqlPlaceholder: "-- 完善表单后将在此显示后端生成的 SQL",
|
|
5423
|
+
previewNoChanges: "没有检测到任何修改",
|
|
5424
|
+
previewFailed: "预览失败: {message}",
|
|
5425
|
+
liveSchemaMissing: "未读取到实时表结构,请刷新表列表后重试。",
|
|
5426
|
+
liveColumnsUnknown: "(未知)",
|
|
5427
|
+
liveColumnsExist: "当前实时表已有列:{columns}\n若你以为新增的字段出现在上面,说明它之前已保存成功,或本来就存在。",
|
|
5428
|
+
createCollectionSuccess: "集合创建成功",
|
|
5429
|
+
createTableSuccess: "表创建成功",
|
|
5430
|
+
alterTableSuccess: "表结构修改成功",
|
|
5431
|
+
refreshTableListFailed: "刷新表列表失败: {message}",
|
|
5432
|
+
tableNameRequired: "请输入表名",
|
|
5433
|
+
columnsIncomplete: "请完善所有字段信息",
|
|
5434
|
+
connectionRequired: "请先选择数据库连接",
|
|
5353
5435
|
columnManagement: "字段管理",
|
|
5354
5436
|
addColumn: "添加字段",
|
|
5355
5437
|
columnNameHeader: "字段名",
|
|
@@ -5381,8 +5463,25 @@ var init_zh_CN = __esmMin((() => {
|
|
|
5381
5463
|
catSpatial: "空间类型",
|
|
5382
5464
|
catOther: "其他类型"
|
|
5383
5465
|
},
|
|
5466
|
+
documentQuery: {
|
|
5467
|
+
filterTab: "过滤查询",
|
|
5468
|
+
aggregateTab: "聚合管道",
|
|
5469
|
+
sortPlaceholder: "排序,如 {\"age\":-1}",
|
|
5470
|
+
execute: "执行",
|
|
5471
|
+
executing: "执行中...",
|
|
5472
|
+
totalLabel: "共 {count} 条",
|
|
5473
|
+
filterLabel: "过滤条件(JSON)",
|
|
5474
|
+
aggregateLabel: "聚合管道(JSON 数组)",
|
|
5475
|
+
filterHint: "留空表示不过滤",
|
|
5476
|
+
aggregateHint: "MongoDB 聚合管道,例如 $match / $group / $sort",
|
|
5477
|
+
filterMustBeObject: "过滤条件必须是 JSON 对象,例如 {\"status\":\"active\"}",
|
|
5478
|
+
aggregateMustBeArray: "聚合管道必须是 JSON 数组,例如 [{ \"$match\": {} }]",
|
|
5479
|
+
result: "结果",
|
|
5480
|
+
noResult: "暂无结果"
|
|
5481
|
+
},
|
|
5384
5482
|
importExport: {
|
|
5385
5483
|
title: "数据导入导出",
|
|
5484
|
+
unknownError: "未知错误",
|
|
5386
5485
|
dataImport: "数据导入",
|
|
5387
5486
|
dataExport: "数据导出",
|
|
5388
5487
|
stepSelectFile: "选择文件",
|
|
@@ -5750,6 +5849,7 @@ var init_en_US = __esmMin((() => {
|
|
|
5750
5849
|
success: "Success",
|
|
5751
5850
|
error: "Error",
|
|
5752
5851
|
warning: "Warning",
|
|
5852
|
+
tip: "Tip",
|
|
5753
5853
|
close: "Close",
|
|
5754
5854
|
back: "Back",
|
|
5755
5855
|
export: "Export",
|
|
@@ -5897,6 +5997,7 @@ var init_en_US = __esmMin((() => {
|
|
|
5897
5997
|
connectionConfig: "Connection Configuration",
|
|
5898
5998
|
sqliteConfig: "SQLite Configuration",
|
|
5899
5999
|
remoteConfig: "Remote Proxy Service",
|
|
6000
|
+
typeRemote: "Remote Proxy Service",
|
|
5900
6001
|
remoteUrl: "Service URL",
|
|
5901
6002
|
remoteUrlPlaceholder: "http://192.168.1.10:9800",
|
|
5902
6003
|
remoteToken: "Access Token (optional)",
|
|
@@ -5958,10 +6059,52 @@ var init_en_US = __esmMin((() => {
|
|
|
5958
6059
|
refreshTable: "Refresh Table",
|
|
5959
6060
|
defaultTitle: "Database Explorer",
|
|
5960
6061
|
defaultDesc: "Select a connection, database, or table from the left panel to view details",
|
|
5961
|
-
rowsSuffix: "rows"
|
|
6062
|
+
rowsSuffix: "rows",
|
|
6063
|
+
docsSuffix: "docs",
|
|
6064
|
+
loadConnectionsFailed: "Failed to load connections",
|
|
6065
|
+
loadDatabasesFailed: "Failed to load databases",
|
|
6066
|
+
loadTablesFailed: "Failed to load tables",
|
|
6067
|
+
loadDatabaseInfoFailed: "Failed to load database info",
|
|
6068
|
+
loadingTableData: "Loading data of table \"{name}\"...",
|
|
6069
|
+
loadTableDataFailed: "Failed to load table data",
|
|
6070
|
+
loadTableStructureFailed: "Failed to load table structure",
|
|
6071
|
+
testConnectionSuccess: "Connection \"{name}\" test succeeded",
|
|
6072
|
+
testConnectionFailed: "Connection \"{name}\" test failed",
|
|
6073
|
+
testConnectionError: "Connection test failed",
|
|
6074
|
+
sqlQueryInDev: "SQL query is under development...",
|
|
6075
|
+
tableStructureRefreshed: "Table structure refreshed",
|
|
6076
|
+
featureInDev: "Feature under development...",
|
|
6077
|
+
connectionRefreshed: "Connection \"{name}\" refreshed",
|
|
6078
|
+
databaseRefreshed: "Database \"{name}\" refreshed",
|
|
6079
|
+
confirmDeleteDatabase: "Are you sure you want to delete database \"{name}\"? This will delete the database and all its data and cannot be undone.",
|
|
6080
|
+
deletingDatabase: "Deleting database \"{name}\"...",
|
|
6081
|
+
databaseDeleted: "Database \"{name}\" deleted",
|
|
6082
|
+
deleteDatabaseFailed: "Failed to delete database",
|
|
6083
|
+
confirmDeleteConnection: "Are you sure you want to delete connection \"{name}\"? This removes the connection config but does not delete actual database data.",
|
|
6084
|
+
deletingConnection: "Deleting connection \"{name}\"...",
|
|
6085
|
+
connectionDeleted: "Connection \"{name}\" deleted",
|
|
6086
|
+
deleteConnectionFailed: "Failed to delete connection",
|
|
6087
|
+
tableRefreshed: "Table \"{name}\" refreshed",
|
|
6088
|
+
creatingTable: "Creating table \"{name}\"...",
|
|
6089
|
+
tableCreated: "Table \"{name}\" created successfully",
|
|
6090
|
+
createTableFailed: "Failed to create table",
|
|
6091
|
+
insertDataInDev: "Insert data is under development...",
|
|
6092
|
+
exportTableInDev: "Export table is under development...",
|
|
6093
|
+
confirmTruncateTable: "Are you sure you want to truncate table \"{name}\"? This will delete all data and cannot be undone.",
|
|
6094
|
+
truncatingTable: "Truncating table \"{name}\"...",
|
|
6095
|
+
tableTruncated: "Table \"{name}\" truncated",
|
|
6096
|
+
truncateTableFailed: "Failed to truncate table",
|
|
6097
|
+
editRowInDev: "Edit row is under development...",
|
|
6098
|
+
deleteRowInDev: "Delete row is under development...",
|
|
6099
|
+
selectConnectionFirst: "Please select a database connection first",
|
|
6100
|
+
sqlCannotBeEmpty: "SQL statement cannot be empty",
|
|
6101
|
+
sqlExecuted: "SQL executed successfully",
|
|
6102
|
+
sqlAffectedRows: "Executed successfully, affected rows: {count}",
|
|
6103
|
+
sqlExecuteFailed: "SQL execution failed"
|
|
5962
6104
|
},
|
|
5963
6105
|
databaseDetail: {
|
|
5964
6106
|
tables: "Tables",
|
|
6107
|
+
collections: "Collections",
|
|
5965
6108
|
views: "Views",
|
|
5966
6109
|
procedures: "Procedures",
|
|
5967
6110
|
functions: "Functions",
|
|
@@ -6019,6 +6162,18 @@ var init_en_US = __esmMin((() => {
|
|
|
6019
6162
|
refreshData: "Refresh Data",
|
|
6020
6163
|
modifyStructure: "Modify Structure",
|
|
6021
6164
|
insertData: "Insert Data",
|
|
6165
|
+
insertDocument: "Insert Document",
|
|
6166
|
+
createIndex: "Create Index",
|
|
6167
|
+
indexKeysLabel: "Index Keys (JSON)",
|
|
6168
|
+
indexNameOptional: "Index Name (optional)",
|
|
6169
|
+
indexKeysInvalidJson: "Invalid JSON: {message}",
|
|
6170
|
+
indexKeysMustBeObject: "Index keys must be a JSON object, e.g. {\"name\": 1}",
|
|
6171
|
+
indexCreated: "Index created successfully",
|
|
6172
|
+
indexCreateFailed: "Failed to create index",
|
|
6173
|
+
primaryIndexUndeletable: "Primary index (_id_) cannot be deleted",
|
|
6174
|
+
indexDeleted: "Index deleted",
|
|
6175
|
+
indexDeleteFailed: "Failed to delete index",
|
|
6176
|
+
docMissingId: "This document has no _id and cannot be deleted",
|
|
6022
6177
|
exportMenu: "Export",
|
|
6023
6178
|
exportCSV: "Export CSV",
|
|
6024
6179
|
exportJSON: "Export JSON",
|
|
@@ -6093,6 +6248,12 @@ var init_en_US = __esmMin((() => {
|
|
|
6093
6248
|
dataEditor: {
|
|
6094
6249
|
editData: "Edit Data",
|
|
6095
6250
|
addData: "New Data",
|
|
6251
|
+
editDocHint: "Edit this document",
|
|
6252
|
+
newDocHint: "New document (JSON object)",
|
|
6253
|
+
docSaveEditHint: "On save, the document is located by _id",
|
|
6254
|
+
docSaveInsertHint: "On save, inserted as a new document; _id is generated by the database",
|
|
6255
|
+
docJsonInvalid: "Invalid JSON: {message}",
|
|
6256
|
+
docJsonRequired: "Provide a JSON object (e.g. {\"name\":\"alice\"})",
|
|
6096
6257
|
autoGenerate: "Auto-generated",
|
|
6097
6258
|
inputPlaceholder: "Enter {name}",
|
|
6098
6259
|
selectPlaceholder: "Select...",
|
|
@@ -6115,6 +6276,26 @@ var init_en_US = __esmMin((() => {
|
|
|
6115
6276
|
tableNamePlaceholder: "Enter table name",
|
|
6116
6277
|
tableCommentLabel: "Table Comment",
|
|
6117
6278
|
tableCommentPlaceholder: "Enter table comment",
|
|
6279
|
+
tableCommentUnsupported: "This database does not support table comments",
|
|
6280
|
+
schemalessHint: "This is a schemaless database (MongoDB). An <strong>empty collection</strong> will be created without column definitions.",
|
|
6281
|
+
refreshTableList: "Refresh Table List",
|
|
6282
|
+
refreshing: "Refreshing...",
|
|
6283
|
+
refreshTableListTip: "Re-fetch the table list (required for duplicate-name detection; preview uses a 30s cache by default)",
|
|
6284
|
+
previewNeedColumns: "-- Complete column info (name & type) to preview SQL",
|
|
6285
|
+
previewCreatePlaceholder: "-- CREATE TABLE SQL will appear here after completing the form",
|
|
6286
|
+
previewCreateSqlPlaceholder: "-- Backend-generated SQL will appear here after completing the form",
|
|
6287
|
+
previewNoChanges: "No changes detected",
|
|
6288
|
+
previewFailed: "Preview failed: {message}",
|
|
6289
|
+
liveSchemaMissing: "Live schema not available, refresh the table list and retry.",
|
|
6290
|
+
liveColumnsUnknown: "(unknown)",
|
|
6291
|
+
liveColumnsExist: "The live table already has columns: {columns}\nIf you expected the new column there, it was saved before or already existed.",
|
|
6292
|
+
createCollectionSuccess: "Collection created successfully",
|
|
6293
|
+
createTableSuccess: "Table created successfully",
|
|
6294
|
+
alterTableSuccess: "Table structure updated successfully",
|
|
6295
|
+
refreshTableListFailed: "Failed to refresh table list: {message}",
|
|
6296
|
+
tableNameRequired: "Please enter a table name",
|
|
6297
|
+
columnsIncomplete: "Please complete all column information",
|
|
6298
|
+
connectionRequired: "Please select a database connection first",
|
|
6118
6299
|
columnManagement: "Column Management",
|
|
6119
6300
|
addColumn: "Add Column",
|
|
6120
6301
|
columnNameHeader: "Column Name",
|
|
@@ -6146,8 +6327,25 @@ var init_en_US = __esmMin((() => {
|
|
|
6146
6327
|
catSpatial: "Spatial",
|
|
6147
6328
|
catOther: "Other"
|
|
6148
6329
|
},
|
|
6330
|
+
documentQuery: {
|
|
6331
|
+
filterTab: "Filter",
|
|
6332
|
+
aggregateTab: "Aggregate",
|
|
6333
|
+
sortPlaceholder: "Sort, e.g. {\"age\":-1}",
|
|
6334
|
+
execute: "Execute",
|
|
6335
|
+
executing: "Executing...",
|
|
6336
|
+
totalLabel: "{count} rows",
|
|
6337
|
+
filterLabel: "Filter (JSON)",
|
|
6338
|
+
aggregateLabel: "Aggregation Pipeline (JSON array)",
|
|
6339
|
+
filterHint: "Leave empty to skip filtering",
|
|
6340
|
+
aggregateHint: "MongoDB aggregation pipeline, e.g. $match / $group / $sort",
|
|
6341
|
+
filterMustBeObject: "Filter must be a JSON object, e.g. {\"status\":\"active\"}",
|
|
6342
|
+
aggregateMustBeArray: "Pipeline must be a JSON array, e.g. [{ \"$match\": {} }]",
|
|
6343
|
+
result: "Result",
|
|
6344
|
+
noResult: "No results"
|
|
6345
|
+
},
|
|
6149
6346
|
importExport: {
|
|
6150
6347
|
title: "Data Import/Export",
|
|
6348
|
+
unknownError: "Unknown error",
|
|
6151
6349
|
dataImport: "Data Import",
|
|
6152
6350
|
dataExport: "Data Export",
|
|
6153
6351
|
stepSelectFile: "Select File",
|
|
@@ -7636,7 +7834,29 @@ var init_dataGrid = __esmMin((() => {
|
|
|
7636
7834
|
}));
|
|
7637
7835
|
//#endregion
|
|
7638
7836
|
//#region src/components/modal/index.ts
|
|
7639
|
-
|
|
7837
|
+
/**
|
|
7838
|
+
* 复位 active 标记并消费待显示modal。
|
|
7839
|
+
*
|
|
7840
|
+
* 复位必须在关闭动作(onConfirm/onCancel)里立即做:
|
|
7841
|
+
* hide() 的关闭动画是异步的,hidden.bs.modal 可能在下一次 show 之后才触发,
|
|
7842
|
+
* 若只依赖该事件复位,"确认 -> 紧接着又弹一个提示" 就会被误判为
|
|
7843
|
+
* "Modal busy" 入队,而入队的 Promise 始终不 resolve,表现为
|
|
7844
|
+
* 弹窗一闪而过、之后再点任何弹窗都没反应。
|
|
7845
|
+
*/
|
|
7846
|
+
function releaseActive() {
|
|
7847
|
+
isModalActive = false;
|
|
7848
|
+
flushPendingModal();
|
|
7849
|
+
}
|
|
7850
|
+
/** 显示排队中的modal,并把结果回传给最初那次调用的 Promise */
|
|
7851
|
+
function flushPendingModal() {
|
|
7852
|
+
if (!pendingModal) return;
|
|
7853
|
+
const pending = pendingModal;
|
|
7854
|
+
pendingModal = null;
|
|
7855
|
+
nextTick(() => {
|
|
7856
|
+
showModal(pending.options).then(pending.resolve);
|
|
7857
|
+
});
|
|
7858
|
+
}
|
|
7859
|
+
var globalModalInstance, modalInstance, modal_default, getModalInstance, isModalActive, closeHandled, pendingModal, showModal, showAlert, showConfirm;
|
|
7640
7860
|
var init_modal = __esmMin((() => {
|
|
7641
7861
|
init_vue_runtime_esm_bundler();
|
|
7642
7862
|
init_modal$1();
|
|
@@ -7650,13 +7870,13 @@ var init_modal = __esmMin((() => {
|
|
|
7650
7870
|
modalApp.use(i18n);
|
|
7651
7871
|
globalModalInstance = modalApp.mount(mount);
|
|
7652
7872
|
if (globalModalInstance && globalModalInstance.$el) globalModalInstance.$el.addEventListener("hidden.bs.modal", () => {
|
|
7873
|
+
if (closeHandled) {
|
|
7874
|
+
closeHandled = false;
|
|
7875
|
+
return;
|
|
7876
|
+
}
|
|
7653
7877
|
isModalActive = false;
|
|
7654
7878
|
console.log("Modal hidden, isModalActive reset to false");
|
|
7655
|
-
|
|
7656
|
-
const pending = pendingModalOptions;
|
|
7657
|
-
pendingModalOptions = null;
|
|
7658
|
-
showModal(pending);
|
|
7659
|
-
}
|
|
7879
|
+
flushPendingModal();
|
|
7660
7880
|
});
|
|
7661
7881
|
app.config.globalProperties.$modal = modalInstance = {
|
|
7662
7882
|
show: () => globalModalInstance.show(),
|
|
@@ -7729,7 +7949,8 @@ var init_modal = __esmMin((() => {
|
|
|
7729
7949
|
return modalInstance;
|
|
7730
7950
|
};
|
|
7731
7951
|
isModalActive = false;
|
|
7732
|
-
|
|
7952
|
+
closeHandled = false;
|
|
7953
|
+
pendingModal = null;
|
|
7733
7954
|
showModal = (options) => {
|
|
7734
7955
|
if (!globalModalInstance) return Promise.resolve(false);
|
|
7735
7956
|
return new Promise((resolve) => {
|
|
@@ -7743,14 +7964,18 @@ var init_modal = __esmMin((() => {
|
|
|
7743
7964
|
...options,
|
|
7744
7965
|
onConfirm: () => {
|
|
7745
7966
|
options.onConfirm?.();
|
|
7967
|
+
closeHandled = true;
|
|
7746
7968
|
globalModalInstance.hide();
|
|
7969
|
+
releaseActive();
|
|
7747
7970
|
nextTick(() => {
|
|
7748
7971
|
resolve(true);
|
|
7749
7972
|
});
|
|
7750
7973
|
},
|
|
7751
7974
|
onCancel: () => {
|
|
7752
7975
|
options.onCancel?.();
|
|
7976
|
+
closeHandled = true;
|
|
7753
7977
|
globalModalInstance.hide();
|
|
7978
|
+
releaseActive();
|
|
7754
7979
|
nextTick(() => {
|
|
7755
7980
|
resolve(false);
|
|
7756
7981
|
});
|
|
@@ -7758,15 +7983,16 @@ var init_modal = __esmMin((() => {
|
|
|
7758
7983
|
};
|
|
7759
7984
|
if (isModalActive) {
|
|
7760
7985
|
console.log("Modal busy, queuing new modal");
|
|
7761
|
-
|
|
7986
|
+
pendingModal = {
|
|
7987
|
+
options,
|
|
7988
|
+
resolve
|
|
7989
|
+
};
|
|
7762
7990
|
return;
|
|
7763
7991
|
}
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
};
|
|
7769
|
-
showModalInstance();
|
|
7992
|
+
globalModalInstance.setOptions?.(finalOptions);
|
|
7993
|
+
isModalActive = true;
|
|
7994
|
+
closeHandled = false;
|
|
7995
|
+
globalModalInstance.show?.() || Promise.resolve();
|
|
7770
7996
|
});
|
|
7771
7997
|
};
|
|
7772
7998
|
showAlert = (content, type = "info", details) => {
|
package/dist/public/layout.js
CHANGED
|
@@ -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.40";
|
|
234
234
|
return (_ctx, _cache) => {
|
|
235
235
|
const _component_router_link = resolveComponent("router-link");
|
|
236
236
|
const _component_router_view = resolveComponent("router-view");
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 数据库支持端到端验证脚本
|
|
3
|
+
* 直接复用 dist/server 编译产物里的 ConnectionService + DatabaseService(即应用真实代码路径),
|
|
4
|
+
* 对每个数据库类型跑完整链路(调用方式与 server/index.ts 路由一致):
|
|
5
|
+
* 建连 -> getDatabases -> getTables -> 建表 -> getColumns -> 插入 -> 查数据
|
|
6
|
+
* -> 改表/校验 -> 导出schema -> 删表
|
|
7
|
+
*
|
|
8
|
+
* 关系型(postgres/cockroachdb/oracle/mssql/sqlite)走 DDL spec 建表/改表 + SQL 查询;
|
|
9
|
+
* MongoDB 走服务自身的集合方法(文档库无 SQL DDL)。
|
|
10
|
+
*
|
|
11
|
+
* 用法:
|
|
12
|
+
* DB_TOOL_DATA_DIR=/tmp/dbtool-verify node scripts/verify-db-support.cjs [type ...]
|
|
13
|
+
* type ∈ postgres cockroachdb oracle mssql mongodb sqlite
|
|
14
|
+
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
|
|
21
|
+
const dataDir = process.env.DB_TOOL_DATA_DIR || path.join(os.tmpdir(), 'dbtool-verify-' + Date.now());
|
|
22
|
+
process.env.DB_TOOL_DATA_DIR = dataDir;
|
|
23
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
24
|
+
|
|
25
|
+
const { ConnectionService } = require(path.join(__dirname, '..', 'dist', 'server', 'service', 'connection.service.js'));
|
|
26
|
+
const { DatabaseService } = require(path.join(__dirname, '..', 'dist', 'server', 'service', 'database', 'database.service.js'));
|
|
27
|
+
|
|
28
|
+
const CONN = {
|
|
29
|
+
postgres: { type: 'postgres', host: '127.0.0.1', port: 15432, username: 'postgres', password: 'postgres', database: 'testdb', options: {} },
|
|
30
|
+
cockroachdb: { type: 'cockroachdb', host: '127.0.0.1', port: 26257, username: 'root', password: '', database: 'defaultdb', options: {} },
|
|
31
|
+
oracle: { type: 'oracle', host: '127.0.0.1', port: 11521, username: 'system', password: 'oracle', database: 'freepdb1', options: {} },
|
|
32
|
+
mssql: { type: 'mssql', host: '127.0.0.1', port: 11433, username: 'sa', password: 'YourStrong!Passw0rd', database: 'master', options: {} },
|
|
33
|
+
mongodb: { type: 'mongodb', host: '127.0.0.1', port: 27017, username: '', password: '', database: 'testdb', options: {} },
|
|
34
|
+
clickhouse: { type: 'clickhouse', host: '127.0.0.1', port: 18123, username: 'default', password: '', database: 'default', options: {} },
|
|
35
|
+
sqlite: { type: 'sqlite', host: '', port: null, username: '', password: '', database: path.join(dataDir, 'verify.db'), options: {} }
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const TABLE = 'verify_tbl';
|
|
39
|
+
|
|
40
|
+
function specBase() {
|
|
41
|
+
return {
|
|
42
|
+
name: TABLE, comment: 'verify table',
|
|
43
|
+
columns: [
|
|
44
|
+
{ name: 'id', type: 'INT', nullable: false, hasDefault: false, autoIncrement: true },
|
|
45
|
+
{ name: 'name', type: 'VARCHAR', length: 100, nullable: true, hasDefault: false },
|
|
46
|
+
{ name: 'age', type: 'INT', nullable: true, hasDefault: false }
|
|
47
|
+
],
|
|
48
|
+
primaryKey: ['id']
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function specAltered() {
|
|
52
|
+
const s = specBase();
|
|
53
|
+
s.columns.push({ name: 'email', type: 'VARCHAR', length: 200, nullable: true, hasDefault: false });
|
|
54
|
+
return s;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function step(name, fn) {
|
|
58
|
+
return Promise.resolve()
|
|
59
|
+
.then(fn)
|
|
60
|
+
.then(r => { console.log(` [PASS] ${name}`); return r; })
|
|
61
|
+
.catch(e => {
|
|
62
|
+
const msg = e && e.message ? e.message.split('\n')[0] : String(e);
|
|
63
|
+
console.log(` [FAIL] ${name} -> ${msg}`);
|
|
64
|
+
return { __error: msg };
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function runType(type) {
|
|
69
|
+
const cfg = CONN[type];
|
|
70
|
+
if (!cfg) { console.log(`未知类型: ${type}`); return; }
|
|
71
|
+
const isMongo = type === 'mongodb';
|
|
72
|
+
console.log(`\n==================== ${type} ====================`);
|
|
73
|
+
const connService = new ConnectionService();
|
|
74
|
+
await connService.init();
|
|
75
|
+
const dispatcher = new DatabaseService();
|
|
76
|
+
dispatcher.connectionService = connService;
|
|
77
|
+
|
|
78
|
+
const baseOf = async (database) => {
|
|
79
|
+
const ds = await connService.getActiveConnection(id, database);
|
|
80
|
+
return { ds, base: dispatcher['getDatabaseService'](ds.options.type) };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const conn = await connService.addConnection({ ...cfg, name: 'verify_' + type, enabled: true });
|
|
84
|
+
const id = conn.id;
|
|
85
|
+
const db = cfg.database;
|
|
86
|
+
|
|
87
|
+
await step('testConnection', async () => {
|
|
88
|
+
const ok = await connService.testConnection(conn);
|
|
89
|
+
if (!ok) throw new Error('连接测试失败');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await step('getDatabases', async () => {
|
|
93
|
+
const list = await dispatcher.getDatabases(id);
|
|
94
|
+
if (!Array.isArray(list)) throw new Error('返回非数组');
|
|
95
|
+
return list;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await step('getTables(初始)', async () => {
|
|
99
|
+
const list = await dispatcher.getTables(id, db);
|
|
100
|
+
if (!Array.isArray(list)) throw new Error('返回非数组');
|
|
101
|
+
return list;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// 清理残留
|
|
105
|
+
await (async () => {
|
|
106
|
+
try { const { ds, base } = await baseOf(db); await base.dropTable(ds, db, TABLE); } catch (e) { /* ignore */ }
|
|
107
|
+
})();
|
|
108
|
+
|
|
109
|
+
if (isMongo) {
|
|
110
|
+
await step('createTable(集合)', async () => {
|
|
111
|
+
const { ds, base } = await baseOf(db);
|
|
112
|
+
await base.createTable(ds, db, specBase());
|
|
113
|
+
});
|
|
114
|
+
await step('getTables(建表后含 verify_tbl)', async () => {
|
|
115
|
+
const list = await dispatcher.getTables(id, db);
|
|
116
|
+
const found = list.some(t => String(t.name).toLowerCase() === TABLE.toLowerCase());
|
|
117
|
+
if (!found) throw new Error('建表后未找到 ' + TABLE);
|
|
118
|
+
return list;
|
|
119
|
+
});
|
|
120
|
+
await step('insertData', async () => {
|
|
121
|
+
const { ds, base } = await baseOf(db);
|
|
122
|
+
await base.insertData(ds, db, TABLE, { name: 'alice', age: 30 });
|
|
123
|
+
await base.insertData(ds, db, TABLE, { name: 'bob', age: 25 });
|
|
124
|
+
});
|
|
125
|
+
await step('getTableData(原生find)', async () => {
|
|
126
|
+
const { ds } = await baseOf(db);
|
|
127
|
+
const docs = await ds.driver.queryRunner.databaseConnection.db(db).collection(TABLE).find().toArray();
|
|
128
|
+
if (!Array.isArray(docs) || docs.length < 2) throw new Error('数据行不足');
|
|
129
|
+
return docs;
|
|
130
|
+
});
|
|
131
|
+
await step('getColumns(插入后)', async () => {
|
|
132
|
+
const { ds, base } = await baseOf(db);
|
|
133
|
+
const cols = await base.getColumns(ds, db, TABLE);
|
|
134
|
+
if (!Array.isArray(cols) || cols.length < 3) throw new Error('列数不足: ' + (cols && cols.length));
|
|
135
|
+
return cols.map(c => c.name + ':' + c.type);
|
|
136
|
+
});
|
|
137
|
+
await step('exportSchema', async () => {
|
|
138
|
+
const s = await dispatcher.exportSchema(id, db);
|
|
139
|
+
if (!s || !s.trim()) throw new Error('schema 为空');
|
|
140
|
+
return s;
|
|
141
|
+
});
|
|
142
|
+
await step('dropTable(清理)', async () => {
|
|
143
|
+
const { ds, base } = await baseOf(db);
|
|
144
|
+
await base.dropTable(ds, db, TABLE);
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
await step('previewCreateTable', async () => {
|
|
148
|
+
const p = await dispatcher.previewCreateTable(id, db, specBase());
|
|
149
|
+
if (!p.preview || !p.preview.trim()) throw new Error('预览为空');
|
|
150
|
+
return p.preview;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await step('executeCreateTable', async () => {
|
|
154
|
+
const r = await dispatcher.executeCreateTable(id, db, specBase());
|
|
155
|
+
if (!r.statements || r.statements.length === 0) throw new Error('未生成建表语句');
|
|
156
|
+
return r.statements;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await step('getTables(建表后含 verify_tbl)', async () => {
|
|
160
|
+
const list = await dispatcher.getTables(id, db);
|
|
161
|
+
const found = list.some(t => String(t.name).toLowerCase() === TABLE.toLowerCase());
|
|
162
|
+
if (!found) throw new Error('建表后未找到 ' + TABLE);
|
|
163
|
+
return list;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await step('getColumns', async () => {
|
|
167
|
+
const { ds, base } = await baseOf(db);
|
|
168
|
+
const cols = await base.getColumns(ds, db, TABLE);
|
|
169
|
+
if (!Array.isArray(cols) || cols.length < 3) throw new Error('列数不足: ' + (cols && cols.length));
|
|
170
|
+
return cols.map(c => c.name + ':' + c.type);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await step('insertData', async () => {
|
|
174
|
+
const { ds, base } = await baseOf(db);
|
|
175
|
+
const cols = await base.getColumns(ds, db, TABLE);
|
|
176
|
+
const ins = (data) => {
|
|
177
|
+
const names = cols.filter(c => !c.isAutoIncrement).map(c => base['quoteIdentifier'](c.name));
|
|
178
|
+
const vals = cols.filter(c => !c.isAutoIncrement).map(c => data[c.name] === undefined ? 'NULL' : (typeof data[c.name] === 'string' ? `'${String(data[c.name]).replace(/'/g, "''")}'` : String(data[c.name])));
|
|
179
|
+
return `INSERT INTO ${base['quoteIdentifier'](TABLE)} (${names.join(',')}) VALUES (${vals.join(',')})`;
|
|
180
|
+
};
|
|
181
|
+
await dispatcher.executeQuery(id, ins({ name: 'alice', age: 30 }), db);
|
|
182
|
+
await dispatcher.executeQuery(id, ins({ name: 'bob', age: 25 }), db);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await step('getTableData', async () => {
|
|
186
|
+
const r = await dispatcher.getTableData(id, db, TABLE, 1, 100);
|
|
187
|
+
if (!r || !Array.isArray(r.data) || r.data.length < 2) throw new Error('数据行不足');
|
|
188
|
+
return r.data;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await step('executeQuery(SELECT COUNT)', async () => {
|
|
192
|
+
const { base } = await baseOf(db);
|
|
193
|
+
const r = await dispatcher.executeQuery(id, `SELECT COUNT(*) AS c FROM ${base['quoteIdentifier'](TABLE)}`, db);
|
|
194
|
+
return r;
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
await step('previewAlterTable(加 email 列)', async () => {
|
|
198
|
+
const p = await dispatcher.previewAlterTable(id, db, TABLE, specAltered());
|
|
199
|
+
if (!p.preview || !p.preview.trim()) throw new Error('预览为空');
|
|
200
|
+
return p.preview;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await step('executeAlterTable(加 email 列)', async () => {
|
|
204
|
+
const r = await dispatcher.executeAlterTable(id, db, TABLE, specAltered());
|
|
205
|
+
if (!r.statements || r.statements.length === 0) throw new Error('未生成改表语句');
|
|
206
|
+
return r.statements;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await step('getColumns(改表后含 email)', async () => {
|
|
210
|
+
const { ds, base } = await baseOf(db);
|
|
211
|
+
const cols = await base.getColumns(ds, db, TABLE);
|
|
212
|
+
const hasEmail = cols.some(c => String(c.name).toLowerCase() === 'email');
|
|
213
|
+
if (!hasEmail) throw new Error('改表后未找到 email 列');
|
|
214
|
+
return cols.map(c => c.name);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
await step('exportSchema', async () => {
|
|
218
|
+
const s = await dispatcher.exportSchema(id, db);
|
|
219
|
+
if (!s || !s.trim()) throw new Error('schema 为空');
|
|
220
|
+
return s;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await step('dropTable(清理)', async () => {
|
|
224
|
+
const { ds, base } = await baseOf(db);
|
|
225
|
+
await base.dropTable(ds, db, TABLE);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try { await connService.closeAllConnections(); } catch (e) { /* ignore */ }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
(async () => {
|
|
233
|
+
const args = process.argv.slice(2);
|
|
234
|
+
const types = args.length ? args : ['postgres', 'cockroachdb', 'oracle', 'mssql', 'mongodb', 'sqlite'];
|
|
235
|
+
console.log('数据目录:', dataDir);
|
|
236
|
+
for (const t of types) { await runType(t); }
|
|
237
|
+
console.log('\n==================== 完成 ====================');
|
|
238
|
+
process.exit(0);
|
|
239
|
+
})().catch(e => { console.error('harness 崩溃:', e); process.exit(1); });
|