befly 3.15.18 → 3.15.20
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/befly.js +140 -6
- package/dist/befly.min.js +12 -11
- package/dist/checks/checkTable.js +12 -2
- package/dist/sync/syncTable.d.ts +2 -0
- package/dist/sync/syncTable.js +140 -4
- package/dist/types/validate.d.ts +1 -1
- package/dist/utils/normalizeFieldDefinition.js +2 -0
- package/package.json +2 -2
package/dist/befly.js
CHANGED
|
@@ -8294,7 +8294,7 @@ init_logger();
|
|
|
8294
8294
|
init_util();
|
|
8295
8295
|
var RESERVED_FIELDS = ["id", "created_at", "updated_at", "deleted_at", "state"];
|
|
8296
8296
|
var RESERVED_FIELD_SET = new Set(RESERVED_FIELDS);
|
|
8297
|
-
var FIELD_TYPES = ["tinyint", "smallint", "mediumint", "int", "bigint", "decimal", "char", "varchar", "tinytext", "text", "mediumtext", "longtext", "datetime", "json"];
|
|
8297
|
+
var FIELD_TYPES = ["tinyint", "smallint", "mediumint", "int", "bigint", "decimal", "float", "double", "char", "varchar", "tinytext", "text", "mediumtext", "longtext", "datetime", "json"];
|
|
8298
8298
|
var FIELD_TYPE_SET = new Set(FIELD_TYPES);
|
|
8299
8299
|
var INPUT_TYPES = ["number", "integer", "string", "char", "array", "array_number", "array_integer", "json", "json_number", "json_integer"];
|
|
8300
8300
|
var INPUT_TYPE_SET = new Set(INPUT_TYPES);
|
|
@@ -8307,6 +8307,8 @@ function inferInputByType(dbType) {
|
|
|
8307
8307
|
case "bigint":
|
|
8308
8308
|
return "integer";
|
|
8309
8309
|
case "decimal":
|
|
8310
|
+
case "float":
|
|
8311
|
+
case "double":
|
|
8310
8312
|
return "number";
|
|
8311
8313
|
case "char":
|
|
8312
8314
|
case "varchar":
|
|
@@ -8461,8 +8463,9 @@ async function checkTable(tables, config2) {
|
|
|
8461
8463
|
const isTextDbType = ["tinytext", "text", "mediumtext", "longtext"].includes(effectiveType);
|
|
8462
8464
|
const isIntDbType = ["tinyint", "smallint", "mediumint", "int", "bigint"].includes(effectiveType);
|
|
8463
8465
|
const isDecimalDbType = effectiveType === "decimal";
|
|
8466
|
+
const isFloatDbType = effectiveType === "float" || effectiveType === "double";
|
|
8464
8467
|
const isJsonDbType = effectiveType === "json";
|
|
8465
|
-
const isNumericDbType = isIntDbType || isDecimalDbType;
|
|
8468
|
+
const isNumericDbType = isIntDbType || isDecimalDbType || isFloatDbType;
|
|
8466
8469
|
if (isRegexInput(normalizedInput) || isEnumInput(normalizedInput) || isAliasInput(normalizedInput)) {
|
|
8467
8470
|
if (!isStringDbType) {
|
|
8468
8471
|
Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input \u4F7F\u7528\u6B63\u5219/\u679A\u4E3E/\u6B63\u5219\u522B\u540D\uFF0C\u4EC5\u5141\u8BB8\u5B57\u7B26\u4E32\u7C7B\u5B57\u6BB5\uFF08char/varchar/text\uFF09`);
|
|
@@ -8598,6 +8601,11 @@ async function checkTable(tables, config2) {
|
|
|
8598
8601
|
Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A ${effectiveType} \u7C7B\u578B\uFF0C\u9ED8\u8BA4\u503C\u5FC5\u987B\u4E3A\u6570\u5B57\u6216 null` + `\uFF08typeof=${typeof field.default}\uFF0Cvalue=${formatValuePreview(field.default)}\uFF09`);
|
|
8599
8602
|
hasError = true;
|
|
8600
8603
|
}
|
|
8604
|
+
} else if (isFloatDbType) {
|
|
8605
|
+
if (field.default !== undefined && field.default !== null && typeof field.default !== "number") {
|
|
8606
|
+
Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A ${effectiveType} \u7C7B\u578B\uFF0C\u9ED8\u8BA4\u503C\u5FC5\u987B\u4E3A\u6570\u5B57\u6216 null` + `\uFF08typeof=${typeof field.default}\uFF0Cvalue=${formatValuePreview(field.default)}\uFF09`);
|
|
8607
|
+
hasError = true;
|
|
8608
|
+
}
|
|
8601
8609
|
}
|
|
8602
8610
|
}
|
|
8603
8611
|
} catch (error) {
|
|
@@ -9987,6 +9995,8 @@ function inferInputByType2(dbType) {
|
|
|
9987
9995
|
case "bigint":
|
|
9988
9996
|
return "integer";
|
|
9989
9997
|
case "decimal":
|
|
9998
|
+
case "float":
|
|
9999
|
+
case "double":
|
|
9990
10000
|
return "number";
|
|
9991
10001
|
case "char":
|
|
9992
10002
|
case "varchar":
|
|
@@ -10184,6 +10194,8 @@ class SyncTable {
|
|
|
10184
10194
|
int: "INT",
|
|
10185
10195
|
bigint: "BIGINT",
|
|
10186
10196
|
decimal: "DECIMAL",
|
|
10197
|
+
float: "FLOAT",
|
|
10198
|
+
double: "DOUBLE",
|
|
10187
10199
|
char: "CHAR",
|
|
10188
10200
|
varchar: "VARCHAR",
|
|
10189
10201
|
datetime: "DATETIME",
|
|
@@ -10196,6 +10208,7 @@ class SyncTable {
|
|
|
10196
10208
|
static TEXT_FAMILY = new Set(["tinytext", "text", "mediumtext", "longtext"]);
|
|
10197
10209
|
static INT_TYPES = new Set(["tinyint", "smallint", "mediumint", "int", "bigint"]);
|
|
10198
10210
|
static DECIMAL_TYPES = new Set(["decimal"]);
|
|
10211
|
+
static FLOAT_TYPES = new Set(["float", "double"]);
|
|
10199
10212
|
db;
|
|
10200
10213
|
dbName;
|
|
10201
10214
|
constructor(ctx) {
|
|
@@ -10214,6 +10227,8 @@ class SyncTable {
|
|
|
10214
10227
|
throw new Error("\u540C\u6B65\u8868\uFF1A\u8BF7\u4F20\u5165\u591A\u4E2A\u8868\u5B9A\u4E49\u7EC4\u6210\u7684\u6570\u7EC4");
|
|
10215
10228
|
}
|
|
10216
10229
|
await SyncTable.ensureDbVersion(this.db);
|
|
10230
|
+
const tableTasks = [];
|
|
10231
|
+
const incompatibleTypeChanges = [];
|
|
10217
10232
|
for (const item of items) {
|
|
10218
10233
|
if (!item || item.type !== "table") {
|
|
10219
10234
|
continue;
|
|
@@ -10224,9 +10239,98 @@ class SyncTable {
|
|
|
10224
10239
|
SyncTable.normalizeFieldDefinitionInPlace(fieldDef);
|
|
10225
10240
|
}
|
|
10226
10241
|
const existsTable = await SyncTable.tableExists(this.db, this.dbName, tableName);
|
|
10242
|
+
const task = {
|
|
10243
|
+
item,
|
|
10244
|
+
tableName,
|
|
10245
|
+
tableFields,
|
|
10246
|
+
existsTable,
|
|
10247
|
+
existingColumns: null,
|
|
10248
|
+
existingIndexes: null,
|
|
10249
|
+
plan: null,
|
|
10250
|
+
planSummary: null,
|
|
10251
|
+
planDetails: null
|
|
10252
|
+
};
|
|
10253
|
+
if (existsTable) {
|
|
10254
|
+
const existingColumns = await SyncTable.getTableColumns(this.db, this.dbName, tableName);
|
|
10255
|
+
const existingIndexes = await SyncTable.getTableIndexes(this.db, this.dbName, tableName);
|
|
10256
|
+
const changes = SyncTable.collectIncompatibleTypeChanges(tableName, existingColumns, tableFields);
|
|
10257
|
+
for (const change of changes) {
|
|
10258
|
+
incompatibleTypeChanges.push(change);
|
|
10259
|
+
}
|
|
10260
|
+
task.existingColumns = existingColumns;
|
|
10261
|
+
task.existingIndexes = existingIndexes;
|
|
10262
|
+
}
|
|
10263
|
+
tableTasks.push(task);
|
|
10264
|
+
}
|
|
10265
|
+
if (incompatibleTypeChanges.length > 0) {
|
|
10266
|
+
const lines = [];
|
|
10267
|
+
for (const change of incompatibleTypeChanges) {
|
|
10268
|
+
lines.push(`- ${change.tableName}.${change.dbFieldName}: ${change.currentType} -> ${change.expectedType}`);
|
|
10269
|
+
}
|
|
10270
|
+
const msgLines = [];
|
|
10271
|
+
msgLines.push("\u7981\u6B62\u5B57\u6BB5\u7C7B\u578B\u53D8\u66F4\uFF08\u68C0\u6D4B\u5230\u4E0D\u517C\u5BB9/\u6536\u7F29\u53D8\u66F4\uFF09:");
|
|
10272
|
+
for (const line of lines) {
|
|
10273
|
+
msgLines.push(line);
|
|
10274
|
+
}
|
|
10275
|
+
msgLines.push("\u8BF4\u660E: \u4EC5\u5141\u8BB8\u540C\u7C7B\u578B\u7684\u5BBD\u5316\u53D8\u66F4\uFF08\u5982 TINYINT->SMALLINT->INT->BIGINT\uFF09\uFF0C\u4EE5\u53CA\u90E8\u5206\u517C\u5BB9\u53D8\u66F4\uFF08\u5982 VARCHAR->TEXT\u3001CHAR/VARCHAR \u4E92\u8F6C\u3001float->double\uFF09\u3002");
|
|
10276
|
+
msgLines.push("\u63D0\u793A: \u82E5\u786E\u9700\u6536\u7F29\uFF0C\u8BF7\u5148\u624B\u5DE5\u8FC1\u79FB/\u6E05\u6D17\u6570\u636E\u540E\u518D\u6267\u884C\u540C\u6B65\u3002");
|
|
10277
|
+
throw new Error(msgLines.join(`
|
|
10278
|
+
`));
|
|
10279
|
+
}
|
|
10280
|
+
for (const task of tableTasks) {
|
|
10281
|
+
if (!task.existsTable) {
|
|
10282
|
+
continue;
|
|
10283
|
+
}
|
|
10284
|
+
if (!task.existingColumns || !task.existingIndexes) {
|
|
10285
|
+
throw new Error(`\u540C\u6B65\u8868\uFF1A\u5185\u90E8\u9519\u8BEF\uFF1A\u9884\u68C0\u9636\u6BB5\u7F3A\u5931\u8868\u5143\u4FE1\u606F\uFF08\u8868=${task.tableName}\uFF09`);
|
|
10286
|
+
}
|
|
10287
|
+
const built = SyncTable.buildTablePlan({
|
|
10288
|
+
tableName: task.tableName,
|
|
10289
|
+
fields: task.tableFields,
|
|
10290
|
+
existingColumns: task.existingColumns,
|
|
10291
|
+
existingIndexes: task.existingIndexes
|
|
10292
|
+
});
|
|
10293
|
+
task.plan = built.plan;
|
|
10294
|
+
task.planSummary = built.summary;
|
|
10295
|
+
task.planDetails = built.details;
|
|
10296
|
+
}
|
|
10297
|
+
for (const task of tableTasks) {
|
|
10298
|
+
const item = task.item;
|
|
10299
|
+
const tableName = task.tableName;
|
|
10300
|
+
const tableFields = task.tableFields;
|
|
10301
|
+
const existsTable = task.existsTable;
|
|
10227
10302
|
try {
|
|
10228
10303
|
if (existsTable) {
|
|
10229
|
-
|
|
10304
|
+
const plan = task.plan;
|
|
10305
|
+
const summary = task.planSummary;
|
|
10306
|
+
const details = task.planDetails;
|
|
10307
|
+
if (plan && plan.changed && summary && details) {
|
|
10308
|
+
const msg = `[\u8868 ${tableName}] \u53D8\u66F4\u6C47\u603B\uFF0C\u65B0\u589E\u5B57\u6BB5=${summary.addedBusiness}\uFF0C\u65B0\u589E\u7CFB\u7EDF\u5B57\u6BB5=${summary.addedSystem}\uFF0C\u4FEE\u6539\u5B57\u6BB5=${summary.modified}\uFF0C\u7D22\u5F15\u53D8\u66F4=${summary.indexChanges}`;
|
|
10309
|
+
const detailLines = details.fieldChanges.flatMap((d) => d.changes.map((change) => {
|
|
10310
|
+
const current = String(change.current ?? "");
|
|
10311
|
+
const expected = String(change.expected ?? "");
|
|
10312
|
+
return `- ${d.dbFieldName}.${change.type}: ${current} -> ${expected}`;
|
|
10313
|
+
}));
|
|
10314
|
+
const indexLines = details.indexChanges.map((change) => {
|
|
10315
|
+
const indexLabel = `idx_${change.fieldName}` === change.indexName ? change.indexName : change.indexName;
|
|
10316
|
+
if (change.action === "create") {
|
|
10317
|
+
return `- index.${indexLabel}: \u65E0 -> ${change.indexName}(${change.fieldName})`;
|
|
10318
|
+
}
|
|
10319
|
+
return `- index.${indexLabel}: ${change.indexName}(${change.fieldName}) -> \u65E0`;
|
|
10320
|
+
});
|
|
10321
|
+
const allLines = [];
|
|
10322
|
+
for (const line of detailLines) {
|
|
10323
|
+
allLines.push(line);
|
|
10324
|
+
}
|
|
10325
|
+
for (const line of indexLines) {
|
|
10326
|
+
allLines.push(line);
|
|
10327
|
+
}
|
|
10328
|
+
Logger.debug(msg);
|
|
10329
|
+
for (const line of allLines) {
|
|
10330
|
+
Logger.debug(`[\u8868 ${tableName}] \u53D8\u66F4\u660E\u7EC6 ${line}`);
|
|
10331
|
+
}
|
|
10332
|
+
await SyncTable.applyTablePlan(this.db, tableName, plan);
|
|
10333
|
+
}
|
|
10230
10334
|
} else {
|
|
10231
10335
|
await SyncTable.createTable(this.db, tableName, tableFields);
|
|
10232
10336
|
}
|
|
@@ -10318,7 +10422,7 @@ class SyncTable {
|
|
|
10318
10422
|
return unsigned ? `${base} UNSIGNED` : base;
|
|
10319
10423
|
}
|
|
10320
10424
|
const baseType = typeMapping[normalizedType] || "TEXT";
|
|
10321
|
-
if (SyncTable.INT_TYPES.has(normalizedType) && unsigned) {
|
|
10425
|
+
if ((SyncTable.INT_TYPES.has(normalizedType) || SyncTable.FLOAT_TYPES.has(normalizedType)) && unsigned) {
|
|
10322
10426
|
return `${baseType} UNSIGNED`;
|
|
10323
10427
|
}
|
|
10324
10428
|
return baseType;
|
|
@@ -10335,6 +10439,8 @@ class SyncTable {
|
|
|
10335
10439
|
case "int":
|
|
10336
10440
|
case "bigint":
|
|
10337
10441
|
case "decimal":
|
|
10442
|
+
case "float":
|
|
10443
|
+
case "double":
|
|
10338
10444
|
return 0;
|
|
10339
10445
|
case "char":
|
|
10340
10446
|
case "varchar":
|
|
@@ -10356,7 +10462,7 @@ class SyncTable {
|
|
|
10356
10462
|
if (SyncTable.TEXT_FAMILY.has(normalizedType) || normalizedType === "json" || actualDefault === "null") {
|
|
10357
10463
|
return "";
|
|
10358
10464
|
}
|
|
10359
|
-
if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.DECIMAL_TYPES.has(normalizedType) || SyncTable.isStringOrArrayType(normalizedType)) {
|
|
10465
|
+
if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.DECIMAL_TYPES.has(normalizedType) || SyncTable.FLOAT_TYPES.has(normalizedType) || SyncTable.isStringOrArrayType(normalizedType)) {
|
|
10360
10466
|
if (typeof actualDefault === "number" && !Number.isNaN(actualDefault)) {
|
|
10361
10467
|
return ` DEFAULT ${actualDefault}`;
|
|
10362
10468
|
} else {
|
|
@@ -10383,7 +10489,7 @@ class SyncTable {
|
|
|
10383
10489
|
return "null";
|
|
10384
10490
|
const normalizedType = String(fieldType || "").toLowerCase();
|
|
10385
10491
|
const raw = String(value).trim();
|
|
10386
|
-
if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.DECIMAL_TYPES.has(normalizedType)) {
|
|
10492
|
+
if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.DECIMAL_TYPES.has(normalizedType) || SyncTable.FLOAT_TYPES.has(normalizedType)) {
|
|
10387
10493
|
if (/^[+-]?\d+(?:\.\d+)?$/.test(raw)) {
|
|
10388
10494
|
let sign = "";
|
|
10389
10495
|
let body = raw;
|
|
@@ -10579,6 +10685,32 @@ class SyncTable {
|
|
|
10579
10685
|
throw new Error([`\u7981\u6B62\u5B57\u6BB5\u7C7B\u578B\u53D8\u66F4: ${tableName}.${dbFieldName}`, `\u5F53\u524D\u7C7B\u578B: ${typeChange.current}`, `\u76EE\u6807\u7C7B\u578B: ${typeChange.expected}`, "\u8BF4\u660E: \u4EC5\u5141\u8BB8\u5BBD\u5316\u578B\u53D8\u66F4\uFF08\u5982 INT->BIGINT, VARCHAR->TEXT\uFF09\uFF0C\u4EE5\u53CA CHAR/VARCHAR \u4E92\u8F6C\uFF1BDATETIME \u4E0E BIGINT \u4E0D\u5141\u8BB8\u4E92\u8F6C\uFF08\u9700\u8981\u624B\u52A8\u8FC1\u79FB\u6570\u636E\uFF09"].join(`
|
|
10580
10686
|
`));
|
|
10581
10687
|
}
|
|
10688
|
+
static collectIncompatibleTypeChanges(tableName, existingColumns, fields) {
|
|
10689
|
+
const out = [];
|
|
10690
|
+
for (const [fieldKey, fieldDef] of Object.entries(fields)) {
|
|
10691
|
+
const dbFieldName = snakeCase(fieldKey);
|
|
10692
|
+
const existing = existingColumns[dbFieldName];
|
|
10693
|
+
if (!existing)
|
|
10694
|
+
continue;
|
|
10695
|
+
const comparison = SyncTable.compareFieldDefinition(existing, fieldDef);
|
|
10696
|
+
const typeChange = comparison.find((c) => c.type === "datatype");
|
|
10697
|
+
if (!typeChange)
|
|
10698
|
+
continue;
|
|
10699
|
+
const currentType = String(typeChange.current || "").toLowerCase();
|
|
10700
|
+
const expectedType = String(typeChange.expected || "").toLowerCase();
|
|
10701
|
+
const currentBase = currentType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
|
|
10702
|
+
const expectedBase = expectedType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
|
|
10703
|
+
if (currentBase !== expectedBase && !SyncTable.isCompatibleTypeChange(currentType, expectedType)) {
|
|
10704
|
+
out.push({
|
|
10705
|
+
tableName,
|
|
10706
|
+
dbFieldName,
|
|
10707
|
+
currentType: String(typeChange.current ?? ""),
|
|
10708
|
+
expectedType: String(typeChange.expected ?? "")
|
|
10709
|
+
});
|
|
10710
|
+
}
|
|
10711
|
+
}
|
|
10712
|
+
return out;
|
|
10713
|
+
}
|
|
10582
10714
|
static truncateForLog(input, maxLen) {
|
|
10583
10715
|
const s = String(input);
|
|
10584
10716
|
if (maxLen <= 0)
|
|
@@ -10683,6 +10815,8 @@ SQL: ${sqlLine}
|
|
|
10683
10815
|
if (nIntIdx > cIntIdx)
|
|
10684
10816
|
return true;
|
|
10685
10817
|
}
|
|
10818
|
+
if (cBase === "float" && nBase === "double")
|
|
10819
|
+
return true;
|
|
10686
10820
|
if (cBase === "varchar" && (nBase === "text" || nBase === "mediumtext" || nBase === "longtext"))
|
|
10687
10821
|
return true;
|
|
10688
10822
|
if (cBase === "char" && nBase === "varchar" || cBase === "varchar" && nBase === "char") {
|