befly 3.14.0 → 3.14.1

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
@@ -105,16 +105,20 @@ function upperFirst(s) {
105
105
  if (s.length === 0) {
106
106
  return s;
107
107
  }
108
- return s[0].toUpperCase() + s.slice(1);
108
+ return s.charAt(0).toUpperCase() + s.slice(1);
109
109
  }
110
110
  function camelCase(input) {
111
111
  const parts = toWordParts(input);
112
112
  if (parts.length === 0) {
113
113
  return "";
114
114
  }
115
- const first = parts[0].toLowerCase();
115
+ const firstPart = parts[0];
116
+ if (!firstPart) {
117
+ return "";
118
+ }
119
+ const first = firstPart.toLowerCase();
116
120
  const rest = parts.slice(1).map((p) => upperFirst(p.toLowerCase()));
117
- return [first, ...rest].join("");
121
+ return [first].concat(rest).join("");
118
122
  }
119
123
  function normalizeToWords(input) {
120
124
  return String(input).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]+/g, " ").trim();
@@ -235,7 +239,7 @@ function sanitizeErrorValue(err, options) {
235
239
  message: truncateString(err.message || "", options.maxStringLen)
236
240
  };
237
241
  if (typeof err.stack === "string") {
238
- out.stack = truncateString(err.stack, options.maxStringLen);
242
+ out["stack"] = truncateString(err.stack, options.maxStringLen);
239
243
  }
240
244
  return out;
241
245
  }
