befly 3.76.5 → 3.76.7

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/Befly.js CHANGED
@@ -131,9 +131,16 @@ export class Befly {
131
131
  });
132
132
 
133
133
  this.started = true;
134
+ const startupTime = calcPerfTime(serverStartTime);
134
135
  Logger.info(`启动 Bun.serve 监听完成,耗时 ${calcPerfTime(listenStartTime)}`);
135
- Logger.info(`启动总耗时 ${calcPerfTime(serverStartTime)}`);
136
- Logger.info(`${this.context.config.appName} 启动成功`, { url: this.server.url, startupTime: calcPerfTime(serverStartTime) });
136
+ Logger.info(`启动总耗时 ${startupTime}`);
137
+ Logger.info(`${this.context.config.appName} 启动成功`, { url: this.server.url, startupTime: startupTime });
138
+
139
+ process.stdout.write(`${this.context.config.appName} 启动成功\n`);
140
+ process.stdout.write(`启动耗时: ${startupTime}\n`);
141
+ process.stdout.write(`监听地址: ${this.server.url}\n`);
142
+ process.stdout.write(`Mysql 数据库地址: ${this.context.config.mysql.hostname}:${this.context.config.mysql.port}\n`);
143
+ process.stdout.write(`Redis 缓存地址: ${this.context.config.redis.hostname}:${this.context.config.redis.port}\n`);
137
144
  return this.server;
