befly 3.15.19 → 3.15.21

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
@@ -913,11 +913,29 @@ function buildLogLine(level, record) {
913
913
  hostname: HOSTNAME
914
914
  };
915
915
  if (record && isPlainObject(record)) {
916
- const out = Object.assign({}, record, base);
916
+ const out = {};
917
+ out["level"] = base["level"];
918
+ out["time"] = base["time"];
919
+ out["timeFormat"] = base["timeFormat"];
920
+ out["pid"] = base["pid"];
921
+ out["hostname"] = base["hostname"];
922
+ for (const key of Object.keys(record)) {
923
+ if (key === "level")
924
+ continue;
925
+ if (key === "time")
926
+ continue;
927
+ if (key === "timeFormat")
928
+ continue;
929
+ if (key === "pid")
930
+ continue;
931
+ if (key === "hostname")
932
+ continue;
933
+ out[key] = record[key];
934
+ }
917
935
  if (record.msg !== undefined) {
918
- out.msg = record.msg;
919
- } else if (out.msg === undefined) {
920
- out.msg = "";
936
+ out["msg"] = record.msg;
937
+ } else if (out["msg"] === undefined) {
938
+ out["msg"] = "";
921
939
  }
922
940
  return `${safeJsonStringify(out)}
923
941
  `;
@@ -7727,6 +7745,38 @@ async function loadBeflyConfig(nodeEnv) {
7727
7745
 
7728
7746
  // checks/checkApi.ts
7729
7747
  init_logger();
7748
+
7749
+ // types/coreError.ts
7750
+ class CoreError extends Error {
7751
+ kind;
7752
+ code;
7753
+ meta;
7754
+ noLog;
7755
+ logged;
7756
+ cause;
7757
+ constructor(options) {
7758
+ super(options.message);
7759
+ this.name = "CoreError";
7760
+ this.kind = options.kind;
7761
+ this.code = typeof options.code === "string" && options.code.length > 0 ? options.code : null;
7762
+ this.meta = options.meta ? options.meta : null;
7763
+ this.noLog = options.noLog === true;
7764
+ this.logged = options.logged === true;
7765
+ this.cause = options.cause;
7766
+ }
7767
+ }
7768
+ function isCoreError(error) {
7769
+ if (typeof error !== "object" || error === null)
7770
+ return false;
7771
+ const anyErr = error;
7772
+ if (anyErr.name !== "CoreError")
7773
+ return false;
7774
+ if (typeof anyErr.kind !== "string")
7775
+ return false;
7776
+ return true;
7777
+ }
7778
+
7779
+ // checks/checkApi.ts
7730
7780
  init_util();
7731
7781
  async function checkApi(apis) {
7732
7782
  let hasError = false;
@@ -7831,7 +7881,15 @@ async function checkApi(apis) {
7831
7881
  }
7832
7882
  }
7833
7883
  if (hasError) {
7834
- throw new Error("\u63A5\u53E3\u7ED3\u6784\u68C0\u67E5\u5931\u8D25");
7884
+ throw new CoreError({
7885
+ kind: "policy",
7886
+ message: "\u63A5\u53E3\u7ED3\u6784\u68C0\u67E5\u5931\u8D25",
7887
+ logged: true,
7888
+ meta: {
7889
+ subsystem: "checks",
7890
+ operation: "checkApi"
7891
+ }
7892
+ });
7835
7893
  }
7836
7894
  }
7837
7895
 
@@ -8067,7 +8125,15 @@ async function checkHook(hooks) {
8067
8125
  }
8068
8126
  }
8069
8127
  if (hasError) {
8070
- throw new Error("\u94A9\u5B50\u7ED3\u6784\u68C0\u67E5\u5931\u8D25");
8128
+ throw new CoreError({
8129
+ kind: "policy",
8130
+ message: "\u94A9\u5B50\u7ED3\u6784\u68C0\u67E5\u5931\u8D25",
8131
+ logged: true,
8132
+ meta: {
8133
+ subsystem: "checks",
8134
+ operation: "checkHook"
8135
+ }
8136
+ });
8071
8137
  }
8072
8138
  }
8073
8139
 
@@ -8285,7 +8351,15 @@ async function checkPlugin(plugins) {
8285
8351
  }
8286
8352
  }
