befly 3.15.17 → 3.15.18

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
@@ -49,6 +49,84 @@ function isPlainObject(value) {
49
49
  const proto = Object.getPrototypeOf(value);
50
50
  return proto === Object.prototype || proto === null;
51
51
  }
52
+ function isJsonPrimitive(value) {
53
+ if (value === null) {
54
+ return true;
55
+ }
56
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
57
+ }
58
+ function isJsonObject(value) {
59
+ if (!value || typeof value !== "object") {
60
+ return false;
61
+ }
62
+ if (value instanceof Date) {
63
+ return false;
64
+ }
65
+ if (Array.isArray(value)) {
66
+ return false;
67
+ }
68
+ return true;
69
+ }
70
+ function isJsonStructure(value) {
71
+ return Array.isArray(value) || isJsonObject(value);
72
+ }
73
+ function isJsonValue(value) {
74
+ if (isJsonPrimitive(value)) {
75
+ return true;
76
+ }
77
+ if (Array.isArray(value)) {
78
+ for (const item of value) {
79
+ if (!isJsonValue(item)) {
80
+ return false;
81
+ }
82
+ }
83
+ return true;
84
+ }
85
+ if (isJsonObject(value)) {
86
+ for (const v of Object.values(value)) {
87
+ if (v === undefined) {
88
+ continue;
89
+ }
90
+ if (!isJsonValue(v)) {
91
+ return false;
92
+ }
93
+ }
94
+ return true;
95
+ }
96
+ return false;
97
+ }
98
+ function isFiniteNumber(value) {
99
+ return typeof value === "number" && Number.isFinite(value);
100
+ }
101
+ function isIntegerNumber(value) {
102
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value);
103
+ }
104
+ function formatValuePreview(value) {
105
+ if (value === null) {
106
+ return "null";
107
+ }
108
+ if (value === undefined) {
109
+ return "undefined";
110
+ }
111
+ if (typeof value === "string") {
112
+ const s2 = value.length > 80 ? `${value.slice(0, 80)}...` : value;
113
+ return JSON.stringify(s2);
114
+ }
115
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
116
+ return String(value);
117
+ }
118
+ if (typeof value === "function") {
119
+ return "[Function]";
120
+ }
121
+ try {
122
+ const s2 = JSON.stringify(value);
123
+ if (typeof s2 === "string") {
124
+ return s2.length > 120 ? `${s2.slice(0, 120)}...` : s2;
125
+ }
126
+ } catch {}
127
+ const s = String(value);
128
+ return s.length > 120 ? `${s.slice(0, 120)}...` : s;
129
+ }
52
130
  function getTypeTag(value) {
53
131
  if (value === null)
54
132
  return "null";
@@ -71,6 +149,38 @@ function getTypeTag(value) {
71
149
  return "function";
72
150
  return "object";
73
151
  }
152
+ function isRegexLiteral(input) {
153
+ const trimmed = String(input || "").trim();
154
+ if (trimmed.length < 2) {
155
+ return false;
156
+ }
157
+ if (!trimmed.startsWith("/")) {
158
+ return false;
159
+ }
160
+ const lastSlash = trimmed.lastIndexOf("/");
161
+ if (lastSlash <= 0) {
162
+ return false;
163
+ }
164
+ const flags = trimmed.slice(lastSlash + 1);
165
+ if (flags !== "" && !/^[gimsuy]+$/.test(flags)) {
166
+ return false;
167
+ }
168
+ return true;
169
+ }
170
+ function isRegexInput(input) {
171
+ const trimmed = String(input || "").trim();
172
+ return isRegexLiteral(trimmed) || trimmed.startsWith("@");
173
+ }
174
+ function isEnumInput(input) {
175
+ const trimmed = String(input || "").trim();
176
+ if (trimmed.length === 0) {
177
+ return false;
178
+ }
179
+ return !trimmed.startsWith("/") && trimmed.includes("|");
180
+ }
181
+ function isAliasInput(input) {
182
+ return String(input || "").trim().startsWith("@");
183
+ }
74
184
  function normalizePositiveInt(value, fallback, min, max) {
75
185
  if (typeof value !== "number")
76
186
  return fallback;
@@ -8181,79 +8291,13 @@ async function checkPlugin(plugins) {
8181
8291
 
8182
8292
  // checks/checkTable.ts
8183
8293
  init_logger();
8184
- function isJsonPrimitive(value) {
8185
- if (value === null)
8186
- return true;
8187
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
8188
- }
8189
- function isJsonObject(value) {
8190
- if (!value || typeof value !== "object")
8191
- return false;
8192
- if (value instanceof Date)
8193
- return false;
8194
- if (Array.isArray(value))
8195
- return false;
8196
- return true;
8197
- }
8198
- function isJsonValue(value) {
8199
- if (isJsonPrimitive(value))
8200
- return true;
8201
- if (Array.isArray(value)) {
8202
- for (const item of value) {
8203
- if (!isJsonValue(item))
8204
- return false;
8205
- }
8206
- return true;
8207
- }
8208
- if (isJsonObject(value)) {
8209
- for (const v of Object.values(value)) {
8210
- if (v === undefined)
8211
- continue;
8212
- if (!isJsonValue(v))
8213
- return false;
8214
- }
8215
- return true;
8216
- }
8217
- return false;
8218
- }
8219
- function formatValuePreview(value) {
8220
- if (value === null)
8221
- return "null";
8222
- if (value === undefined)
8223
- return "undefined";
8224
- if (typeof value === "string") {
8225
- const s2 = value.length > 80 ? `${value.slice(0, 80)}...` : value;
8226
- return JSON.stringify(s2);
8227
- }
8228
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
8229
- return String(value);
8230
- }
8231
- if (typeof value === "function") {
8232
- return "[Function]";
8233
- }
8234
- try {
8235
- const s2 = JSON.stringify(value);
8236
- if (typeof s2 === "string") {
8237
- return s2.length > 120 ? `${s2.slice(0, 120)}...` : s2;
8238
- }
8239
- } catch {}
8240
- const s = String(value);
8241
- return s.length > 120 ? `${s.slice(0, 120)}...` : s;
8242
- }
8294
+ init_util();
8243
8295
  var RESERVED_FIELDS = ["id", "created_at", "updated_at", "deleted_at", "state"];
8244
8296
  var RESERVED_FIELD_SET = new Set(RESERVED_FIELDS);
8245
- var FIELD_TYPES = ["tinyint", "smallint", "mediumint", "int", "bigint", "char", "varchar", "tinytext", "text", "mediumtext", "longtext", "datetime", "json"];
8297
+ var FIELD_TYPES = ["tinyint", "smallint", "mediumint", "int", "bigint", "decimal", "char", "varchar", "tinytext", "text", "mediumtext", "longtext", "datetime", "json"];
8246
8298
  var FIELD_TYPE_SET = new Set(FIELD_TYPES);
8247
8299
  var INPUT_TYPES = ["number", "integer", "string", "char", "array", "array_number", "array_integer", "json", "json_number", "json_integer"];
8248
8300
  var INPUT_TYPE_SET = new Set(INPUT_TYPES);
8249
- function isRegexInput(input) {
8250
- if (input.startsWith("@"))
8251
- return true;
8252
- return /[\\^$.*+?()[\]{}]/.test(input);
8253
- }
8254
- function isEnumInput(input) {
8255
- return input.includes("|") && !isRegexInput(input);
8256
- }
8257
8301
  function inferInputByType(dbType) {
8258
8302
  switch (dbType.toLowerCase()) {
8259
8303
  case "tinyint":
@@ -8262,6 +8306,8 @@ function inferInputByType(dbType) {
8262
8306
  case "int":
8263
8307
  case "bigint":
8264
8308
  return "integer";
8309
+ case "decimal":
8310
+ return "number";
8265
8311
  case "char":
8266
8312
  case "varchar":
8267
8313
  case "tinytext":
@@ -8285,7 +8331,7 @@ function normalizeTypeAndInput(type, input) {
8285
8331
  input: rawInput || inferInputByType(rawType)
8286
8332
  };
8287
8333
  }
8288
- var ALLOWED_FIELD_PROPERTIES = ["name", "type", "input", "min", "max", "default", "detail", "index", "unique", "nullable", "unsigned"];
8334
+ var ALLOWED_FIELD_PROPERTIES = ["name", "type", "input", "min", "max", "default", "detail", "precision", "scale", "index", "unique", "nullable", "unsigned"];
8289
8335
  var ALLOWED_FIELD_PROPERTY_SET = new Set(ALLOWED_FIELD_PROPERTIES);
8290
8336
  var LOWER_CAMEL_CASE_REGEX = /^_?[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
8291
8337
  var FIELD_NAME_REGEX = /^[\u4e00-\u9fa5a-zA-Z0-9 _-]+$/;
@@ -8324,8 +8370,9 @@ async function checkTable(tables, config2) {
8324
8370
  hasError = true;
8325
8371
  continue;
8326
8372
  }
8327
- if (RESERVED_FIELD_SET.has(colKey)) {
8328
- Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6\u5305\u542B\u4FDD\u7559\u5B57\u6BB5 ${colKey}\uFF0C` + `\u4E0D\u80FD\u5728\u8868\u5B9A\u4E49\u4E2D\u4F7F\u7528\u4EE5\u4E0B\u5B57\u6BB5: ${RESERVED_FIELDS.join(", ")}`);
8373
+ const dbFieldName = snakeCase(colKey);
8374
+ if (RESERVED_FIELD_SET.has(colKey) || RESERVED_FIELD_SET.has(dbFieldName)) {
8375
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6\u5305\u542B\u4FDD\u7559\u5B57\u6BB5 ${colKey}\uFF08\u6620\u5C04\u5217\u540D: ${dbFieldName}\uFF09\uFF0C` + `\u4E0D\u80FD\u5728\u8868\u5B9A\u4E49\u4E2D\u4F7F\u7528\u4EE5\u4E0B\u5B57\u6BB5: ${RESERVED_FIELDS.join(", ")}`);
8329
8376
  hasError = true;
8330
8377
  }
8331
8378
  const field = fieldDef;
@@ -8361,6 +8408,14 @@ async function checkTable(tables, config2) {
8361
8408
  Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 detail \u7C7B\u578B\u9519\u8BEF\uFF0C\u5FC5\u987B\u4E3A\u5B57\u7B26\u4E32`);
8362
8409
  hasError = true;
8363
8410
  }
8411
+ if (field.precision !== undefined && !(field.precision === null || typeof field.precision === "number")) {
8412
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 precision \u7C7B\u578B\u9519\u8BEF\uFF0C\u5FC5\u987B\u4E3A null \u6216\u6570\u5B57`);
8413
+ hasError = true;
8414
+ }
8415
+ if (field.scale !== undefined && !(field.scale === null || typeof field.scale === "number")) {
8416
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 scale \u7C7B\u578B\u9519\u8BEF\uFF0C\u5FC5\u987B\u4E3A null \u6216\u6570\u5B57`);
8417
+ hasError = true;
8418
+ }
8364
8419
  if (field.index !== undefined && typeof field.index !== "boolean") {
8365
8420
  Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 index \u7C7B\u578B\u9519\u8BEF\uFF0C\u5FC5\u987B\u4E3A\u5E03\u5C14\u503C`);
8366
8421
  hasError = true;
@@ -8398,25 +8453,31 @@ async function checkTable(tables, config2) {
8398
8453
  if (normalizedInput === "") {
8399
8454
  Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input \u65E0\u6CD5\u63A8\u5BFC`);
8400
8455
  hasError = true;
8401
- } else if (!INPUT_TYPE_SET.has(normalizedInput) && !isRegexInput(normalizedInput) && !isEnumInput(normalizedInput)) {
8402
- Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input "${normalizedInput}" \u4E0D\u5408\u6CD5\uFF0C` + `\u5FC5\u987B\u4E3A${INPUT_TYPES.join("\u3001")}\u4E4B\u4E00\uFF0C\u6216\u6B63\u5219/\u679A\u4E3E`);
8456
+ } else if (!INPUT_TYPE_SET.has(normalizedInput) && !isRegexInput(normalizedInput) && !isEnumInput(normalizedInput) && !isAliasInput(normalizedInput)) {
8457
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input "${normalizedInput}" \u4E0D\u5408\u6CD5\uFF0C` + `\u5FC5\u987B\u4E3A${INPUT_TYPES.join("\u3001")}\u4E4B\u4E00\uFF0C\u6216\u6B63\u5219/\u679A\u4E3E/\u6B63\u5219\u522B\u540D`);
8403
8458
  hasError = true;
8404
8459
  }
8405
8460
  const isStringDbType = ["char", "varchar", "tinytext", "text", "mediumtext", "longtext"].includes(effectiveType);
8406
8461
  const isTextDbType = ["tinytext", "text", "mediumtext", "longtext"].includes(effectiveType);
8407
8462
  const isIntDbType = ["tinyint", "smallint", "mediumint", "int", "bigint"].includes(effectiveType);
8463
+ const isDecimalDbType = effectiveType === "decimal";
8408
8464
  const isJsonDbType = effectiveType === "json";
8409
- if (isRegexInput(normalizedInput) || isEnumInput(normalizedInput)) {
8465
+ const isNumericDbType = isIntDbType || isDecimalDbType;
8466
+ if (isRegexInput(normalizedInput) || isEnumInput(normalizedInput) || isAliasInput(normalizedInput)) {
8410
8467
  if (!isStringDbType) {
8411
- Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input \u4F7F\u7528\u6B63\u5219/\u679A\u4E3E\uFF0C\u4EC5\u5141\u8BB8\u5B57\u7B26\u4E32\u7C7B\u5B57\u6BB5\uFF08char/varchar/text\uFF09`);
8468
+ 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`);
8412
8469
  hasError = true;
8413
8470
  }
8414
8471
  }
8415
8472
  if (normalizedInput === "number" || normalizedInput === "integer") {
8416
- if (!isIntDbType) {
8473
+ if (normalizedInput === "integer" && !isIntDbType) {
8417
8474
  Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input=${normalizedInput} \u4EC5\u5141\u8BB8\u6574\u6570\u7C7B\u5B57\u6BB5\uFF08tinyint/smallint/mediumint/int/bigint\uFF09`);
8418
8475
  hasError = true;
8419
8476
  }
8477
+ if (normalizedInput === "number" && !isNumericDbType) {
8478
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5 input=${normalizedInput} \u4EC5\u5141\u8BB8\u6570\u503C\u7C7B\u5B57\u6BB5\uFF08tinyint/smallint/mediumint/int/bigint/decimal\uFF09`);
8479
+ hasError = true;
8480
+ }
8420
8481
  }
8421
8482
  if (normalizedInput === "array" || normalizedInput === "array_number" || normalizedInput === "array_integer") {
8422
8483
  if (!isStringDbType) {
@@ -8430,8 +8491,8 @@ async function checkTable(tables, config2) {
8430
8491
  hasError = true;
8431
8492
  }
8432
8493
  }
8433
- if (!isIntDbType && field.unsigned !== undefined) {
8434
- Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5\u7C7B\u578B\u4E3A ${effectiveType}\uFF0C\u4E0D\u5141\u8BB8\u8BBE\u7F6E unsigned\uFF08\u4EC5\u6574\u6570\u7C7B\u578B\u6709\u6548\uFF09`);
8494
+ if (!isNumericDbType && field.unsigned !== undefined) {
8495
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u5B57\u6BB5\u7C7B\u578B\u4E3A ${effectiveType}\uFF0C\u4E0D\u5141\u8BB8\u8BBE\u7F6E unsigned\uFF08\u4EC5\u6570\u503C\u7C7B\u578B\u6709\u6548\uFF09`);
8435
8496
  hasError = true;
8436
8497
  }
8437
8498
  if (field.unique === true && field.index === true) {
@@ -8482,6 +8543,31 @@ async function checkTable(tables, config2) {
8482
8543
  Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A datetime \u7C7B\u578B\uFF0C\u4E0D\u5141\u8BB8\u8BBE\u7F6E unsigned`);
8483
8544
  hasError = true;
8484
8545
  }