138
145
  } catch (error) {
139
146
  Logger.error("项目启动失败", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.76.5",
3
+ "version": "3.76.7",
4
4
  "gitHead": "49c39d36695036e85fc64083cc43c1652fff96cb",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
@@ -30,7 +30,7 @@ async function buildPlan(mysql, targetTables, tableNames) {
30
30
  export async function syncDb(mysqlConfig, options = {}) {
31
31
  const apply = options.apply === true;
32
32
  const list = options.list === true;
33
- const tableNames = Array.isArray(options.tableNames) ? options.tableNames.filter(Boolean) : options.tableName ? [options.tableName] : [];
33
+ const tableNames = [...new Set((Array.isArray(options.tableNames) ? options.tableNames : options.tableName ? [options.tableName] : []).filter(Boolean))];
34
34
  const tableScope = tableNames.length > 0 ? tableNames.join(", ") : "";
35
35
  const startedAt = nowText();
36
36
  let client;
@@ -33,7 +33,7 @@ export function selectTables(tables, tableNames = []) {
33
33
 
34
34
  const selectedNameSet = new Set(selectedNames);
35
35
  const selectedTables = Object.fromEntries(Object.entries(tables).filter(([key, table]) => selectedNameSet.has(key) || selectedNameSet.has(table.codeName) || selectedNameSet.has(table.fileName)));
36
- const matchedNames = new Set(Object.values(selectedTables).flatMap((table) => [table.codeName, table.fileName]));
36
+ const matchedNames = new Set(Object.entries(selectedTables).flatMap(([key, table]) => [key, table.codeName, table.fileName]));
37
37
  const missingName = selectedNames.find((name) => !matchedNames.has(name));
38
38
  if (missingName) throw new TypeError(`找不到 Table 定义:${missingName}`);
39
39
  return selectedTables;
@@ -15,21 +15,28 @@ function targetDbType(fieldType) {
15
15
  return { integer: "bigint", number: "decimal", varchar: "varchar", text: "mediumtext" }[fieldType];
16
16
  }
17
17
 
18
+ const compatibleFieldTypeSources = {
19
+ integer: new Set(["integer", "tinyint", "smallint", "mediumint", "int"]),
20
+ number: new Set(["number"]),
21
+ varchar: new Set(["varchar", "char"]),
22
+ text: new Set(["text", "tinytext", "longtext", "json", "varchar", "char"])
23
+ };
24
+
18
25
  function compatibleFieldType(target, actual) {
19
- const sources = {
20
- integer: new Set(["integer", "tinyint", "smallint", "mediumint", "int"]),
21
- number: new Set(["number"]),
22
- varchar: new Set(["varchar", "char"]),
23
- text: new Set(["text", "tinytext", "longtext", "json", "varchar", "char"])
24
- };
25
- return sources[target]?.has(actual) === true;
26
+ return compatibleFieldTypeSources[target]?.has(actual) === true;
26
27
  }
27
28
 
28
29
  function columnChanges(target, actual) {
29
30
  const changes = [];
30
- for (const property of ["fieldType", "maxValue", "precision", "scale", "nullable", "name", "unsigned"]) {
31
- if (target[property] !== actual[property]) changes.push({ property: property, before: actual[property], after: target[property] });
31
+ if (target.fieldType !== actual.fieldType) changes.push({ property: "fieldType", before: actual.fieldType, after: target.fieldType });
32
+ if (target.fieldType === "varchar" && target.maxValue !== actual.maxValue) changes.push({ property: "maxValue", before: actual.maxValue, after: target.maxValue });
33
+ if (target.fieldType === "number") {
34
+ if (target.precision !== actual.precision) changes.push({ property: "precision", before: actual.precision, after: target.precision });
35
+ if (target.scale !== actual.scale) changes.push({ property: "scale", before: actual.scale, after: target.scale });
32
36
  }
37
+ if (target.nullable !== actual.nullable) changes.push({ property: "nullable", before: actual.nullable, after: target.nullable });
38
+ if (target.name !== actual.name) changes.push({ property: "name", before: actual.name, after: target.name });
39
+ if (target.unsigned !== actual.unsigned) changes.push({ property: "unsigned", before: actual.unsigned, after: target.unsigned });
33
40
  if (!sameDefault(target.default, actual.default)) changes.push({ property: "default", before: actual.default, after: target.default });
34
41
  return changes;
35
42
  }
@@ -68,8 +75,7 @@ export function updatePlanHash(plan) {
68
75
  }
69
76
 
70
77
  export function buildSchemaPlan(targetTables, actualTables, { tableNames = [], singleTable = false } = {}) {
71
- const selectedTableNames = Array.isArray(tableNames) ? tableNames : tableNames ? [tableNames] : [];
72
- const restrictUnmanagedTables = selectedTableNames.length > 0 || singleTable;
78
+ const restrictUnmanagedTables = singleTable || (Array.isArray(tableNames) ? tableNames.length > 0 : Boolean(tableNames));
73
79
  const plan = { operations: [], notices: [], blocked: [], counts: {} };
74
80
 
75
81
  for (const target of Object.values(targetTables)) {
@@ -2,6 +2,8 @@ import { mkdir } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
3
 
4
4
  function formatValue(value) {
5
+ if (value === true) return "是";
6
+ if (value === false) return "否";
5
7
  if (value === "") return '""';
6
8
  if (value === null || value === undefined) return "无";
7
9
  if (value && typeof value === "object") {
@@ -41,6 +43,17 @@ function colorText(text, color, enabled) {
41
43
 
42
44
  const fieldTypeProperties = new Set(["fieldType", "maxValue", "precision", "scale"]);
43
45
 
46
+ const propertyNames = {
47
+ nullable: "是否可空",
48
+ name: "注释",
49
+ default: "默认值",
50
+ unsigned: "无符号",
51
+ type: "索引类型",
52
+ fields: "索引字段"
53
+ };
54
+
55
+ const indexTypeNames = { index: "普通", unique: "唯一" };
56
+
44
57
  function targetFieldTypeText(field) {
45
58
  if (!field || typeof field !== "object") return "-";
46
59
  return field.fieldType === "integer" ? "BIGINT" : field.fieldType === "number" ? `DECIMAL(${field.precision},${field.scale})` : field.fieldType === "varchar" ? `VARCHAR(${field.maxValue})` : field.fieldType === "text" ? "MEDIUMTEXT" : "-";
@@ -75,7 +88,8 @@ function blockedFieldTypeText(blocked) {
75
88
  }
76
89
 
77
90
  function preflightResult(operation, colors) {
78
- return colorText(operation.preflight?.blocked ? "" : "是", operation.preflight?.blocked ? "red" : "limegreen", colors);
91
+ if (!operation.preflight) return "-";
92
+ return colorText(operation.preflight.blocked ? "否" : "是", operation.preflight.blocked ? "red" : "limegreen", colors);
79
93
  }
80
94
 
81
95
  function buildSyncRows(plan, colors) {
@@ -165,14 +179,24 @@ function escapeTableValue(value) {
165
179
 
166
180
  function archiveChangeText(operation) {
167
181
  if (operation.kind === "createTable") return "创建全新表";
168
- if (operation.kind === "addColumn") return `NULL=${operation.after.nullable ? "是" : "否"},默认值=${formatValue(operation.after.default)}`;
182
+ if (operation.kind === "addColumn") return `是否可空=${operation.after.nullable ? "是" : "否"},默认值=${formatValue(operation.after.default)}`;
169
183
  if (operation.kind === "addIndex") return `新增${operation.after.type === "unique" ? "唯一" : "普通"}索引:${operation.after.fields.join(", ")}`;
170
- if (operation.kind === "replaceIndex") return `${formatValue(operation.before)} -> ${formatValue(operation.after)}`;
184
+ if (operation.kind === "replaceIndex") {
185
+ return operation.changes
186
+ .map((change) => {
187
+ const label = propertyNames[change.property] || change.property;
188
+ if (change.property === "type") return `${label}: ${indexTypeNames[change.before] || change.before} -> ${indexTypeNames[change.after] || change.after}`;
189
+ const before = Array.isArray(change.before) ? change.before.join(", ") : formatValue(change.before);
190
+ const after = Array.isArray(change.after) ? change.after.join(", ") : formatValue(change.after);
191
+ return `${label}: ${before} -> ${after}`;
192
+ })
193
+ .join(";");
194
+ }
171
195
  if (operation.kind === "dropIndex") return `删除未在 Tables 中声明的索引:${operation.object}`;
172
196
  return (
173
197
  operation.changes
174
198
  .filter((change) => !fieldTypeProperties.has(change.property))
175
- .map((change) => `${change.property}: ${formatValue(change.before)} -> ${formatValue(change.after)}`)
199
+ .map((change) => `${propertyNames[change.property] || change.property}: ${formatValue(change.before)} -> ${formatValue(change.after)}`)
176
200
  .join(";") || "-"
177
201
  );
178
202
  }