8287
8353
  if (hasError) {
8288
- throw new Error("\u63D2\u4EF6\u7ED3\u6784\u68C0\u67E5\u5931\u8D25");
8354
+ throw new CoreError({
8355
+ kind: "policy",
8356
+ message: "\u63D2\u4EF6\u7ED3\u6784\u68C0\u67E5\u5931\u8D25",
8357
+ logged: true,
8358
+ meta: {
8359
+ subsystem: "checks",
8360
+ operation: "checkPlugin"
8361
+ }
8362
+ });
8289
8363
  }
8290
8364
  }
8291
8365
 
@@ -8614,13 +8688,21 @@ async function checkTable(tables, config2) {
8614
8688
  }
8615
8689
  }
8616
8690
  if (hasError) {
8617
- throw new Error("\u8868\u7ED3\u6784\u68C0\u67E5\u5931\u8D25");
8691
+ throw new CoreError({
8692
+ kind: "policy",
8693
+ message: "\u8868\u7ED3\u6784\u68C0\u67E5\u5931\u8D25",
8694
+ logged: true,
8695
+ meta: {
8696
+ subsystem: "checks",
8697
+ operation: "checkTable"
8698
+ }
8699
+ });
8618
8700
  }
8619
8701
  }
8620
8702
 
8621
8703
  // lib/connect.ts
8622
- init_logger();
8623
8704
  var {SQL, RedisClient } = globalThis.Bun;
8705
+ init_logger();
8624
8706
 