8546
+ } else if (isDecimalDbType) {
8547
+ const precision = field.precision;
8548
+ const scale = field.scale;
8549
+ if (typeof precision !== "number") {
8550
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A decimal \u7C7B\u578B\uFF0C\u5FC5\u987B\u8BBE\u7F6E precision\uFF08\u603B\u4F4D\u6570\uFF09`);
8551
+ hasError = true;
8552
+ } else if (precision < 1 || precision > 65) {
8553
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A decimal \u7C7B\u578B\uFF0Cprecision \u5FC5\u987B\u5728 1..65 \u8303\u56F4\u5185\uFF0C\u5F53\u524D\u4E3A ${precision}`);
8554
+ hasError = true;
8555
+ }
8556
+ if (typeof scale !== "number") {
8557
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A decimal \u7C7B\u578B\uFF0C\u5FC5\u987B\u8BBE\u7F6E scale\uFF08\u5C0F\u6570\u4F4D\u6570\uFF09`);
8558
+ hasError = true;
8559
+ } else if (scale < 0 || scale > 30) {
8560
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A decimal \u7C7B\u578B\uFF0Cscale \u5FC5\u987B\u5728 0..30 \u8303\u56F4\u5185\uFF0C\u5F53\u524D\u4E3A ${scale}`);
8561
+ hasError = true;
8562
+ }
8563
+ if (typeof precision === "number" && typeof scale === "number" && scale > precision) {
8564
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A decimal \u7C7B\u578B\uFF0Cscale \u4E0D\u80FD\u5927\u4E8E precision\uFF08precision=${precision}, scale=${scale}\uFF09`);
8565
+ hasError = true;
8566
+ }
8567
+ if (field.default !== undefined && field.default !== null && typeof field.default !== "number") {
8568
+ Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A decimal \u7C7B\u578B\uFF0C\u9ED8\u8BA4\u503C\u5FC5\u987B\u4E3A\u6570\u5B57\u6216 null` + `\uFF08typeof=${typeof field.default}\uFF0Cvalue=${formatValuePreview(field.default)}\uFF09`);
8569
+ hasError = true;
8570
+ }
8485
8571
  } else if (effectiveType === "char" || effectiveType === "varchar") {
8486
8572
  if (field.max === undefined || field.max === null || typeof field.max !== "number") {
8487
8573
  Logger.warn(`${tablePrefix}${fileName} \u6587\u4EF6 ${colKey} \u4E3A ${effectiveType} \u7C7B\u578B\uFF0C` + `\u5FC5\u987B\u8BBE\u7F6E max \u4E14\u7C7B\u578B\u4E3A\u6570\u5B57\uFF0C\u5F53\u524D\u4E3A "${field.max}"`);
@@ -9900,6 +9986,8 @@ function inferInputByType2(dbType) {
9900
9986
  case "int":
9901
9987
  case "bigint":
9902
9988
  return "integer";
9989
+ case "decimal":
9990
+ return "number";
9903
9991
  case "char":
9904
9992
  case "varchar":
9905
9993
  case "tinytext":
@@ -9936,6 +10024,8 @@ function normalizeFieldDefinition(fieldDef) {
9936
10024
  detail: fieldDef.detail ?? "",
9937
10025
  min: fieldDef.min ?? null,
9938
10026
  max: fieldDef.max ?? null,
10027
+ precision: fieldDef.precision ?? null,
10028
+ scale: fieldDef.scale ?? null,
9939
10029
  default: normalizedDefault,
9940
10030
  index: fieldDef.index ?? false,
9941
10031
  unique: fieldDef.unique ?? false,
@@ -10093,6 +10183,7 @@ class SyncTable {
10093
10183
  mediumint: "MEDIUMINT",
10094
10184
  int: "INT",
10095
10185
  bigint: "BIGINT",
10186
+ decimal: "DECIMAL",
10096
10187
  char: "CHAR",
10097
10188
  varchar: "VARCHAR",
10098
10189
  datetime: "DATETIME",
@@ -10104,6 +10195,7 @@ class SyncTable {
10104
10195
  };
10105
10196
  static TEXT_FAMILY = new Set(["tinytext", "text", "mediumtext", "longtext"]);
10106
10197
  static INT_TYPES = new Set(["tinyint", "smallint", "mediumint", "int", "bigint"]);
10198
+ static DECIMAL_TYPES = new Set(["decimal"]);
10107
10199
  db;
10108
10200
  dbName;
10109
10201
  constructor(ctx) {
@@ -10187,6 +10279,8 @@ class SyncTable {
10187
10279
  fieldDef.detail = normalized.detail;
10188
10280
  fieldDef.min = normalized.min;
10189
10281
  fieldDef.max = normalized.max;
10282
+ fieldDef.precision = normalized.precision;
10283
+ fieldDef.scale = normalized.scale;
10190
10284
  fieldDef.default = normalized.default;
10191
10285
  fieldDef.index = normalized.index;
10192
10286
  fieldDef.unique = normalized.unique;
@@ -10207,7 +10301,7 @@ class SyncTable {
10207
10301
  }
10208
10302
  return `\`${trimmed}\``;
10209
10303
  }
10210
- static getSqlType(fieldType, fieldMax, unsigned = false) {
10304
+ static getSqlType(fieldType, fieldMax, unsigned = false, precision = null, scale = null) {
10211
10305
  const normalizedType = String(fieldType || "").toLowerCase();
10212
10306
  const typeMapping = SyncTable.TYPE_MAPPING;
10213
10307
  if (SyncTable.isStringOrArrayType(normalizedType)) {
@@ -10216,6 +10310,13 @@ class SyncTable {
10216
10310
  }
10217
10311
  return `${typeMapping[normalizedType]}(${fieldMax})`;
10218
10312
  }
10313
+ if (SyncTable.DECIMAL_TYPES.has(normalizedType)) {
10314
+ if (typeof precision !== "number" || typeof scale !== "number") {
10315
+ throw new Error(`\u540C\u6B65\u8868\uFF1A\u5185\u90E8\u9519\u8BEF\uFF1Adecimal \u7C7B\u578B\u7F3A\u5931 precision/scale\uFF08\u5E94\u7531 checkTable \u963B\u65AD\uFF09`);
10316
+ }
10317
+ const base = `${typeMapping[normalizedType]}(${precision},${scale})`;
10318
+ return unsigned ? `${base} UNSIGNED` : base;
10319
+ }
10219
10320
  const baseType = typeMapping[normalizedType] || "TEXT";
10220
10321
  if (SyncTable.INT_TYPES.has(normalizedType) && unsigned) {
10221
10322
  return `${baseType} UNSIGNED`;
@@ -10233,6 +10334,7 @@ class SyncTable {
10233
10334
  case "mediumint":
10234
10335
  case "int":
10235
10336
  case "bigint":
10337
+ case "decimal":
10236
10338
  return 0;
10237
10339
  case "char":
10238
10340
  case "varchar":
@@ -10254,7 +10356,7 @@ class SyncTable {
10254
10356
  if (SyncTable.TEXT_FAMILY.has(normalizedType) || normalizedType === "json" || actualDefault === "null") {
10255
10357
  return "";
10256
10358
  }
10257
- if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.isStringOrArrayType(normalizedType)) {
10359
+ if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.DECIMAL_TYPES.has(normalizedType) || SyncTable.isStringOrArrayType(normalizedType)) {
10258
10360
  if (typeof actualDefault === "number" && !Number.isNaN(actualDefault)) {
10259
10361
  return ` DEFAULT ${actualDefault}`;
10260
10362
  } else {
@@ -10276,10 +10378,31 @@ class SyncTable {
10276
10378
  }
10277
10379
  return "";
10278
10380
  }
10279
- static normalizeDefaultForCompare(value) {
10381
+ static normalizeDefaultForCompare(value, fieldType) {
10280
10382
  if (value === null || value === undefined)
10281
10383
  return "null";
10282
- return String(value);
10384
+ const normalizedType = String(fieldType || "").toLowerCase();
10385
+ const raw = String(value).trim();
10386
+ if (SyncTable.INT_TYPES.has(normalizedType) || SyncTable.DECIMAL_TYPES.has(normalizedType)) {
10387
+ if (/^[+-]?\d+(?:\.\d+)?$/.test(raw)) {
10388
+ let sign = "";
10389
+ let body = raw;
10390
+ if (body.startsWith("+") || body.startsWith("-")) {
10391
+ sign = body.startsWith("-") ? "-" : "";
10392
+ body = body.slice(1);
10393
+ }
10394
+ const parts = body.split(".");
10395
+ const intPart = parts[0] || "0";
10396
+ const fracPart = parts[1] || "";
10397
+ const normalizedInt = intPart.replace(/^0+(\d)/, "$1");
10398
+ const normalizedFrac = fracPart.replace(/0+$/g, "");
10399
+ const mergedInt = normalizedInt === "" ? "0" : normalizedInt;
10400
+ const merged = normalizedFrac.length > 0 ? `${mergedInt}.${normalizedFrac}` : mergedInt;
10401
+ const normalized = sign === "-" && merged !== "0" ? `-${merged}` : merged;
10402
+ return normalized;
10403
+ }
10404
+ }
10405
+ return raw;
10283
10406
  }
10284
10407
  static buildIndexClause(indexName, fieldName, action) {
10285
10408
  const indexQuoted = SyncTable.quoteIdentifier(indexName);
@@ -10337,7 +10460,7 @@ class SyncTable {
10337
10460
  const normalized = normalizeFieldDefinition(fieldDef);
10338
10461
  const dbFieldName = snakeCase(fieldKey);
10339
10462
  const colQuoted = SyncTable.quoteIdentifier(dbFieldName);
10340
- const sqlType = SyncTable.getSqlType(normalized.type, normalized.max, normalized.unsigned);
10463
+ const sqlType = SyncTable.getSqlType(normalized.type, normalized.max, normalized.unsigned, normalized.precision, normalized.scale);
10341
10464
  const actualDefault = SyncTable.resolveDefaultValue(normalized.default, normalized.type);
10342
10465
  const defaultSql = SyncTable.generateDefaultSql(actualDefault, normalized.type);
10343
10466
  const uniqueSql = normalized.unique ? " UNIQUE" : "";
@@ -10381,6 +10504,10 @@ class SyncTable {
10381
10504
  addedSystem,
10382
10505
  modified,
10383
10506
  indexChanges: indexActions.length
10507
+ },
10508
+ details: {
10509
+ fieldChanges: fieldPlan.changeDetails,
10510
+ indexChanges: indexPlan.indexActions
10384
10511
  }
10385
10512
  };
10386
10513
  }
@@ -10388,12 +10515,14 @@ class SyncTable {
10388
10515
  const alterClauses = [];
10389
10516
  let addedBusiness = 0;
10390
10517
  let modified = 0;
10518
+ const changeDetails = [];
10391
10519
  for (const [fieldKey, fieldDef] of Object.entries(options.fields)) {
10392
10520
  const dbFieldName = snakeCase(fieldKey);
10393
10521
  if (options.existingColumns[dbFieldName]) {
10394
10522
  const comparison = SyncTable.compareFieldDefinition(options.existingColumns[dbFieldName], fieldDef);
10395
10523
  if (comparison.length > 0) {
10396
10524
  modified = modified + 1;
10525
+ changeDetails.push({ fieldKey, dbFieldName, changes: comparison });
10397
10526
  SyncTable.assertCompatibleTypeChange(options.tableName, dbFieldName, comparison, fieldDef);
10398
10527
  alterClauses.push(SyncTable.generateDDLClause(fieldKey, fieldDef, false));
10399
10528
  }
@@ -10402,7 +10531,7 @@ class SyncTable {
10402
10531
  alterClauses.push(SyncTable.generateDDLClause(fieldKey, fieldDef, true));
10403
10532
  }
10404
10533
  }
10405
- return { alterClauses, addedBusiness, modified };
10534
+ return { alterClauses, addedBusiness, modified, changeDetails };
10406
10535
  }
10407
10536
  static buildSystemFieldPlan(options) {
10408
10537
  const alterClauses = [];
@@ -10443,7 +10572,7 @@ class SyncTable {
10443
10572
  if (!typeChange)
10444
10573
  return;
10445
10574
  const currentType = String(typeChange.current || "").toLowerCase();
10446
- const expectedType = SyncTable.getSqlType(fieldDef.type, fieldDef.max ?? null, fieldDef.unsigned ?? false).toLowerCase();
10575
+ const expectedType = SyncTable.getSqlType(fieldDef.type, fieldDef.max ?? null, fieldDef.unsigned ?? false, fieldDef.precision ?? null, fieldDef.scale ?? null).toLowerCase();
10447
10576
  const currentBase = currentType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
10448
10577
  const expectedBase = expectedType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
10449
10578
  if (currentBase !== expectedBase && !SyncTable.isCompatibleTypeChange(currentType, expectedType))
@@ -10567,12 +10696,14 @@ SQL: ${sqlLine}
10567
10696
  static compareFieldDefinition(existingColumn, fieldDef) {
10568
10697
  const changes = [];
10569
10698
  const normalized = normalizeFieldDefinition(fieldDef);
10570
- const expectedType = SyncTable.getSqlType(normalized.type, normalized.max, normalized.unsigned).toLowerCase().replace(/\s+/g, " ").trim();
10699
+ const expectedType = SyncTable.getSqlType(normalized.type, normalized.max, normalized.unsigned, normalized.precision, normalized.scale).toLowerCase().replace(/\s+/g, " ").trim();
10571
10700
  const currentType = (typeof existingColumn.columnType === "string" && existingColumn.columnType.trim() !== "" ? existingColumn.columnType : typeof existingColumn.type === "string" && existingColumn.type.trim() !== "" ? SyncTable.isStringOrArrayType(normalized.type) && typeof existingColumn.max === "number" ? `${existingColumn.type.trim()}(${existingColumn.max})` : existingColumn.type.trim() : String(existingColumn.type ?? "")).toLowerCase().replace(/\s+/g, " ").trim();
10572
- if (currentType !== expectedType) {
10701
+ const currentBase = currentType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
10702
+ const normalizedCurrentType = SyncTable.INT_TYPES.has(currentBase) ? currentType.replace(/\([^)]*\)/g, "").replace(/\s+/g, " ").trim() : currentType;
10703
+ if (normalizedCurrentType !== expectedType) {
10573
10704
  changes.push({
10574
10705
  type: "datatype",
10575
- current: currentType,
10706
+ current: normalizedCurrentType,
10576
10707
  expected: expectedType
10577
10708
  });
10578
10709
  }
@@ -10584,12 +10715,26 @@ SQL: ${sqlLine}
10584
10715
  expected: expectedNullable
10585
10716
  });
10586
10717
  }
10587
- const expectedDefault = SyncTable.resolveDefaultValue(normalized.default, normalized.type);
10588
- if (SyncTable.normalizeDefaultForCompare(existingColumn.defaultValue) !== SyncTable.normalizeDefaultForCompare(expectedDefault)) {
10718
+ const normalizedType = String(normalized.type || "").toLowerCase();
10719
+ if (!SyncTable.TEXT_FAMILY.has(normalizedType) && normalizedType !== "json") {
10720
+ const expectedDefault = SyncTable.resolveDefaultValue(normalized.default, normalized.type);
10721
+ const currentDefaultNormalized = SyncTable.normalizeDefaultForCompare(existingColumn.defaultValue, normalized.type);
10722
+ const expectedDefaultNormalized = SyncTable.normalizeDefaultForCompare(expectedDefault, normalized.type);
10723
+ if (currentDefaultNormalized !== expectedDefaultNormalized) {
10724
+ changes.push({
10725
+ type: "default",
10726
+ current: existingColumn.defaultValue,
10727
+ expected: expectedDefault
10728
+ });
10729
+ }
10730
+ }
10731
+ const currentComment = String(existingColumn.comment ?? "");
10732
+ const expectedComment = String(normalized.name ?? "");
10733
+ if (currentComment !== expectedComment) {
10589
10734
  changes.push({
10590
- type: "default",
10591
- current: existingColumn.defaultValue,
10592
- expected: expectedDefault
10735
+ type: "comment",
10736
+ current: currentComment,
10737
+ expected: expectedComment
10593
10738
  });
10594
10739
  }
10595
10740
  return changes;
@@ -10703,7 +10848,7 @@ SQL: ${sqlLine}
10703
10848
  static async modifyTable(db, dbName, tableName, fields) {
10704
10849
  const existingColumns = await SyncTable.getTableColumns(db, dbName, tableName);
10705
10850
  const existingIndexes = await SyncTable.getTableIndexes(db, dbName, tableName);
10706
- const { plan, summary } = SyncTable.buildTablePlan({
10851
+ const { plan, summary, details } = SyncTable.buildTablePlan({
10707
10852
  tableName,
10708
10853
  fields,
10709
10854
  existingColumns,
@@ -10711,7 +10856,23 @@ SQL: ${sqlLine}
10711
10856
  });
10712
10857
  if (plan.changed) {
10713
10858
  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}`;
10859
+ const detailLines = details.fieldChanges.flatMap((item) => item.changes.map((change) => {
10860
+ const current = String(change.current ?? "");
10861
+ const expected = String(change.expected ?? "");
10862
+ return `- ${item.dbFieldName}.${change.type}: ${current} -> ${expected}`;
10863
+ }));
10864
+ const indexLines = details.indexChanges.map((change) => {
10865
+ const indexLabel = `idx_${change.fieldName}` === change.indexName ? change.indexName : change.indexName;
10866
+ if (change.action === "create") {
10867
+ return `- index.${indexLabel}: \u65E0 -> ${change.indexName}(${change.fieldName})`;
10868
+ }
10869
+ return `- index.${indexLabel}: ${change.indexName}(${change.fieldName}) -> \u65E0`;
10870
+ });
10871
+ const allLines = [...detailLines, ...indexLines];
10714
10872
  Logger.debug(msg);
10873
+ for (const line of allLines) {
10874
+ Logger.debug(`[\u8868 ${tableName}] \u53D8\u66F4\u660E\u7EC6 ${line}`);
10875
+ }
10715
10876
  await SyncTable.applyTablePlan(db, tableName, plan);
10716
10877
  }
10717
10878
  return plan;
@@ -12473,12 +12634,28 @@ function getRegex(name) {
12473
12634
  }
12474
12635
  return name;
12475
12636
  }
12637
+ function parseRegexLiteral(pattern) {
12638
+ const trimmed = String(pattern || "").trim();
12639
+ if (trimmed.length < 2)
12640
+ return null;
12641
+ if (!trimmed.startsWith("/"))
12642
+ return null;
12643
+ const lastSlash = trimmed.lastIndexOf("/");
12644
+ if (lastSlash <= 0)
12645
+ return null;
12646
+ const flags = trimmed.slice(lastSlash + 1);
12647
+ if (flags !== "" && !/^[gimsuy]+$/.test(flags))
12648
+ return null;
12649
+ return { source: trimmed.slice(1, lastSlash), flags };
12650
+ }
12476
12651
  function getCompiledRegex(pattern, flags) {
12477
- const regexStr = getRegex(pattern);
12478
- const cacheKey = `${regexStr}:${flags || ""}`;
12652
+ const literal = parseRegexLiteral(pattern);
12653
+ const regexStr = literal ? literal.source : getRegex(pattern);
12654
+ const useFlags = literal && literal.flags !== "" ? literal.flags : flags || "";
12655
+ const cacheKey = `${regexStr}:${useFlags}`;
12479
12656
  let cached = regexCache.get(cacheKey);
12480
12657
  if (!cached) {
12481
- cached = new RegExp(regexStr, flags);
12658
+ cached = new RegExp(regexStr, useFlags);
12482
12659
  regexCache.set(cacheKey, cached);
12483
12660
  }
12484
12661
  return cached;
@@ -12487,39 +12664,8 @@ function getCompiledRegex(pattern, flags) {
12487
12664
  // lib/validator.ts
12488
12665
  init_util();
12489
12666
  var INPUT_TYPE_SET2 = new Set(["number", "integer", "string", "char", "array", "array_number", "array_integer", "json", "json_number", "json_integer"]);
12490
- function isRegexInput2(input) {
12491
- if (input.startsWith("@"))
12492
- return true;
12493
- return /[\\^$.*+?()[\]{}]/.test(input);
12494
- }
12495
- function isEnumInput2(input) {
12496
- return input.includes("|") && !isRegexInput2(input);
12497
- }
12498
- function isJsonPrimitive3(value) {
12499
- if (value === null)
12500
- return true;
12501
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
12502
- }
12503
- function isJsonObject3(value) {
12504
- if (!value || typeof value !== "object")
12505
- return false;
12506
- if (value instanceof Date)
12507
- return false;
12508
- if (Array.isArray(value))
12509
- return false;
12510
- return true;
12511
- }
12512
- function isJsonStructure(value) {
12513
- return Array.isArray(value) || isJsonObject3(value);
12514
- }
12515
- function isFiniteNumber(value) {
12516
- return typeof value === "number" && Number.isFinite(value);
12517
- }
12518
- function isIntegerNumber(value) {
12519
- return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value);
12520
- }
12521
12667
  function checkJsonLeaves(value, rule) {
12522
- if (isJsonPrimitive3(value)) {
12668
+ if (isJsonPrimitive(value)) {
12523
12669
  if (rule === "number")
12524
12670
  return isFiniteNumber(value);
12525
12671
  return isIntegerNumber(value);
@@ -12531,7 +12677,7 @@ function checkJsonLeaves(value, rule) {
12531
12677
  }
12532
12678
  return true;
12533
12679
  }
12534
- if (isJsonObject3(value)) {
12680
+ if (isJsonObject(value)) {
12535
12681
  for (const v of Object.values(value)) {
12536
12682
  if (v === undefined)
12537
12683
  continue;
@@ -12657,14 +12803,15 @@ class Validator {
12657
12803
  return error ? `${label}${error}` : null;
12658
12804
  }
12659
12805
  static convert(value, input, dbType) {
12660
- const normalizedInput = String(input || "").toLowerCase();
12661
- if (!INPUT_TYPE_SET2.has(normalizedInput) && isRegexInput2(normalizedInput)) {
12806
+ const inputRaw = String(input || "").trim();
12807
+ const inputKey = inputRaw.toLowerCase();
12808
+ if (!INPUT_TYPE_SET2.has(inputKey) && isRegexInput(inputRaw)) {
12662
12809
  return typeof value === "string" ? { value, error: null } : { value: null, error: "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32" };
12663
12810
  }
12664
- if (!INPUT_TYPE_SET2.has(normalizedInput) && isEnumInput2(normalizedInput)) {
12811
+ if (!INPUT_TYPE_SET2.has(inputKey) && isEnumInput(inputRaw)) {
12665
12812
  return typeof value === "string" ? { value, error: null } : { value: null, error: "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32" };
12666
12813
  }
12667
- switch (normalizedInput) {
12814
+ switch (inputKey) {
12668
12815
  case "number": {
12669
12816
  if (isFiniteNumber(value))
12670
12817
  return { value, error: null };
@@ -12749,43 +12896,44 @@ class Validator {
12749
12896
  const input = normalized.input;
12750
12897
  const min = normalized.min;
12751
12898
  const max = normalized.max;
12752
- const normalizedInput = String(input || "").toLowerCase();
12753
- const isRegex = !INPUT_TYPE_SET2.has(normalizedInput) && isRegexInput2(normalizedInput);
12754
- const isEnum = !INPUT_TYPE_SET2.has(normalizedInput) && isEnumInput2(normalizedInput);
12755
- if (normalizedInput === "number" || normalizedInput === "integer") {
12899
+ const inputRaw = String(input || "").trim();
12900
+ const inputKey = inputRaw.toLowerCase();
12901
+ const isRegex = !INPUT_TYPE_SET2.has(inputKey) && isRegexInput(inputRaw);
12902
+ const isEnum = !INPUT_TYPE_SET2.has(inputKey) && isEnumInput(inputRaw);
12903
+ if (inputKey === "number" || inputKey === "integer") {
12756
12904
  if (typeof value !== "number")
12757
- return normalizedInput === "integer" ? "\u5FC5\u987B\u662F\u6574\u6570" : "\u5FC5\u987B\u662F\u6570\u5B57";
12905
+ return inputKey === "integer" ? "\u5FC5\u987B\u662F\u6574\u6570" : "\u5FC5\u987B\u662F\u6570\u5B57";
12758
12906
  if (min !== null && value < min)
12759
12907
  return `\u4E0D\u80FD\u5C0F\u4E8E${min}`;
12760
12908
  if (max !== null && max > 0 && value > max)
12761
12909
  return `\u4E0D\u80FD\u5927\u4E8E${max}`;
12762
- } else if (normalizedInput === "string" || normalizedInput === "char" || isRegex || isEnum) {
12910
+ } else if (inputKey === "string" || inputKey === "char" || isRegex || isEnum) {
12763
12911
  if (typeof value !== "string")
12764
12912
  return "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32";
12765
- if (normalizedInput === "char" && value.length !== 1)
12913
+ if (inputKey === "char" && value.length !== 1)
12766
12914
  return "\u5FC5\u987B\u662F\u5355\u5B57\u7B26";
12767
12915
  if (min !== null && value.length < min)
12768
12916
  return `\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E${min}\u4E2A\u5B57\u7B26`;
12769
12917
  if (max !== null && max > 0 && value.length > max)
12770
12918
  return `\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7${max}\u4E2A\u5B57\u7B26`;
12771
12919
  if (isRegex) {
12772
- const regex = this.resolveRegex(normalizedInput);
12920
+ const regex = this.resolveRegex(inputRaw);
12773
12921
  if (!this.testRegex(regex, value))
12774
12922
  return "\u683C\u5F0F\u4E0D\u6B63\u786E";
12775
12923
  }
12776
12924
  if (isEnum) {
12777
- const enums = normalizedInput.split("|").map((item) => item.trim()).filter((item) => item !== "");
12925
+ const enums = inputRaw.split("|").map((item) => item.trim()).filter((item) => item !== "");
12778
12926
  if (!enums.includes(value))
12779
12927
  return "\u503C\u4E0D\u5728\u679A\u4E3E\u8303\u56F4\u5185";
12780
12928
  }
12781
- } else if (normalizedInput === "array" || normalizedInput === "array_number" || normalizedInput === "array_integer") {
12929
+ } else if (inputKey === "array" || inputKey === "array_number" || inputKey === "array_integer") {
12782
12930
  if (!Array.isArray(value))
12783
12931
  return "\u5FC5\u987B\u662F\u6570\u7EC4";
12784
12932
  if (min !== null && value.length < min)
12785
12933
  return `\u81F3\u5C11\u9700\u8981${min}\u4E2A\u5143\u7D20`;
12786
12934
  if (max !== null && max > 0 && value.length > max)
12787
12935
  return `\u6700\u591A\u53EA\u80FD\u6709${max}\u4E2A\u5143\u7D20`;
12788
- } else if (normalizedInput === "json" || normalizedInput === "json_number" || normalizedInput === "json_integer") {
12936
+ } else if (inputKey === "json" || inputKey === "json_number" || inputKey === "json_integer") {
12789
12937
  if (!isJsonStructure(value))
12790
12938
  return "\u5FC5\u987B\u662FJSON\u5BF9\u8C61\u6216\u6570\u7EC4";
12791
12939
  }