befly 3.20.6 → 3.20.8

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.
@@ -2,6 +2,56 @@ import { toSqlParams } from "../sqlBuilder/util.js";
2
2
  import { snakeCase } from "../../utils/util.js";
3
3
  import { validateExecuteSql } from "./validate.js";
4
4
 
5
+ function safeStringify(value) {
6
+ try {
7
+ return JSON.stringify(value);
8
+ } catch {
9
+ try {
10
+ return String(value);
11
+ } catch {
12
+ return "[Unserializable]";
13
+ }
14
+ }
15
+ }
16
+
17
+ function getExecuteErrorMessage(error) {
18
+ if (error instanceof Error && typeof error.message === "string" && error.message.trim().length > 0) {
19
+ return error.message.trim();
20
+ }
21
+
22
+ if (!error || typeof error !== "object") {
23
+ return String(error);
24
+ }
25
+
26
+ const message = typeof error.message === "string" && error.message.trim().length > 0 ? error.message.trim() : typeof error.sqlMessage === "string" && error.sqlMessage.trim().length > 0 ? error.sqlMessage.trim() : "";
27
+ const detail = {};
28
+
29
+ if (typeof error.code === "string" && error.code.trim().length > 0) {
30
+ detail.code = error.code;
31
+ }
32
+ if (typeof error.errno === "number") {
33
+ detail.errno = error.errno;
34
+ }
35
+ if (typeof error.sqlState === "string" && error.sqlState.trim().length > 0) {
36
+ detail.sqlState = error.sqlState;
37
+ }
38
+ if (typeof error.sqlMessage === "string" && error.sqlMessage.trim().length > 0) {
39
+ detail.sqlMessage = error.sqlMessage;
40
+ }
41
+ if (typeof error.detail === "string" && error.detail.trim().length > 0) {
42
+ detail.detail = error.detail;
43
+ }
44
+
45
+ const detailText = Object.keys(detail).length > 0 ? safeStringify(detail) : safeStringify(error);
46
+ if (message && detailText && detailText !== "{}") {
47
+ return `${message} | ${detailText}`;
48
+ }
49
+ if (message) {
50
+ return message;
51
+ }
52
+ return detailText;
53
+ }
54
+
5
55
  export const executeMethods = {
6
56
  async execute(sql, params) {
7
57
  if (!this.sql) {
@@ -44,21 +94,24 @@ export const executeMethods = {
44
94
  };
45
95
  } catch (error) {
46
96
  const duration = Date.now() - startTime;
47
- const msg = error instanceof Error ? error.message : String(error);
48
-
49
- throw new Error(`SQL执行失败: ${msg}`, {
50
- cause: error,
51
- code: "runtime",
52
- subsystem: "sql",
53
- operation: "execute",
97
+ const msg = getExecuteErrorMessage(error);
98
+ const sqlInfo = {
99
+ sql: sql,
54
100
  params: safeParams,
55
- duration: duration,
56
- sqlInfo: {
57
- sql: sql,
58
- params: safeParams,
59
- duration: duration
60
- }
101
+ duration: duration
102
+ };
103
+ const wrappedError = new Error(`SQL执行失败: ${msg}`, {
104
+ cause: error
61
105
  });
106
+
107
+ wrappedError.code = "runtime";
108
+ wrappedError.subsystem = "sql";
109
+ wrappedError.operation = "execute";
110
+ wrappedError.params = safeParams;
111
+ wrappedError.duration = duration;
112
+ wrappedError.sqlInfo = sqlInfo;
113
+
114
+ throw wrappedError;
62
115
  }
63
116
  },
64
117
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.20.6",
4
- "gitHead": "fc0f4f7193f9e234c87732cd7fc0b3b53591cee5",
3
+ "version": "3.20.8",
4
+ "gitHead": "c5906450b26c4f674094344de80a750c5f753736",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
7
7
  "keywords": [
@@ -52,7 +52,7 @@
52
52
  "test": "bun test"
53
53
  },
54
54
  "dependencies": {
55
- "fast-xml-parser": "^5.5.7",
55
+ "fast-xml-parser": "^5.5.8",
56
56
  "nodemailer": "^8.0.2",
57
57
  "pathe": "^2.0.3",
58
58
  "ua-parser-js": "^2.0.9",
@@ -41,11 +41,28 @@ function sanitizeErrorValue(err, options) {
41
41
  name: err.name || "Error",
42
42
  message: truncateString(err.message || "", options.maxStringLen)
43
43
  };
44
+ const state = { nodes: 0 };
45
+ const visited = new WeakSet();
46
+
47
+ visited.add(err);
44
48
 
45
49
  if (isString(err.stack)) {
46
50
  out["stack"] = truncateString(err.stack, options.maxStringLen);
47
51
  }
48
52
 
53
+ if (!isNullable(err.cause)) {
54
+ out["cause"] = sanitizeAny(err.cause, options, state, 0, visited);
55
+ }
56
+
57
+ const ownKeys = Object.keys(err);
58
+ for (const key of ownKeys) {
59
+ if (key === "name" || key === "message" || key === "stack" || key === "cause") {
60
+ continue;
61
+ }
62
+
63
+ out[key] = sanitizeAny(err[key], options, state, 0, visited);
64
+ }
65
+
49
66
  return out;
50
67
  }
51
68
 
package/utils/util.js CHANGED
@@ -130,6 +130,7 @@ export function forOwn(obj, iteratee) {
130
130
  // 将字符串拆分为词段(用于 camelCase/snakeCase)
131
131
  function toWordParts(input) {
132
132
  const normalized = String(input)
133
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
133
134
  .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
134
135
  .replace(/[^a-zA-Z0-9]+/g, " ")
135
136
  .trim();
@@ -169,29 +170,17 @@ export function camelCase(input) {
169
170
  return [first].concat(rest).join("");
170
171
  }
171
172
 
172
- // 归一化字符串为词序列
173
- function normalizeToWords(input) {
174
- return String(input)
175
- .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
176
- .replace(/[^a-zA-Z0-9]+/g, " ")
177
- .trim();
178
- }
179
-
180
173
  /**
181
174
  * 把字符串转为 snake_case。
182
175
  * - 主要用于表名/字段名(例如 userId -> user_id)。
183
176
  */
184
177
  export function snakeCase(input) {
185
- const normalized = normalizeToWords(input);
186
- if (normalized.length === 0) {
178
+ const parts = toWordParts(input);
179
+ if (parts.length === 0) {
187
180
  return "";
188
181
  }
189
182
 
190
- return normalized
191
- .split(/\s+/)
192
- .filter((p) => p.length > 0)
193
- .map((p) => p.toLowerCase())
194
- .join("_");
183
+ return parts.map((p) => p.toLowerCase()).join("_");
195
184
  }
196
185
 
197
186
  /**