8625
8707
  class Connect {
8626
8708
  static sqlClient = null;
@@ -8638,16 +8720,16 @@ class Connect {
8638
8720
  const password = config2.password === undefined ? "" : typeof config2.password === "string" ? config2.password : "";
8639
8721
  const database = typeof config2.database === "string" ? config2.database.trim() : "";
8640
8722
  if (!host) {
8641
- throw new Error("\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.host \u7F3A\u5931");
8723
+ throw new CoreError({ message: "\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.host \u7F3A\u5931", kind: "validation" });
8642
8724
  }
8643
8725
  if (!Number.isFinite(port) || port < 1 || port > 65535) {
8644
- throw new Error(`\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.port \u975E\u6CD5\uFF08\u5F53\u524D\u503C\uFF1A${String(config2.port)}\uFF09`);
8726
+ throw new CoreError({ message: `\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.port \u975E\u6CD5\uFF08\u5F53\u524D\u503C\uFF1A${String(config2.port)}\uFF09`, kind: "validation" });
8645
8727
  }
8646
8728
  if (!username) {
8647
- throw new Error("\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.username \u7F3A\u5931");
8729
+ throw new CoreError({ message: "\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.username \u7F3A\u5931", kind: "validation" });
8648
8730
  }
8649
8731
  if (!database) {
8650
- throw new Error("\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.database \u7F3A\u5931");
8732
+ throw new CoreError({ message: "\u6570\u636E\u5E93\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF1Adb.database \u7F3A\u5931", kind: "validation" });
8651
8733
  }
8652
8734
  const user = encodeURIComponent(username);
8653
8735
  const pass = encodeURIComponent(password);
@@ -8666,10 +8748,10 @@ class Connect {
8666
8748
  const majorText = versionText.split(".")[0];
8667
8749
  const major = Number(majorText);
8668
8750
  if (!Number.isFinite(major)) {
8669
- throw new Error(`\u65E0\u6CD5\u89E3\u6790 MySQL \u7248\u672C\u4FE1\u606F: ${versionText}`);
8751
+ throw new CoreError({ message: `\u65E0\u6CD5\u89E3\u6790 MySQL \u7248\u672C\u4FE1\u606F: ${versionText}`, kind: "runtime", meta: { subsystem: "sql", operation: "version" } });
8670
8752
  }
8671
8753
  if (major < 8) {
8672
- throw new Error(`\u4EC5\u652F\u6301 MySQL 8.0+\uFF0C\u5F53\u524D\u7248\u672C\uFF1A${versionText}`);
8754
+ throw new CoreError({ message: `\u4EC5\u652F\u6301 MySQL 8.0+\uFF0C\u5F53\u524D\u7248\u672C\uFF1A${versionText}`, kind: "policy", meta: { subsystem: "sql", operation: "version" } });
8673
8755
  }
8674
8756
  this.mysqlVersionText = versionText;
8675
8757
  this.mysqlVersionMajor = major;
@@ -8690,7 +8772,21 @@ class Connect {
8690
8772
  try {
8691
8773
  await sql?.close();
8692
8774
  } catch {}
8693
- throw error;
8775
+ if (isCoreError(error)) {
8776
+ error.logged = true;
8777
+ throw error;
8778
+ }
8779
+ const msg = `SQL \u8FDE\u63A5\u5931\u8D25: ${error && typeof error.message === "string" ? error.message : String(error)}`;
8780
+ throw new CoreError({
8781
+ message: msg,
8782
+ kind: "runtime",
8783
+ logged: true,
8784
+ cause: error,
8785
+ meta: {
8786
+ subsystem: "sql",
8787
+ operation: "connect"
8788
+ }
8789
+ });
8694
8790
  }
8695
8791
  }
8696
8792
  static async disconnectSql() {
@@ -8741,7 +8837,17 @@ class Connect {
8741
8837
  return redis;
8742
8838
  } catch (error) {
8743
8839
  Logger.error({ err: error, msg: "[Connect] Redis \u8FDE\u63A5\u5931\u8D25" });
8744
- throw new Error(`Redis \u8FDE\u63A5\u5931\u8D25: ${error.message}`);
8840
+ const msg = `Redis \u8FDE\u63A5\u5931\u8D25: ${error && typeof error.message === "string" ? error.message : String(error)}`;
8841
+ throw new CoreError({
8842
+ message: msg,
8843
+ kind: "runtime",
8844
+ logged: true,
8845
+ cause: error,
8846
+ meta: {
8847
+ subsystem: "redis",
8848
+ operation: "connect"
8849
+ }
8850
+ });
8745
8851
  }
8746
8852
  }
8747
8853
  static async disconnectRedis() {
@@ -8769,7 +8875,21 @@ class Connect {
8769
8875
  const env = typeof process?.env?.NODE_ENV === "string" ? "development" : "";
8770
8876
  Logger.error({ env, err: error, msg: "\u6570\u636E\u5E93\u8FDE\u63A5\u521D\u59CB\u5316\u5931\u8D25" });
8771
8877
  await this.disconnect();
8772
- throw error;
8878
+ if (isCoreError(error)) {
8879
+ error.logged = true;
8880
+ throw error;
8881
+ }
8882
+ const msg = `\u6570\u636E\u5E93\u8FDE\u63A5\u521D\u59CB\u5316\u5931\u8D25: ${error && typeof error.message === "string" ? error.message : String(error)}`;
8883
+ throw new CoreError({
8884
+ message: msg,
8885
+ kind: "runtime",
8886
+ logged: true,
8887
+ cause: error,
8888
+ meta: {
8889
+ subsystem: "connect",
8890
+ operation: "connect"
8891
+ }
8892
+ });
8773
8893
  }
8774
8894
  }
8775
8895
  static async disconnect() {
@@ -8863,7 +8983,18 @@ async function loadApis(apis) {
8863
8983
  apisMap.set(path, route);
8864
8984
  } catch (error) {
8865
8985
  Logger.error({ err: error, api: api.relativePath, file: api.filePath, msg: "\u63A5\u53E3\u52A0\u8F7D\u5931\u8D25" });
8866
- throw error;
8986
+ throw new CoreError({
8987
+ kind: "runtime",
8988
+ message: "\u63A5\u53E3\u52A0\u8F7D\u5931\u8D25",
8989
+ logged: true,
8990
+ cause: error,
8991
+ meta: {
8992
+ subsystem: "api",
8993
+ operation: "load",
8994
+ relativePath: api.relativePath,
8995
+ filePath: api.filePath
8996
+ }
8997
+ });
8867
8998
  }
8868
8999
  }
8869
9000
  return apisMap;
@@ -8979,7 +9110,7 @@ async function loadHooks(hooks) {
8979
9110
  });
8980
9111
  const sortedHooks = sortModules(enabledHooks, { moduleLabel: "\u94A9\u5B50" });
8981
9112
  if (sortedHooks === false) {
8982
- throw new Error("\u94A9\u5B50\u4F9D\u8D56\u5173\u7CFB\u9519\u8BEF");
9113
+ throw new CoreError({ message: "\u94A9\u5B50\u4F9D\u8D56\u5173\u7CFB\u9519\u8BEF", kind: "policy", logged: true });
8983
9114
  }
8984
9115
  for (const item of sortedHooks) {
8985
9116
  const hookName = item.moduleName;
@@ -9017,7 +9148,7 @@ async function loadPlugins(plugins, context) {
9017
9148
  });
9018
9149
  const sortedPlugins = sortModules(enabledPlugins, { moduleLabel: "\u63D2\u4EF6" });
9019
9150
  if (sortedPlugins === false) {
9020
- throw new Error("\u63D2\u4EF6\u4F9D\u8D56\u5173\u7CFB\u9519\u8BEF");
9151
+ throw new CoreError({ message: "\u63D2\u4EF6\u4F9D\u8D56\u5173\u7CFB\u9519\u8BEF", kind: "policy", logged: true });
9021
9152
  }
9022
9153
  for (const item of sortedPlugins) {
9023
9154
  const pluginName = item.moduleName;
@@ -9039,7 +9170,17 @@ async function loadPlugins(plugins, context) {
9039
9170
  });
9040
9171
  } catch (error) {
9041
9172
  Logger.error({ err: error, plugin: pluginName, msg: "\u63D2\u4EF6\u521D\u59CB\u5316\u5931\u8D25" });
9042
- throw error;
9173
+ throw new CoreError({
9174
+ kind: "runtime",
9175
+ message: `\u63D2\u4EF6\u521D\u59CB\u5316\u5931\u8D25: ${pluginName}`,
9176
+ logged: true,
9177
+ cause: error,
9178
+ meta: {
9179
+ subsystem: "plugin",
9180
+ operation: "init",
9181
+ plugin: pluginName
9182
+ }
9183
+ });
9043
9184
  }