@@ -323,8 +327,12 @@ function sanitizeAny(value, options, state, depth, visited) {
323
327
  const entries = Object.entries(obj);
324
328
  const limit = entries.length > options.sanitizeObjectKeys ? options.sanitizeObjectKeys : entries.length;
325
329
  for (let i = 0;i < limit; i++) {
326
- const key = entries[i][0];
327
- const child = entries[i][1];
330
+ const entry = entries[i];
331
+ if (!entry) {
332
+ continue;
333
+ }
334
+ const key = entry[0];
335
+ const child = entry[1];
328
336
  if (isSensitiveKey(key, options.sensitiveKeyMatcher)) {
329
337
  out[key] = "[MASKED]";
330
338
  continue;
@@ -365,10 +373,10 @@ function setCtxUser(userId, roleCode, nickname, roleType) {
365
373
  const store = storage.getStore();
366
374
  if (!store)
367
375
  return;
368
- store.userId = userId;
369
- store.roleCode = roleCode;
370
- store.nickname = nickname;
371
- store.roleType = roleType;
376
+ store.userId = userId === undefined ? null : userId;
377
+ store.roleCode = roleCode === undefined ? null : roleCode;
378
+ store.nickname = nickname === undefined ? null : nickname;
379
+ store.roleType = roleType === undefined ? null : roleType;
372
380
  }
373
381
  var storage;
374
382
  var init_asyncContext = __esm(() => {
@@ -798,8 +806,8 @@ function buildLogLine(level, record) {
798
806
  return `${safeJsonStringify(out)}
799
807
  `;
800
808
  }
801
- if (base.msg === undefined) {
802
- base.msg = "";
809
+ if (base["msg"] === undefined) {
810
+ base["msg"] = "";
803
811
  }
804
812
  return `${safeJsonStringify(base)}
805
813
  `;
@@ -809,7 +817,7 @@ function safeJsonStringify(obj) {
809
817
  return JSON.stringify(obj);
810
818
  } catch {
811
819
  try {
812
- return JSON.stringify({ level: obj.level, time: obj.time, pid: obj.pid, hostname: obj.hostname, msg: "[Unserializable log record]" });
820
+ return JSON.stringify({ level: obj["level"], time: obj["time"], pid: obj["pid"], hostname: obj["hostname"], msg: "[Unserializable log record]" });
813
821
  } catch {
814
822
  return '{"msg":"[Unserializable log record]"}';
815
823
  }
@@ -855,10 +863,10 @@ function metaToObject() {
855
863
  now: meta.now,
856
864
  durationSinceNowMs
857
865
  };
858
- obj.userId = meta.userId;
859
- obj.roleCode = meta.roleCode;
860
- obj.nickname = meta.nickname;
861
- obj.roleType = meta.roleType;
866
+ obj["userId"] = meta.userId;
867
+ obj["roleCode"] = meta.roleCode;
868
+ obj["nickname"] = meta.nickname;
869
+ obj["roleType"] = meta.roleType;
862
870
  return obj;
863
871
  }
864
872
  function mergeMetaIntoObject(input, meta) {
@@ -1309,7 +1317,7 @@ async function scanViewsDirToMenuConfigs(viewsDir, prefix, parentPath = "") {
1309
1317
  return menus;
1310
1318
  }
1311
1319
  function getParentPath(path) {
1312
- const parts = path.split("/").filter((p) => !!p);
1320
+ const parts = path.split("/").filter((p) => Boolean(p));
1313
1321
  if (parts.length <= 1) {
1314
1322
  return "";
1315
1323
  }
@@ -8468,7 +8476,7 @@ async function loadHooks(hooks) {
8468
8476
  hooksMap.push({
8469
8477
  name: hookName,
8470
8478
  enable: true,
8471
- deps: hook.deps,
8479
+ deps: Array.isArray(hook.deps) ? hook.deps : [],
8472
8480
  handler: hook.handler
8473
8481
  });
8474
8482
  }
@@ -8502,7 +8510,7 @@ async function loadPlugins(plugins, context) {
8502
8510
  pluginsMap.push({
8503
8511
  name: pluginName,
8504
8512
  enable: true,
8505
- deps: plugin.deps,
8513
+ deps: Array.isArray(plugin.deps) ? plugin.deps : [],
8506
8514
  handler: plugin.handler
8507
8515
  });
8508
8516
  } catch (error) {
@@ -8641,11 +8649,12 @@ function apiHandler(apis, hooks, context) {
8641
8649
  requestId,
8642
8650
  corsHeaders: {
8643
8651
  "X-Request-ID": requestId
8644
- },
8645
- api: apis.get(apiPath),
8646
- response: undefined,
8647
- result: undefined
8652
+ }
8648
8653
  };
8654
+ const api = apis.get(apiPath);
8655
+ if (api) {
8656
+ ctx.api = api;
8657
+ }
8649
8658
  return withCtx({
8650
8659
  requestId,
8651
8660
  method: req.method,
@@ -8666,9 +8675,9 @@ function apiHandler(apis, hooks, context) {
8666
8675
  apiName: ctx.api.name
8667
8676
  };
8668
8677
  if (ctx.body && Object.keys(ctx.body).length > 0) {
8669
- logData.body = ctx.body;
8678
+ logData["body"] = ctx.body;
8670
8679
  }
8671
- logData.msg = "request";
8680
+ logData["msg"] = "request";
8672
8681
  Logger.info(logData);
8673
8682
  }
8674
8683
  if (!ctx.api) {
@@ -10085,8 +10094,14 @@ async function getTableColumnsRuntime(runtime, tableName) {
10085
10094
  let max = null;
10086
10095
  const m = /^(\w+)\s*\((\d+)\)/.exec(baseType);
10087
10096
  if (m) {
10088
- baseType = m[1];
10089
- max = Number(m[2]);
10097
+ const base = m[1];
10098
+ const maxText = m[2];
10099
+ if (typeof base === "string") {
10100
+ baseType = base;
10101
+ }
10102
+ if (typeof maxText === "string") {
10103
+ max = Number(maxText);
10104
+ }
10090
10105
  }
10091
10106
  columns[row.name] = {
10092
10107
  type: baseType.toLowerCase(),
@@ -10115,9 +10130,13 @@ async function getTableIndexesRuntime(runtime, tableName) {
10115
10130
  const q = getSyncTableIndexesQuery({ dialect: "mysql", table: tableName, dbName: runtime.dbName });
10116
10131
  const result = await runtime.db.unsafe(q.sql, q.params);
10117
10132
  for (const row of result.data) {
10118
- if (!indexes[row.INDEX_NAME])
10119
- indexes[row.INDEX_NAME] = [];
10120
- indexes[row.INDEX_NAME].push(row.COLUMN_NAME);
10133
+ const indexName = row.INDEX_NAME;
10134
+ const current = indexes[indexName];
10135
+ if (Array.isArray(current)) {
10136
+ current.push(row.COLUMN_NAME);
10137
+ } else {
10138
+ indexes[indexName] = [row.COLUMN_NAME];
10139
+ }
10121
10140
  }
10122
10141
  } else if (runtime.dbDialect === "postgresql") {
10123
10142
  const q = getSyncTableIndexesQuery({ dialect: "postgresql", table: tableName, dbName: runtime.dbName });
@@ -10125,7 +10144,8 @@ async function getTableIndexesRuntime(runtime, tableName) {
10125
10144
  for (const row of result.data) {
10126
10145
  const m = /\(([^)]+)\)/.exec(row.indexdef);
10127
10146
  if (m) {
10128
- const col = m[1].replace(/"/g, "").trim();
10147
+ const colPart = m[1];
10148
+ const col = typeof colPart === "string" ? colPart.replace(/"/g, "").trim() : "";
10129
10149
  indexes[row.indexname] = [col];
10130
10150
  }
10131
10151
  }
@@ -10158,7 +10178,8 @@ async function ensureDbVersion(dbDialect, db) {
10158
10178
  throw new Error("\u65E0\u6CD5\u83B7\u53D6 MySQL \u7248\u672C\u4FE1\u606F");
10159
10179
  }
10160
10180
  const version = r.data[0].version;
10161
- const majorVersion = parseInt(String(version).split(".")[0], 10);
10181
+ const majorPart = String(version).split(".")[0] || "0";
10182
+ const majorVersion = parseInt(majorPart, 10);
10162
10183
  if (!Number.isFinite(majorVersion) || majorVersion < DB_VERSION_REQUIREMENTS.MYSQL_MIN_MAJOR) {
10163
10184
  throw new Error(`\u6B64\u811A\u672C\u4EC5\u652F\u6301 MySQL ${DB_VERSION_REQUIREMENTS.MYSQL_MIN_MAJOR}.0+\uFF0C\u5F53\u524D\u7248\u672C: ${version}`);
10164
10185
  }
@@ -10171,7 +10192,8 @@ async function ensureDbVersion(dbDialect, db) {
10171
10192
  }
10172
10193
  const versionText = r.data[0].version;
10173
10194
  const m = /PostgreSQL\s+(\d+)/i.exec(versionText);
10174
- const major = m ? parseInt(m[1], 10) : NaN;
10195
+ const majorText = m ? m[1] : undefined;
10196
+ const major = typeof majorText === "string" ? parseInt(majorText, 10) : NaN;
10175
10197
  if (!Number.isFinite(major) || major < DB_VERSION_REQUIREMENTS.POSTGRES_MIN_MAJOR) {
10176
10198
  throw new Error(`\u6B64\u811A\u672C\u8981\u6C42 PostgreSQL >= ${DB_VERSION_REQUIREMENTS.POSTGRES_MIN_MAJOR}\uFF0C\u5F53\u524D: ${versionText}`);
10177
10199
  }
@@ -10183,7 +10205,10 @@ async function ensureDbVersion(dbDialect, db) {
10183
10205
  throw new Error("\u65E0\u6CD5\u83B7\u53D6 SQLite \u7248\u672C\u4FE1\u606F");
10184
10206
  }
10185
10207
  const version = r.data[0].version;
10186
- const [maj, min, patch] = String(version).split(".").map((v) => parseInt(v, 10) || 0);
10208
+ const parts = String(version).split(".").map((v) => parseInt(v, 10) || 0);
10209
+ const maj = parts[0] ?? 0;
10210
+ const min = parts[1] ?? 0;
10211
+ const patch = parts[2] ?? 0;
10187
10212
  const vnum = maj * 1e4 + min * 100 + patch;
10188
10213
  if (!Number.isFinite(vnum) || vnum < DB_VERSION_REQUIREMENTS.SQLITE_MIN_VERSION_NUM) {
10189
10214
  throw new Error(`\u6B64\u811A\u672C\u8981\u6C42 SQLite >= ${DB_VERSION_REQUIREMENTS.SQLITE_MIN_VERSION}\uFF0C\u5F53\u524D: ${version}`);
@@ -10213,7 +10238,11 @@ function compareFieldDefinition(dbDialect, existingColumn, fieldDef) {
10213
10238
  }
10214
10239
  }
10215
10240
  const typeMapping = getTypeMapping(dbDialect);
10216
- const expectedType = typeMapping[fieldDef.type].toLowerCase();
10241
+ const mapped = typeMapping[fieldDef.type];
10242
+ if (typeof mapped !== "string") {
10243
+ throw new Error(`\u672A\u77E5\u5B57\u6BB5\u7C7B\u578B\u6620\u5C04\uFF1Adialect=${dbDialect} type=${String(fieldDef.type)}`);
10244
+ }
10245
+ const expectedType = mapped.toLowerCase();
10217
10246
  const currentType = existingColumn.type.toLowerCase();
10218
10247
  if (currentType !== expectedType) {
10219
10248
  changes.push({
@@ -10523,8 +10552,8 @@ var calcPerfTime = (startTime, endTime = Bun.nanoseconds()) => {
10523
10552
  // utils/processInfo.ts
10524
10553
  function getProcessRole(env) {
10525
10554
  const runtimeEnv = env || {};
10526
- const bunWorkerId = runtimeEnv.BUN_WORKER_ID;
10527
- const pm2InstanceId = runtimeEnv.PM2_INSTANCE_ID;
10555
+ const bunWorkerId = runtimeEnv["BUN_WORKER_ID"];
10556
+ const pm2InstanceId = runtimeEnv["PM2_INSTANCE_ID"];
10528
10557
  if (bunWorkerId !== undefined) {
10529
10558
  return {
10530
10559
  role: bunWorkerId === "" ? "primary" : "worker",
@@ -12311,7 +12340,7 @@ class Validator {
12311
12340
  return {
12312
12341
  code: failed ? 1 : 0,
12313
12342
  failed,
12314
- firstError: failed ? errors[0] : null,
12343
+ firstError: failed ? errors[0] ?? null : null,
12315
12344
  errors,
12316
12345
  errorFields,
12317
12346
  fieldErrors
@@ -12825,18 +12854,44 @@ class DbUtils {
12825
12854
  throw new Error("tableRef \u4E0D\u80FD\u4E3A\u7A7A");
12826
12855
  }
12827
12856
  const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
12857
+ if (parts.length === 0) {
12858
+ throw new Error("tableRef \u4E0D\u80FD\u4E3A\u7A7A");
12859
+ }
12828
12860
  if (parts.length > 2) {
12829
12861
  throw new Error(`\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08\u5305\u542B\u8FC7\u591A\u7247\u6BB5\uFF09\u3002\u8BF7\u4F7F\u7528\u6700\u7B80\u5F62\u5F0F\uFF1Atable \u6216 table alias \u6216 schema.table \u6216 schema.table alias (tableRef: ${trimmed})`);
12830
12862
  }
12831
12863
  const namePart = parts[0];
12832
- const aliasPart = parts.length === 2 ? parts[1] : null;
12864
+ if (typeof namePart !== "string" || namePart.trim() === "") {
12865
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1A\u7F3A\u5C11\u8868\u540D (tableRef: ${trimmed})`);
12866
+ }
12867
+ let aliasPart = null;
12868
+ if (parts.length === 2) {
12869
+ const alias = parts[1];
12870
+ if (typeof alias !== "string" || alias.trim() === "") {
12871
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1A\u7F3A\u5C11 alias (tableRef: ${trimmed})`);
12872
+ }
12873
+ aliasPart = alias;
12874
+ }
12833
12875
  const nameSegments = namePart.split(".");
12834
12876
  if (nameSegments.length > 2) {
12835
12877
  throw new Error(`\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08schema \u5C42\u7EA7\u8FC7\u6DF1\uFF09 (tableRef: ${trimmed})`);
12836
12878
  }
12837
- const schema = nameSegments.length === 2 ? nameSegments[0] : null;
12838
- const table = nameSegments.length === 2 ? nameSegments[1] : nameSegments[0];
12839
- return { schema, table, alias: aliasPart };
12879
+ if (nameSegments.length === 2) {
12880
+ const schema = nameSegments[0];
12881
+ const table2 = nameSegments[1];
12882
+ if (typeof schema !== "string" || schema.trim() === "") {
12883
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Aschema \u4E3A\u7A7A (tableRef: ${trimmed})`);
12884
+ }
12885
+ if (typeof table2 !== "string" || table2.trim() === "") {
12886
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Atable \u4E3A\u7A7A (tableRef: ${trimmed})`);
12887
+ }
12888
+ return { schema, table: table2, alias: aliasPart };
12889
+ }
12890
+ const table = nameSegments[0];
12891
+ if (typeof table !== "string" || table.trim() === "") {
12892
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Atable \u4E3A\u7A7A (tableRef: ${trimmed})`);
12893
+ }
12894
+ return { schema: null, table, alias: aliasPart };
12840
12895
  }
12841
12896
  static normalizeTableRef(tableRef) {
12842
12897
  const parsed = DbUtils.parseTableRef(tableRef);
@@ -12914,7 +12969,15 @@ class DbUtils {
12914
12969
  if (typeof item !== "string" || !item.includes("#")) {
12915
12970
  return item;
12916
12971
  }
12917
- const [field, direction] = item.split("#");
12972
+ const parts = item.split("#");
12973
+ if (parts.length !== 2) {
12974
+ return item;
12975
+ }
12976
+ const field = parts[0];
12977
+ const direction = parts[1];
12978
+ if (typeof field !== "string" || typeof direction !== "string") {
12979
+ return item;
12980
+ }
12918
12981
  return `${snakeCase(field.trim())}#${direction.trim()}`;
12919
12982
  });
12920
12983
  }
@@ -12924,14 +12987,26 @@ class DbUtils {
12924
12987
  }
12925
12988
  if (field.toUpperCase().includes(" AS ")) {
12926
12989
  const parts = field.split(/\s+AS\s+/i);
12927
- const fieldPart = parts[0].trim();
12928
- const aliasPart = parts[1].trim();
12929
- return `${DbUtils.processJoinField(fieldPart)} AS ${aliasPart}`;
12990
+ const fieldPart = parts[0];
12991
+ const aliasPart = parts[1];
12992
+ if (typeof fieldPart !== "string" || typeof aliasPart !== "string") {
12993
+ return field;
12994
+ }
12995
+ return `${DbUtils.processJoinField(fieldPart.trim())} AS ${aliasPart.trim()}`;
12930
12996
  }
12931
12997
  if (field.includes(".")) {
12932
12998
  const parts = field.split(".");
12933
- const tableName = parts[0];
12934
- const fieldName = parts[1];
12999
+ if (parts.length < 2) {
13000
+ return snakeCase(field);
13001
+ }
13002
+ if (parts.some((p) => p.trim() === "")) {
13003
+ return field;
13004
+ }
13005
+ const fieldName = parts[parts.length - 1];
13006
+ const tableName = parts.slice(0, parts.length - 1).join(".");
13007
+ if (typeof fieldName !== "string" || typeof tableName !== "string") {
13008
+ return snakeCase(field);
13009
+ }
12935
13010
  return `${tableName.trim()}.${snakeCase(fieldName)}`;
12936
13011
  }
12937
13012
  return snakeCase(field);
@@ -12946,16 +13021,34 @@ class DbUtils {
12946
13021
  const operator = key.substring(lastDollarIndex);
12947
13022
  if (fieldPart.includes(".")) {
12948
13023
  const parts = fieldPart.split(".");
12949
- const tableName = parts[0];
12950
- const fieldName = parts[1];
13024
+ if (parts.length < 2) {
13025
+ return `${snakeCase(fieldPart)}${operator}`;
13026
+ }
13027
+ if (parts.some((p) => p.trim() === "")) {
13028
+ return `${snakeCase(fieldPart)}${operator}`;
13029
+ }
13030
+ const fieldName = parts[parts.length - 1];
13031
+ const tableName = parts.slice(0, parts.length - 1).join(".");
13032
+ if (typeof fieldName !== "string" || typeof tableName !== "string") {
13033
+ return `${snakeCase(fieldPart)}${operator}`;
13034
+ }
12951
13035
  return `${tableName.trim()}.${snakeCase(fieldName)}${operator}`;
12952
13036
  }
12953
13037
  return `${snakeCase(fieldPart)}${operator}`;
12954
13038
  }
12955
13039
  if (key.includes(".")) {
12956
13040
  const parts = key.split(".");
12957
- const tableName = parts[0];
12958
- const fieldName = parts[1];
13041
+ if (parts.length < 2) {
13042
+ return snakeCase(key);
13043
+ }
13044
+ if (parts.some((p) => p.trim() === "")) {
13045
+ return snakeCase(key);
13046
+ }
13047
+ const fieldName = parts[parts.length - 1];
13048
+ const tableName = parts.slice(0, parts.length - 1).join(".");
13049
+ if (typeof fieldName !== "string" || typeof tableName !== "string") {
13050
+ return snakeCase(key);
13051
+ }
12959
13052
  return `${tableName.trim()}.${snakeCase(fieldName)}`;
12960
13053
  }
12961
13054
  return snakeCase(key);
@@ -12988,7 +13081,15 @@ class DbUtils {
12988
13081
  if (typeof item !== "string" || !item.includes("#")) {
12989
13082
  return item;
12990
13083
  }
12991
- const [field, direction] = item.split("#");
13084
+ const parts = item.split("#");
13085
+ if (parts.length !== 2) {
13086
+ return item;
13087
+ }
13088
+ const field = parts[0];
13089
+ const direction = parts[1];
13090
+ if (typeof field !== "string" || typeof direction !== "string") {
13091
+ return item;
13092
+ }
12992
13093
  return `${DbUtils.processJoinField(field.trim())}#${direction.trim()}`;
12993
13094
  });
12994
13095
  }
@@ -13109,10 +13210,10 @@ class DbUtils {
13109
13210
  for (const [key, value] of Object.entries(userData)) {
13110
13211
  result[key] = value;
13111
13212
  }
13112
- result.id = options.id;
13113
- result.created_at = options.now;
13114
- result.updated_at = options.now;
13115
- result.state = 1;
13213
+ result["id"] = options.id;
13214
+ result["created_at"] = options.now;
13215
+ result["updated_at"] = options.now;
13216
+ result["state"] = 1;
13116
13217
  return result;
13117
13218
  }
13118
13219
  static buildUpdateRow(options) {
@@ -13122,7 +13223,7 @@ class DbUtils {
13122
13223
  for (const [key, value] of Object.entries(userData)) {
13123
13224
  result[key] = value;
13124
13225
  }
13125
- result.updated_at = options.now;
13226
+ result["updated_at"] = options.now;
13126
13227
  return result;
13127
13228
  }
13128
13229
  static buildPartialUpdateData(options) {
@@ -13246,6 +13347,7 @@ var SqlBuilderError = {
13246
13347
  QUOTE_IDENT_NEED_STRING: (identifier) => `quoteIdent \u9700\u8981\u5B57\u7B26\u4E32\u7C7B\u578B\u6807\u8BC6\u7B26 (identifier: ${String(identifier)})`,
13247
13348
  IDENT_EMPTY: "SQL \u6807\u8BC6\u7B26\u4E0D\u80FD\u4E3A\u7A7A",
13248
13349
  FIELD_EXPR_NOT_ALLOWED: (field) => `\u5B57\u6BB5\u5305\u542B\u51FD\u6570/\u8868\u8FBE\u5F0F\uFF0C\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${field})`,
13350
+ FIELD_INVALID: (field) => `\u5B57\u6BB5\u683C\u5F0F\u975E\u6CD5\uFF0C\u8BF7\u4F7F\u7528\u7B80\u5355\u5B57\u6BB5\u540D\u6216\u5B89\u5168\u5F15\u7528\uFF0C\u590D\u6742\u8868\u8FBE\u5F0F\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${field})`,
13249
13351
  FROM_EMPTY: "FROM \u8868\u540D\u4E0D\u80FD\u4E3A\u7A7A",
13250
13352
  FROM_NEED_NON_EMPTY: (table) => `FROM \u8868\u540D\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32 (table: ${String(table)})`,
13251
13353
  FROM_REQUIRED: "FROM \u8868\u540D\u662F\u5FC5\u9700\u7684",
@@ -13349,10 +13451,18 @@ class SqlBuilder {
13349
13451
  }
13350
13452
  if (field.toUpperCase().includes(" AS ")) {
13351
13453
  const parts = field.split(/\s+AS\s+/i);
13352
- const fieldPart = parts[0].trim();
13353
- const aliasPart = parts[1].trim();
13354
- SqlCheck.assertSafeAlias(aliasPart);
13355
- return `${this._escapeField(fieldPart)} AS ${aliasPart}`;
13454
+ if (parts.length !== 2) {
13455
+ throw new Error(SqlBuilderError.FIELD_INVALID(field));
13456
+ }
13457
+ const fieldPart = parts[0];
13458
+ const aliasPart = parts[1];
13459
+ if (typeof fieldPart !== "string" || typeof aliasPart !== "string") {
13460
+ throw new Error(SqlBuilderError.FIELD_INVALID(field));
13461
+ }
13462
+ const cleanFieldPart = fieldPart.trim();
13463
+ const cleanAliasPart = aliasPart.trim();
13464
+ SqlCheck.assertSafeAlias(cleanAliasPart);
13465
+ return `${this._escapeField(cleanFieldPart)} AS ${cleanAliasPart}`;
13356
13466
  }
13357
13467
  if (field.includes(".")) {
13358
13468
  const parts = field.split(".");
@@ -13386,16 +13496,26 @@ class SqlBuilder {
13386
13496
  if (parts.length > 2) {
13387
13497
  throw new Error(SqlBuilderError.TABLE_REF_TOO_MANY_PARTS(table));
13388
13498
  }
13389
- const namePart = parts[0].trim();
13390
- const aliasPart = parts.length === 2 ? parts[1].trim() : null;
13499
+ const namePartRaw = parts[0];
13500
+ if (typeof namePartRaw !== "string" || namePartRaw.trim() === "") {
13501
+ throw new Error(SqlBuilderError.FROM_EMPTY);
13502
+ }
13503
+ const namePart = namePartRaw.trim();
13504
+ const aliasPartRaw = parts.length === 2 ? parts[1] : null;
13505
+ const aliasPart = typeof aliasPartRaw === "string" ? aliasPartRaw.trim() : null;
13391
13506
  const nameSegments = namePart.split(".");
13392
13507
  if (nameSegments.length > 2) {
13393
13508
  throw new Error(SqlBuilderError.TABLE_REF_SCHEMA_TOO_DEEP(table));
13394
13509
  }
13395
13510
  let escapedName = "";
13396
13511
  if (nameSegments.length === 2) {
13397
- const schema = nameSegments[0].trim();
13398
- const tableName = nameSegments[1].trim();
13512
+ const schemaRaw = nameSegments[0];
13513
+ const tableNameRaw = nameSegments[1];
13514
+ if (typeof schemaRaw !== "string" || typeof tableNameRaw !== "string") {
13515
+ throw new Error(SqlBuilderError.TABLE_REF_SCHEMA_TOO_DEEP(table));
13516
+ }
13517
+ const schema = schemaRaw.trim();
13518
+ const tableName = tableNameRaw.trim();
13399
13519
  const escapedSchema = this._isQuotedIdent(schema) ? schema : (() => {
13400
13520
  if (this._startsWithQuote(schema) && !this._isQuotedIdent(schema)) {
13401
13521
  throw new Error(SqlBuilderError.SCHEMA_QUOTE_NOT_PAIRED(schema));
@@ -13412,7 +13532,11 @@ class SqlBuilder {
13412
13532
  })();
13413
13533
  escapedName = `${escapedSchema}.${escapedTableName}`;
13414
13534
  } else {
13415
- const tableName = nameSegments[0].trim();
13535
+ const tableNameRaw = nameSegments[0];
13536
+ if (typeof tableNameRaw !== "string") {
13537
+ throw new Error(SqlBuilderError.FROM_EMPTY);
13538
+ }
13539
+ const tableName = tableNameRaw.trim();
13416
13540
  if (this._isQuotedIdent(tableName)) {
13417
13541
  escapedName = tableName;
13418
13542
  } else {
@@ -13670,7 +13794,15 @@ class SqlBuilder {
13670
13794
  if (typeof item !== "string" || !item.includes("#")) {
13671
13795
  throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
13672
13796
  }
13673
- const [fieldName, direction] = item.split("#");
13797
+ const parts = item.split("#");
13798
+ if (parts.length !== 2) {
13799
+ throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
13800
+ }
13801
+ const fieldName = parts[0];
13802
+ const direction = parts[1];
13803
+ if (typeof fieldName !== "string" || typeof direction !== "string") {
13804
+ throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
13805
+ }
13674
13806
  const cleanField = fieldName.trim();
13675
13807
  const cleanDir = direction.trim().toUpperCase();
13676
13808
  if (!cleanField) {
@@ -13767,7 +13899,9 @@ class SqlBuilder {
13767
13899
  for (let i = 0;i < data.length; i++) {
13768
13900
  const row = data[i];
13769
13901
  for (const field of fields) {
13770
- params.push(row[field]);
13902
+ const value = row[field];
13903
+ this._validateParam(value);
13904
+ params.push(value);
13771
13905
  }
13772
13906
  }
13773
13907
  return { sql, params };
@@ -13782,7 +13916,12 @@ class SqlBuilder {
13782
13916
  const escapedFields = fields.map((field) => this._escapeField(field));
13783
13917
  const placeholders = fields.map(() => "?").join(", ");
13784
13918
  const sql = `INSERT INTO ${escapedTable} (${escapedFields.join(", ")}) VALUES (${placeholders})`;
13785
- const params = fields.map((field) => data[field]);
13919
+ const params = [];
13920
+ for (const field of fields) {
13921
+ const value = data[field];
13922
+ this._validateParam(value);
13923
+ params.push(value);
13924
+ }
13786
13925
  return { sql, params };
13787
13926
  }
13788
13927
  }
@@ -13884,7 +14023,9 @@ class SqlBuilder {
13884
14023
  }
13885
14024
  whenList.push("WHEN ? THEN ?");
13886
14025
  args.push(row.id);
13887
- args.push(row.data[field]);
14026
+ const value = row.data[field];
14027
+ SqlCheck.assertNoUndefinedParam(value, "SQL \u53C2\u6570\u503C");
14028
+ args.push(value);
13888
14029
  }
13889
14030
  if (whenList.length === 0) {
13890
14031
  continue;
@@ -13916,7 +14057,7 @@ class DbHelper {
13916
14057
  constructor(options) {
13917
14058
  this.redis = options.redis;
13918
14059
  this.sql = options.sql || null;
13919
- this.isTransaction = !!options.sql;
14060
+ this.isTransaction = Boolean(options.sql);
13920
14061
  this.dialect = options.dialect ? options.dialect : new MySqlDialect;
13921
14062
  }
13922
14063
  createSqlBuilder() {
@@ -14046,7 +14187,8 @@ class DbHelper {
14046
14187
  }
14047
14188
  async getCount(options) {
14048
14189
  const { table, where, joins, tableQualifier } = await this.prepareQueryOptions(options);
14049
- const builder = this.createSqlBuilder().selectRaw("COUNT(*) as count").from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, !!joins));
14190
+ const hasJoins = Array.isArray(joins) && joins.length > 0;
14191
+ const builder = this.createSqlBuilder().selectRaw("COUNT(*) as count").from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, hasJoins));
14050
14192
  this.applyJoins(builder, joins);
14051
14193
  const { sql, params } = builder.toSelectSql();
14052
14194
  const execRes = await this.executeWithConn(sql, params);
@@ -14058,7 +14200,8 @@ class DbHelper {
14058
14200
  }
14059
14201
  async getOne(options) {
14060
14202
  const { table, fields, where, joins, tableQualifier } = await this.prepareQueryOptions(options);
14061
- const builder = this.createSqlBuilder().select(fields).from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, !!joins));
14203
+ const hasJoins = Array.isArray(joins) && joins.length > 0;
14204
+ const builder = this.createSqlBuilder().select(fields).from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, hasJoins));
14062
14205
  this.applyJoins(builder, joins);
14063
14206
  const { sql, params } = builder.toSelectSql();
14064
14207
  const execRes = await this.executeWithConn(sql, params);
@@ -14078,12 +14221,16 @@ class DbHelper {
14078
14221
  sql: execRes.sql
14079
14222
  };
14080
14223
  }
14081
- const data = convertBigIntFields([deserialized])[0];
14224
+ const convertedList = convertBigIntFields([deserialized]);
14225
+ const data = convertedList[0] ?? deserialized;
14082
14226
  return {
14083
14227
  data,
14084
14228
  sql: execRes.sql
14085
14229
  };
14086
14230
  }
14231
+ async getDetail(options) {
14232
+ return await this.getOne(options);
14233
+ }
14087
14234
  async getList(options) {
14088
14235
  const prepared = await this.prepareQueryOptions(options);
14089
14236
  if (prepared.page < 1 || prepared.page > 1e4) {
@@ -14092,7 +14239,7 @@ class DbHelper {
14092
14239
  if (prepared.limit < 1 || prepared.limit > 1000) {
14093
14240
  throw new Error(`\u6BCF\u9875\u6570\u91CF\u5FC5\u987B\u5728 1 \u5230 1000 \u4E4B\u95F4 (table: ${options.table}, page: ${prepared.page}, limit: ${prepared.limit})`);
14094
14241
  }
14095
- const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, !!prepared.joins);
14242
+ const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, Array.isArray(prepared.joins) && prepared.joins.length > 0);
14096
14243
  const countBuilder = this.createSqlBuilder().selectRaw("COUNT(*) as total").from(prepared.table).where(whereFiltered);
14097
14244
  this.applyJoins(countBuilder, prepared.joins);
14098
14245
  const { sql: countSql, params: countParams } = countBuilder.toSelectSql();
@@ -14140,8 +14287,21 @@ class DbHelper {
14140
14287
  async getAll(options) {
14141
14288
  const MAX_LIMIT = 1e4;
14142
14289
  const WARNING_LIMIT = 1000;
14143
- const prepared = await this.prepareQueryOptions({ ...options, page: 1, limit: 10 });
14144
- const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, !!prepared.joins);
14290
+ const prepareOptions = {
14291
+ table: options.table,
14292
+ page: 1,
14293
+ limit: 10
14294
+ };
14295
+ if (options.fields !== undefined)
14296
+ prepareOptions.fields = options.fields;
14297
+ if (options.where !== undefined)
14298
+ prepareOptions.where = options.where;
14299
+ if (options.joins !== undefined)
14300
+ prepareOptions.joins = options.joins;
14301
+ if (options.orderBy !== undefined)
14302
+ prepareOptions.orderBy = options.orderBy;
14303
+ const prepared = await this.prepareQueryOptions(prepareOptions);
14304
+ const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, Array.isArray(prepared.joins) && prepared.joins.length > 0);
14145
14305
  const countBuilder = this.createSqlBuilder().selectRaw("COUNT(*) as total").from(prepared.table).where(whereFiltered);
14146
14306
  this.applyJoins(countBuilder, prepared.joins);
14147
14307
  const { sql: countSql, params: countParams } = countBuilder.toSelectSql();
@@ -14201,7 +14361,7 @@ class DbHelper {
14201
14361
  const builder = this.createSqlBuilder();
14202
14362
  const { sql, params } = builder.toInsertSql(snakeTable, processed);
14203
14363
  const execRes = await this.executeWithConn(sql, params);
14204
- const insertedId = processed.id || execRes.data?.lastInsertRowid || 0;
14364
+ const insertedId = processed["id"] || execRes.data?.lastInsertRowid || 0;
14205
14365
  return {
14206
14366
  data: insertedId,
14207
14367
  sql: execRes.sql
@@ -14226,7 +14386,11 @@ class DbHelper {
14226
14386
  }
14227
14387
  const now = Date.now();
14228
14388
  const processedList = dataList.map((data, index) => {
14229
- return DbUtils.buildInsertRow({ data, id: ids[index], now });
14389
+ const id = ids[index];
14390
+ if (typeof id !== "number") {
14391
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u751F\u6210 ID \u5931\u8D25\uFF1Aids[${index}] \u4E0D\u662F number (table: ${snakeTable})`);
14392
+ }
14393
+ return DbUtils.buildInsertRow({ data, id, now });
14230
14394
  });
14231
14395
  const insertFields = SqlCheck.assertBatchInsertRowsConsistent(processedList, { table: snakeTable });
14232
14396
  const builder = this.createSqlBuilder();
@@ -14509,38 +14673,50 @@ class Jwt {
14509
14673
  };
14510
14674
  }
14511
14675
  sign(payload, options = {}) {
14512
- const key = options.secret || this.config.secret || "befly-secret";
14676
+ const key = options.secret || this.config.secret;
14513
14677
  const algorithm = options.algorithm || this.config.algorithm || "HS256";
14514
- const signer = import_fast_jwt.createSigner({
14678
+ const signerOptions = {
14515
14679
  key,
14516
14680
  algorithm,
14517
- expiresIn: options.expiresIn || this.config.expiresIn,
14518
- iss: options.issuer,
14519
- aud: options.audience,
14520
- sub: options.subject,
14521
- jti: options.jwtId,
14522
- notBefore: typeof options.notBefore === "number" ? options.notBefore : undefined
14523
- });
14681
+ expiresIn: options.expiresIn ?? this.config.expiresIn
14682
+ };
14683
+ if (options.issuer !== undefined)
14684
+ signerOptions.iss = options.issuer;
14685
+ if (options.audience !== undefined)
14686
+ signerOptions.aud = options.audience;
14687
+ if (options.subject !== undefined)
14688
+ signerOptions.sub = options.subject;
14689
+ if (options.jwtId !== undefined)
14690
+ signerOptions.jti = options.jwtId;
14691
+ if (typeof options.notBefore === "number")
14692
+ signerOptions.notBefore = options.notBefore;
14693
+ const signer = import_fast_jwt.createSigner(signerOptions);
14524
14694
  return signer(payload);
14525
14695
  }
14526
14696
  verify(token, options = {}) {
14527
14697
  if (!token || typeof token !== "string") {
14528
14698
  throw new Error("Token\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
14529
14699
  }
14530
- const key = options.secret || this.config.secret || "befly-secret";
14700
+ const key = options.secret || this.config.secret;
14531
14701
  const algorithm = this.config.algorithm || "HS256";
14532
14702
  const algorithms = options.algorithms ? options.algorithms : [algorithm];
14533
- const verifier = import_fast_jwt.createVerifier({
14703
+ const verifierOptions = {
14534
14704
  key,
14535
14705
  algorithms,
14536
- allowedIss: options.issuer,
14537
- allowedAud: options.audience,
14538
- allowedSub: options.subject,
14539
- ignoreExpiration: options.ignoreExpiration,
14540
- ignoreNotBefore: options.ignoreNotBefore,
14541
14706
  cache: true,
14542
14707
  cacheTTL: 600000
14543
- });
14708
+ };
14709
+ if (options.issuer !== undefined)
14710
+ verifierOptions.allowedIss = options.issuer;
14711
+ if (options.audience !== undefined)
14712
+ verifierOptions.allowedAud = options.audience;
14713
+ if (options.subject !== undefined)
14714
+ verifierOptions.allowedSub = options.subject;
14715
+ if (typeof options.ignoreExpiration === "boolean")
14716
+ verifierOptions.ignoreExpiration = options.ignoreExpiration;
14717
+ if (typeof options.ignoreNotBefore === "boolean")
14718
+ verifierOptions.ignoreNotBefore = options.ignoreNotBefore;
14719
+ const verifier = import_fast_jwt.createVerifier(verifierOptions);
14544
14720
  return verifier(token);
14545
14721
  }
14546
14722
  decode(token, complete = false) {
@@ -15151,7 +15327,7 @@ async function scanFiles(dir, source, type, pattern) {
15151
15327
  const base = {
15152
15328
  source,
15153
15329
  type,
15154
- sourceName: { core: "\u6838\u5FC3", addon: "\u7EC4\u4EF6", app: "\u9879\u76EE" }[source],
15330
+ sourceName: source === "core" ? "\u6838\u5FC3" : source === "addon" ? "\u7EC4\u4EF6" : "\u9879\u76EE",
15155
15331
  filePath: normalizedFile,
15156
15332
  relativePath,
15157
15333
  fileName,
@@ -15162,28 +15338,28 @@ async function scanFiles(dir, source, type, pattern) {
15162
15338
  customKeys: isPlainObject(content) ? Object.keys(content) : []
15163
15339
  };
15164
15340
  if (type === "table") {
15165
- base.content = content;
15341
+ base["content"] = content;
15166
15342
  results.push(base);
15167
15343
  continue;
15168
15344
  }
15169
15345
  if (type === "api") {
15170
- base.auth = true;
15171
- base.rawBody = false;
15172
- base.method = "POST";
15173
- base.fields = {};
15174
- base.required = [];
15346
+ base["auth"] = true;
15347
+ base["rawBody"] = false;
15348
+ base["method"] = "POST";
15349
+ base["fields"] = {};
15350
+ base["required"] = [];
15175
15351
  }
15176
15352
  if (type === "plugin" || type === "hook") {
15177
- base.deps = [];
15178
- base.name = "";
15179
- base.handler = null;
15353
+ base["deps"] = [];
15354
+ base["name"] = "";
15355
+ base["handler"] = null;
15180
15356
  }
15181
15357
  forOwn(content, (value, key) => {
15182
15358
  base[key] = value;
15183
15359
  });
15184
15360
  if (type === "api") {
15185
- base.routePrefix = source === "app" ? "/app" : `/addon/${addonName}`;
15186
- base.path = `/api${base.routePrefix}/${relativePath}`;
15361
+ base["routePrefix"] = source === "app" ? "/app" : `/addon/${addonName}`;
15362
+ base["path"] = `/api${base["routePrefix"]}/${relativePath}`;
15187
15363
  }
15188
15364
  results.push(base);
15189
15365
  }
@@ -15239,11 +15415,11 @@ class Befly {
15239
15415
  async start(env = {}) {
15240
15416
  try {
15241
15417
  const serverStartTime = Bun.nanoseconds();
15242
- this.config = await loadBeflyConfig(env.NODE_ENV || "development");
15418
+ this.config = await loadBeflyConfig(env["NODE_ENV"] || "development");
15243
15419
  this.context.config = this.config;
15244
15420
  this.context.env = env;
15245
15421
  const { apis, tables, plugins, hooks, addons } = await scanSources();
15246
- this.context.addons = addons;
15422
+ this.context["addons"] = addons;
15247
15423
  await checkApi(apis);
15248
15424
  await checkTable(tables);
15249
15425
  await checkPlugin(plugins);
@@ -15266,15 +15442,23 @@ class Befly {
15266
15442
  await syncTable(this.context, tables);
15267
15443
  await syncApi(this.context, apis);
15268
15444
  await syncMenu(this.context, checkedMenus);
15269
- await syncDev(this.context, { devEmail: this.config.devEmail, devPassword: this.config.devPassword });
15445
+ const devEmail = this.config.devEmail;
15446
+ const devPassword = this.config.devPassword;
15447
+ if (typeof devEmail === "string" && devEmail.length > 0 && typeof devPassword === "string" && devPassword.length > 0) {
15448
+ await syncDev(this.context, { devEmail, devPassword });
15449
+ } else {
15450
+ Logger.debug("\u8DF3\u8FC7 syncDev\uFF1A\u672A\u914D\u7F6E devEmail/devPassword");
15451
+ }
15270
15452
  await syncCache(this.context);
15271
15453
  this.hooks = await loadHooks(hooks);
15272
15454
  this.apis = await loadApis(apis);
15273
15455
  const apiFetch = apiHandler(this.apis, this.hooks, this.context);
15274
15456
  const staticFetch = staticHandler(this.config.cors);
15457
+ const port = typeof this.config.appPort === "number" ? this.config.appPort : 3000;
15458
+ const hostname = typeof this.config.appHost === "string" && this.config.appHost.length > 0 ? this.config.appHost : "0.0.0.0";
15275
15459
  const server = Bun.serve({
15276
- port: this.config.appPort,
15277
- hostname: this.config.appHost,
15460
+ port,
15461
+ hostname,
15278
15462
  development: this.config.nodeEnv === "development",
15279
15463
  idleTimeout: 30,
15280
15464
  fetch: async (req, bunServer) => {