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 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
- await SyncTable.modifyTable(this.db, this.dbName, tableName, tableFields);
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") {