9044
9185
  }
9045
9186
  return pluginsMap;
@@ -9327,10 +9468,10 @@ var getApiParentPath = (apiPath) => {
9327
9468
  async function syncApi(ctx, apis) {
9328
9469
  const tableName = "addon_admin_api";
9329
9470
  if (!ctx.db) {
9330
- throw new Error("\u540C\u6B65\u63A5\u53E3\uFF1Actx.db \u672A\u521D\u59CB\u5316");
9471
+ throw new CoreError({ message: "\u540C\u6B65\u63A5\u53E3\uFF1Actx.db \u672A\u521D\u59CB\u5316", kind: "validation" });
9331
9472
  }
9332
9473
  if (!ctx.cache) {
9333
- throw new Error("\u540C\u6B65\u63A5\u53E3\uFF1Actx.cache \u672A\u521D\u59CB\u5316");
9474
+ throw new CoreError({ message: "\u540C\u6B65\u63A5\u53E3\uFF1Actx.cache \u672A\u521D\u59CB\u5316", kind: "validation" });
9334
9475
  }
9335
9476
  if (!(await ctx.db.tableExists(tableName)).data) {
9336
9477
  Logger.debug(`${tableName} \u8868\u4E0D\u5B58\u5728`);
@@ -9439,7 +9580,7 @@ async function syncApi(ctx, apis) {
9439
9580
  // sync/syncCache.ts
9440
9581
  async function syncCache(ctx) {
9441
9582
  if (!ctx.cache) {
9442
- throw new Error("\u540C\u6B65\u7F13\u5B58\uFF1Actx.cache \u672A\u521D\u59CB\u5316");
9583
+ throw new CoreError({ message: "\u540C\u6B65\u7F13\u5B58\uFF1Actx.cache \u672A\u521D\u59CB\u5316", kind: "validation" });
9443
9584
  }
9444
9585
  await ctx.cache.cacheApis();
9445
9586
  await ctx.cache.cacheMenus();
@@ -9827,13 +9968,13 @@ function flattenMenusToDefMap(mergedMenus) {
9827
9968
  }
9828
9969
  async function syncMenu(ctx, mergedMenus) {
9829
9970
  if (!ctx.db) {
9830
- throw new Error("\u540C\u6B65\u83DC\u5355\uFF1Actx.db \u672A\u521D\u59CB\u5316");
9971
+ throw new CoreError({ message: "\u540C\u6B65\u83DC\u5355\uFF1Actx.db \u672A\u521D\u59CB\u5316", kind: "validation" });
9831
9972
  }
9832
9973
  if (!ctx.cache) {
9833
- throw new Error("\u540C\u6B65\u83DC\u5355\uFF1Actx.cache \u672A\u521D\u59CB\u5316");
9974
+ throw new CoreError({ message: "\u540C\u6B65\u83DC\u5355\uFF1Actx.cache \u672A\u521D\u59CB\u5316", kind: "validation" });
9834
9975
  }
9835
9976
  if (!ctx.config) {
9836
- throw new Error("\u540C\u6B65\u83DC\u5355\uFF1Actx.config \u672A\u521D\u59CB\u5316");
9977
+ throw new CoreError({ message: "\u540C\u6B65\u83DC\u5355\uFF1Actx.config \u672A\u521D\u59CB\u5316", kind: "validation" });
9837
9978
  }
9838
9979
  if (!(await ctx.db.tableExists("addon_admin_menu")).data) {
9839
9980
  Logger.debug(`addon_admin_menu \u8868\u4E0D\u5B58\u5728`);
@@ -10227,6 +10368,8 @@ class SyncTable {
10227
10368
  throw new Error("\u540C\u6B65\u8868\uFF1A\u8BF7\u4F20\u5165\u591A\u4E2A\u8868\u5B9A\u4E49\u7EC4\u6210\u7684\u6570\u7EC4");
10228
10369
  }
10229
10370
  await SyncTable.ensureDbVersion(this.db);
10371
+ const tableTasks = [];
10372
+ const incompatibleTypeChanges = [];
10230
10373
  for (const item of items) {
10231
10374
  if (!item || item.type !== "table") {
10232
10375
  continue;
@@ -10237,9 +10380,100 @@ class SyncTable {
10237
10380
  SyncTable.normalizeFieldDefinitionInPlace(fieldDef);
10238
10381
  }
10239
10382
  const existsTable = await SyncTable.tableExists(this.db, this.dbName, tableName);
10383
+ const task = {
10384
+ item,
10385
+ tableName,
10386
+ tableFields,
10387
+ existsTable,
10388
+ existingColumns: null,
10389
+ existingIndexes: null,
10390
+ plan: null,
10391
+ planSummary: null,
10392
+ planDetails: null
10393
+ };
10394
+ if (existsTable) {
10395
+ const existingColumns = await SyncTable.getTableColumns(this.db, this.dbName, tableName);
10396
+ const existingIndexes = await SyncTable.getTableIndexes(this.db, this.dbName, tableName);
10397
+ const changes = SyncTable.collectIncompatibleTypeChanges(tableName, existingColumns, tableFields);
10398
+ for (const change of changes) {
10399
+ incompatibleTypeChanges.push(change);
10400
+ }
10401
+ task.existingColumns = existingColumns;
10402
+ task.existingIndexes = existingIndexes;
10403
+ }
10404
+ tableTasks.push(task);
10405
+ }
10406
+ if (incompatibleTypeChanges.length > 0) {
10407
+ const lines = [];
10408
+ for (const change of incompatibleTypeChanges) {
10409
+ lines.push(`- ${change.tableName}.${change.dbFieldName}: ${change.currentType} -> ${change.expectedType}`);
10410
+ }
10411
+ const msgLines = [];
10412
+ msgLines.push("\u7981\u6B62\u5B57\u6BB5\u7C7B\u578B\u53D8\u66F4\uFF08\u68C0\u6D4B\u5230\u4E0D\u517C\u5BB9/\u6536\u7F29\u53D8\u66F4\uFF09:");
10413
+ for (const line of lines) {
10414
+ msgLines.push(line);
10415
+ }
10416
+ 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");
10417
+ 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");
10418
+ const err = new Error(msgLines.join(`
10419
+ `));
10420
+ err.__syncTableNoLog = true;
10421
+ throw err;
10422
+ }
10423
+ for (const task of tableTasks) {
10424
+ if (!task.existsTable) {
10425
+ continue;
10426
+ }
10427
+ if (!task.existingColumns || !task.existingIndexes) {
10428
+ 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`);
10429
+ }
10430
+ const built = SyncTable.buildTablePlan({
10431
+ tableName: task.tableName,
10432
+ fields: task.tableFields,
10433
+ existingColumns: task.existingColumns,
10434
+ existingIndexes: task.existingIndexes
10435
+ });
10436
+ task.plan = built.plan;
10437
+ task.planSummary = built.summary;
10438
+ task.planDetails = built.details;
10439
+ }
10440
+ for (const task of tableTasks) {
10441
+ const item = task.item;
10442
+ const tableName = task.tableName;
10443
+ const tableFields = task.tableFields;
10444
+ const existsTable = task.existsTable;
10240
10445
  try {
10241
10446
  if (existsTable) {
10242
- await SyncTable.modifyTable(this.db, this.dbName, tableName, tableFields);
10447
+ const plan = task.plan;
10448
+ const summary = task.planSummary;
10449
+ const details = task.planDetails;
10450
+ if (plan && plan.changed && summary && details) {
10451
+ 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}`;
10452
+ const detailLines = details.fieldChanges.flatMap((d) => d.changes.map((change) => {
10453
+ const current = String(change.current ?? "");
10454
+ const expected = String(change.expected ?? "");
10455
+ return `- ${d.dbFieldName}.${change.type}: ${current} -> ${expected}`;
10456
+ }));
10457
+ const indexLines = details.indexChanges.map((change) => {
10458
+ const indexLabel = `idx_${change.fieldName}` === change.indexName ? change.indexName : change.indexName;
10459
+ if (change.action === "create") {
10460
+ return `- index.${indexLabel}: \u65E0 -> ${change.indexName}(${change.fieldName})`;
10461
+ }
10462
+ return `- index.${indexLabel}: ${change.indexName}(${change.fieldName}) -> \u65E0`;
10463
+ });
10464
+ const allLines = [];
10465
+ for (const line of detailLines) {
10466
+ allLines.push(line);
10467
+ }
10468
+ for (const line of indexLines) {
10469
+ allLines.push(line);
10470
+ }
10471
+ Logger.debug(msg);
10472
+ for (const line of allLines) {
10473
+ Logger.debug(`[\u8868 ${tableName}] \u53D8\u66F4\u660E\u7EC6 ${line}`);
10474
+ }
10475
+ await SyncTable.applyTablePlan(this.db, tableName, plan);
10476
+ }
10243
10477
  } else {
10244
10478
  await SyncTable.createTable(this.db, tableName, tableFields);
10245
10479
  }
@@ -10268,6 +10502,9 @@ class SyncTable {
10268
10502
  if (error?.__syncTableLogged === true) {
10269
10503
  throw error;
10270
10504
  }
10505
+ if (error?.__syncTableNoLog === true) {
10506
+ throw error;
10507
+ }
10271
10508
  const errMsg = String(error?.message || error);
10272
10509
  const sqlInfo = error?.sqlInfo;
10273
10510
  Logger.error({
@@ -10594,6 +10831,32 @@ class SyncTable {
10594
10831
  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(`
10595
10832
  `));
10596
10833
  }
10834
+ static collectIncompatibleTypeChanges(tableName, existingColumns, fields) {
10835
+ const out = [];
10836
+ for (const [fieldKey, fieldDef] of Object.entries(fields)) {
10837
+ const dbFieldName = snakeCase(fieldKey);
10838
+ const existing = existingColumns[dbFieldName];
10839
+ if (!existing)
10840
+ continue;
10841
+ const comparison = SyncTable.compareFieldDefinition(existing, fieldDef);
10842
+ const typeChange = comparison.find((c) => c.type === "datatype");
10843
+ if (!typeChange)
10844
+ continue;
10845
+ const currentType = String(typeChange.current || "").toLowerCase();
10846
+ const expectedType = String(typeChange.expected || "").toLowerCase();
10847
+ const currentBase = currentType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
10848
+ const expectedBase = expectedType.replace(/\s*unsigned/gi, "").replace(/\([^)]*\)/g, "").trim();
10849
+ if (currentBase !== expectedBase && !SyncTable.isCompatibleTypeChange(currentType, expectedType)) {
10850
+ out.push({
10851
+ tableName,
10852
+ dbFieldName,
10853
+ currentType: String(typeChange.current ?? ""),
10854
+ expectedType: String(typeChange.expected ?? "")
10855
+ });
10856
+ }
10857
+ }
10858
+ return out;
10859
+ }
10597
10860
  static truncateForLog(input, maxLen) {
10598
10861
  const s = String(input);
10599
10862
  if (maxLen <= 0)
@@ -13216,7 +13479,16 @@ class CacheHelper {
13216
13479
  }
13217
13480
  } catch (error) {
13218
13481
  Logger.error({ err: error, msg: "\u26A0\uFE0F \u89D2\u8272\u6743\u9650\u7F13\u5B58\u5F02\u5E38\uFF08\u5C06\u963B\u65AD\u542F\u52A8\uFF09" });
13219
- throw error;
13482
+ throw new CoreError({
13483
+ kind: "runtime",
13484
+ message: "\u26A0\uFE0F \u89D2\u8272\u6743\u9650\u7F13\u5B58\u5F02\u5E38\uFF08\u5C06\u963B\u65AD\u542F\u52A8\uFF09",
13485
+ logged: true,
13486
+ cause: error,
13487
+ meta: {
13488
+ subsystem: "cache",
13489
+ operation: "rebuildRoleApiPermissions"
13490
+ }
13491
+ });
13220
13492
  }
13221
13493
  }
13222
13494
  async refreshRoleApiPermissions(roleCode, apiPaths) {
@@ -13301,10 +13573,10 @@ var cachePlugin = {
13301
13573
  deps: ["logger", "redis", "db"],
13302
13574
  async handler(befly) {
13303
13575
  if (!befly.db) {
13304
- throw new Error("\u7F13\u5B58\u521D\u59CB\u5316\u5931\u8D25\uFF1Actx.db \u672A\u521D\u59CB\u5316");
13576
+ throw new CoreError({ message: "\u7F13\u5B58\u521D\u59CB\u5316\u5931\u8D25\uFF1Actx.db \u672A\u521D\u59CB\u5316", kind: "validation" });
13305
13577
  }
13306
13578
  if (!befly.redis) {
13307
- throw new Error("\u7F13\u5B58\u521D\u59CB\u5316\u5931\u8D25\uFF1Actx.redis \u672A\u521D\u59CB\u5316");
13579
+ throw new CoreError({ message: "\u7F13\u5B58\u521D\u59CB\u5316\u5931\u8D25\uFF1Actx.redis \u672A\u521D\u59CB\u5316", kind: "validation" });
13308
13580
  }
13309
13581
  return new CacheHelper({ db: befly.db, redis: befly.redis });
13310
13582
  }
@@ -15092,7 +15364,20 @@ class DbHelper {
15092
15364
  fields: insertFields,
15093
15365
  msg: "\u6279\u91CF\u63D2\u5165\u5931\u8D25"
15094
15366
  });
15095
- throw error;
15367
+ throw new CoreError({
15368
+ kind: "runtime",
15369
+ message: "\u6279\u91CF\u63D2\u5165\u5931\u8D25",
15370
+ logged: true,
15371
+ cause: error,
15372
+ meta: {
15373
+ subsystem: "sql",
15374
+ operation: "insBatch",
15375
+ table,
15376
+ snakeTable,
15377
+ count: dataList.length,
15378
+ fields: insertFields
15379
+ }
15380
+ });
15096
15381
  }
15097
15382
  }
15098
15383
  async delForceBatch(table, ids) {
@@ -15370,10 +15655,10 @@ var dbPlugin = {
15370
15655
  const env = befly.config?.nodeEnv;
15371
15656
  const dbName = String(befly.config?.db?.database || "");
15372
15657
  if (!dbName) {
15373
- throw new Error("\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25\uFF1Abefly.config.db.database \u7F3A\u5931");
15658
+ throw new CoreError({ message: "\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25\uFF1Abefly.config.db.database \u7F3A\u5931", kind: "validation" });
15374
15659
  }
15375
15660
  if (!befly.redis) {
15376
- throw new Error("Redis \u672A\u521D\u59CB\u5316");
15661
+ throw new CoreError({ message: "Redis \u672A\u521D\u59CB\u5316", kind: "validation" });
15377
15662
  }
15378
15663
  try {
15379
15664
  const sql = Connect.getSql();
@@ -15383,7 +15668,16 @@ var dbPlugin = {
15383
15668
  return dbManager;
15384
15669
  } catch (error) {
15385
15670
  Logger.error({ env, err: error, msg: "\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25" });
15386
- throw error;
15671
+ throw new CoreError({
15672
+ kind: "runtime",
15673
+ message: "\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25",
15674
+ logged: true,
15675
+ cause: error,
15676
+ meta: {
15677
+ subsystem: "db",
15678
+ operation: "init"
15679
+ }
15680
+ });
15387
15681
  }
15388
15682
  }
15389
15683
  };
@@ -15853,7 +16147,16 @@ class RedisHelper {
15853
16147
  return res;
15854
16148
  } catch (error) {
15855
16149
  Logger.error({ err: error, msg: "Redis ping \u9519\u8BEF" });
15856
- throw error;
16150
+ throw new CoreError({
16151
+ kind: "runtime",
16152
+ message: "Redis ping \u9519\u8BEF",
16153
+ logged: true,
16154
+ cause: error,
16155
+ meta: {
16156
+ subsystem: "redis",
16157
+ operation: "ping"
16158
+ }
16159
+ });
15857
16160
  }
15858
16161
  }
15859
16162
  }
@@ -15871,7 +16174,16 @@ var redisPlugin = {
15871
16174
  return new RedisHelper(redisPrefix);
15872
16175
  } catch (error) {
15873
16176
  Logger.error({ env, err: error, msg: "Redis \u521D\u59CB\u5316\u5931\u8D25" });
15874
- throw error;
16177
+ throw new CoreError({
16178
+ kind: "runtime",
16179
+ message: "Redis \u521D\u59CB\u5316\u5931\u8D25",
16180
+ logged: true,
16181
+ cause: error,
16182
+ meta: {
16183
+ subsystem: "redis",
16184
+ operation: "init"
16185
+ }
16186
+ });
15875
16187
  }
15876
16188
  }
15877
16189
  };
@@ -16221,7 +16533,49 @@ class Befly {
16221
16533
  Logger.info(`\u670D\u52A1\u5668\u76D1\u542C\u5730\u5740: ${server.url}`);
16222
16534
  return server;
16223
16535
  } catch (error) {
16224
- Logger.error({ err: error, msg: "\u9879\u76EE\u542F\u52A8\u5931\u8D25" });
16536
+ const errMessage = error instanceof Error ? error.message : String(error);
16537
+ let kind = "runtime";
16538
+ let alreadyLogged = false;
16539
+ let noLog = false;
16540
+ if (isCoreError(error)) {
16541
+ kind = error.kind;
16542
+ alreadyLogged = error.logged === true;
16543
+ noLog = error.noLog === true;
16544
+ } else {
16545
+ const anyErr = error;
16546
+ if (anyErr && anyErr.__syncTableLogged === true) {
16547
+ alreadyLogged = true;
16548
+ }
16549
+ if (anyErr && anyErr.__syncTableNoLog === true) {
16550
+ kind = "policy";
16551
+ }
16552
+ if (anyErr && anyErr.__coreLogged === true) {
16553
+ alreadyLogged = true;
16554
+ }
16555
+ if (anyErr && anyErr.__coreNoLog === true) {
16556
+ noLog = true;
16557
+ }
16558
+ }
16559
+ if (alreadyLogged) {
16560
+ Logger.error({
16561
+ msg: "\u9879\u76EE\u542F\u52A8\u5931\u8D25\uFF08\u4E0B\u5C42\u5DF2\u8BB0\u5F55\uFF09",
16562
+ errorKind: kind,
16563
+ errorMessage: errMessage
16564
+ });
16565
+ } else if (noLog) {
16566
+ Logger.error({
16567
+ msg: "\u9879\u76EE\u542F\u52A8\u5931\u8D25",
16568
+ errorKind: kind,
16569
+ errorMessage: errMessage
16570
+ });
16571
+ } else {
16572
+ Logger.error({
16573
+ msg: "\u9879\u76EE\u542F\u52A8\u5931\u8D25",
16574
+ errorKind: kind,
16575
+ errorMessage: errMessage,
16576
+ err: error
16577
+ });
16578
+ }
16225
16579
  try {
16226
16580
  await Logger.flush();
16227
16581
  } catch {}