befly 3.20.5 → 3.20.7

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/apis/dict/all.js CHANGED
@@ -11,12 +11,7 @@ export default {
11
11
  handler: async (befly) => {
12
12
  const result = await befly.mysql.getAll({
13
13
  table: "beflyDict",
14
- joins: [
15
- {
16
- table: "beflyDictType",
17
- on: "beflyDict.typeCode = beflyDictType.code"
18
- }
19
- ],
14
+ leftJoin: ["beflyDictType ON beflyDict.typeCode = beflyDictType.code"],
20
15
  fields: ["beflyDict.id", "beflyDict.typeCode", "beflyDict.key", "beflyDict.label", "beflyDict.sort", "beflyDict.remark", "beflyDict.createdAt", "beflyDict.updatedAt", "beflyDictType.name AS typeName"],
21
16
  orderBy: ["beflyDict.sort#ASC", "beflyDict.id#ASC"]
22
17
  });
@@ -10,12 +10,7 @@ export default {
10
10
  handler: async (befly, ctx) => {
11
11
  const dict = await befly.mysql.getOne({
12
12
  table: "beflyDict",
13
- joins: [
14
- {
15
- table: "beflyDictType",
16
- on: "beflyDict.typeCode = beflyDictType.code"
17
- }
18
- ],
13
+ leftJoin: ["beflyDictType ON beflyDict.typeCode = beflyDictType.code"],
19
14
  fields: ["beflyDict.id", "beflyDict.typeCode", "beflyDict.key", "beflyDict.label", "beflyDict.sort", "beflyDict.remark", "beflyDict.createdAt", "beflyDict.updatedAt", "beflyDictType.name AS typeName"],
20
15
  where: { "beflyDict.id": ctx.body.id }
21
16
  });
package/apis/dict/list.js CHANGED
@@ -15,12 +15,7 @@ export default {
15
15
  handler: async (befly, ctx) => {
16
16
  const result = await befly.mysql.getList({
17
17
  table: "beflyDict",
18
- joins: [
19
- {
20
- table: "beflyDictType",
21
- on: "beflyDict.typeCode = beflyDictType.code"
22
- }
23
- ],
18
+ leftJoin: ["beflyDictType ON beflyDict.typeCode = beflyDictType.code"],
24
19
  fields: ["beflyDict.id", "beflyDict.typeCode", "beflyDict.key", "beflyDict.label", "beflyDict.sort", "beflyDict.remark", "beflyDict.createdAt", "beflyDict.updatedAt", "beflyDictType.name AS typeName"],
25
20
  where: {
26
21
  "beflyDict.typeCode": ctx.body.typeCode
@@ -349,6 +349,29 @@ export function processJoinOrderBy(orderBy) {
349
349
  });
350
350
  }
351
351
 
352
+ export function parseLeftJoinItem(joinItem) {
353
+ if (!isString(joinItem)) {
354
+ return null;
355
+ }
356
+
357
+ const parts = joinItem.split(/\s+ON\s+/i);
358
+ if (parts.length !== 2) {
359
+ return null;
360
+ }
361
+
362
+ const table = parts[0]?.trim();
363
+ const on = parts[1]?.trim();
364
+ if (!isNonEmptyString(table) || !isNonEmptyString(on)) {
365
+ return null;
366
+ }
367
+
368
+ return {
369
+ type: "left",
370
+ table: normalizeTableRef(table),
371
+ on: processJoinOn(on)
372
+ };
373
+ }
374
+
352
375
  export function processJoinOn(on) {
353
376
  if (!isString(on)) {
354
377
  return String(on);
@@ -402,7 +425,7 @@ export function processJoinOn(on) {
402
425
  return result;
403
426
  }
404
427
 
405
- export function addDefaultStateFilter(where = {}, table, hasJoins = false, beflyMode = "auto") {
428
+ export function addDefaultStateFilter(where = {}, table, hasLeftJoin = false, beflyMode = "auto") {
406
429
  if (beflyMode === "manual") {
407
430
  return where;
408
431
  }
@@ -412,7 +435,7 @@ export function addDefaultStateFilter(where = {}, table, hasJoins = false, befly
412
435
  return where;
413
436
  }
414
437
 
415
- if (hasJoins && table) {
438
+ if (hasLeftJoin && table) {
416
439
  const result = {};
417
440
  for (const [key, value] of Object.entries(where)) {
418
441
  result[key] = value;
@@ -575,7 +598,7 @@ export const builderMethods = {
575
598
 
576
599
  createListBuilder(prepared, whereFiltered, limit, offset) {
577
600
  const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(whereFiltered).limit(limit);
578
- this.applyJoins(builder, prepared.joins);
601
+ this.applyLeftJoins(builder, prepared.leftJoins);
579
602
  if (!isNullable(offset)) {
580
603
  builder.offset(offset);
581
604
  }
@@ -587,7 +610,7 @@ export const builderMethods = {
587
610
 
588
611
  async fetchCount(prepared, whereFiltered, alias) {
589
612
  const builder = this.createSqlBuilder().selectRaw(alias).from(prepared.table).where(whereFiltered);
590
- this.applyJoins(builder, prepared.joins);
613
+ this.applyLeftJoins(builder, prepared.leftJoins);
591
614
  const result = builder.toSelectSql();
592
615
  const executeRes = await this.execute(result.sql, result.params);
593
616
  const countRow = executeRes.data?.[0] || null;
@@ -624,17 +647,24 @@ export const builderMethods = {
624
647
  return normalizeBigIntValues(deserializedList);
625
648
  },
626
649
 
627
- normalizeJoinOptions(options, cleanWhere) {
650
+ normalizeLeftJoinOptions(options, cleanWhere) {
628
651
  const processedFields = (options.fields || []).map((field) => processJoinField(field));
629
652
  const normalizedTableRef = normalizeTableRef(options.table);
630
653
  const mainQualifier = getJoinMainQualifier(options.table);
654
+ const leftJoins = [];
655
+ for (const joinItem of options.leftJoin || []) {
656
+ const parsedJoin = parseLeftJoinItem(joinItem);
657
+ if (parsedJoin) {
658
+ leftJoins.push(parsedJoin);
659
+ }
660
+ }
631
661
 
632
662
  return {
633
663
  table: normalizedTableRef,
634
664
  tableQualifier: mainQualifier,
635
665
  fields: processedFields.length > 0 ? processedFields : ["*"],
636
666
  where: processJoinWhere(cleanWhere),
637
- joins: options.joins,
667
+ leftJoins: leftJoins,
638
668
  orderBy: processJoinOrderBy(options.orderBy || []),
639
669
  page: options.page || 1,
640
670
  limit: options.limit || 10
@@ -650,7 +680,7 @@ export const builderMethods = {
650
680
  tableQualifier: snakeTable,
651
681
  fields: processedFields,
652
682
  where: whereKeysToSnake(cleanWhere),
653
- joins: undefined,
683
+ leftJoins: undefined,
654
684
  orderBy: orderByToSnake(options.orderBy || []),
655
685
  page: options.page || 1,
656
686
  limit: options.limit || 10
@@ -666,7 +696,7 @@ export const builderMethods = {
666
696
 
667
697
  if (options.fields !== undefined) output.fields = options.fields;
668
698
  if (options.where !== undefined) output.where = options.where;
669
- if (options.joins !== undefined) output.joins = options.joins;
699
+ if (options.leftJoin !== undefined) output.leftJoin = options.leftJoin;
670
700
  if (options.orderBy !== undefined) output.orderBy = options.orderBy;
671
701
  return output;
672
702
  },
@@ -720,35 +750,20 @@ export const builderMethods = {
720
750
  async prepareQueryOptions(options, label = "queryOptions") {
721
751
  validateQueryOptions(options, label);
722
752
  const cleanWhere = clearDeep(options.where || {});
723
- const hasJoins = options.joins && options.joins.length > 0;
753
+ const hasLeftJoin = options.leftJoin && options.leftJoin.length > 0;
724
754
 
725
- if (hasJoins) {
726
- return this.normalizeJoinOptions(options, cleanWhere);
755
+ if (hasLeftJoin) {
756
+ return this.normalizeLeftJoinOptions(options, cleanWhere);
727
757
  }
728
758
 
729
759
  return await this.normalizeSingleTableOptions(options, cleanWhere);
730
760
  },
731
761
 
732
- applyJoins(builder, joins) {
733
- if (!joins || joins.length === 0) return;
734
-
735
- for (const join of joins) {
736
- const processedTable = normalizeTableRef(join.table);
737
- const processedOn = processJoinOn(join.on);
738
- const type = join.type || "left";
739
-
740
- switch (type) {
741
- case "inner":
742
- builder.innerJoin(processedTable, processedOn);
743
- break;
744
- case "right":
745
- builder.rightJoin(processedTable, processedOn);
746
- break;
747
- case "left":
748
- default:
749
- builder.leftJoin(processedTable, processedOn);
750
- break;
751
- }
762
+ applyLeftJoins(builder, leftJoins) {
763
+ if (!leftJoins || leftJoins.length === 0) return;
764
+
765
+ for (const join of leftJoins) {
766
+ builder.leftJoin(join.table, join.on);
752
767
  }
753
768
  }
754
769
  };
@@ -4,16 +4,16 @@ import { Logger } from "../logger.js";
4
4
  import { SqlBuilder } from "../sqlBuilder/index.js";
5
5
  import { normalizeSqlMetaNumber, quoteIdentMySql } from "./util.js";
6
6
  import { addDefaultStateFilter, buildInsertRow, buildPartialUpdateData, buildUpdateRow, clearDeep, whereKeysToSnake } from "./builders.js";
7
- import { assertBatchInsertRowsConsistent, assertNoUndefinedInRecord, validateGeneratedBatchId, validateIncrementOptions, validateInsertBatchSize, validateNoJoinReadOptions, validatePageLimitRange, validateSafeFieldName, validateTableBatchDataOptions, validateTableDataOptions, validateTableName, validateTableWhereOptions } from "./validate.js";
7
+ import { assertBatchInsertRowsConsistent, assertNoUndefinedInRecord, validateGeneratedBatchId, validateIncrementOptions, validateInsertBatchSize, validateNoLeftJoinReadOptions, validatePageLimitRange, validateSafeFieldName, validateTableBatchDataOptions, validateTableDataOptions, validateTableName, validateTableWhereOptions } from "./validate.js";
8
8
 
9
9
  export const dataOpsMethods = {
10
10
  // 读取操作
11
11
  async getCount(options) {
12
- const { table, where, joins, tableQualifier } = await this.prepareQueryOptions(options, "getCount.options");
13
- const hasJoins = Array.isArray(joins) && joins.length > 0;
12
+ const { table, where, leftJoins, tableQualifier } = await this.prepareQueryOptions(options, "getCount.options");
13
+ const hasLeftJoin = Array.isArray(leftJoins) && leftJoins.length > 0;
14
14
 
15
- const whereFiltered = addDefaultStateFilter(where, tableQualifier, hasJoins, this.beflyMode);
16
- const result = await this.fetchCount({ table: table, joins: joins }, whereFiltered, "COUNT(*) as count");
15
+ const whereFiltered = addDefaultStateFilter(where, tableQualifier, hasLeftJoin, this.beflyMode);
16
+ const result = await this.fetchCount({ table: table, leftJoins: leftJoins }, whereFiltered, "COUNT(*) as count");
17
17
 
18
18
  return {
19
19
  data: result.total,
@@ -22,12 +22,12 @@ export const dataOpsMethods = {
22
22
  },
23
23
 
24
24
  async getOne(options) {
25
- const { table, fields, where, joins, tableQualifier } = await this.prepareQueryOptions(options, "getOne.options");
26
- const hasJoins = Array.isArray(joins) && joins.length > 0;
25
+ const { table, fields, where, leftJoins, tableQualifier } = await this.prepareQueryOptions(options, "getOne.options");
26
+ const hasLeftJoin = Array.isArray(leftJoins) && leftJoins.length > 0;
27
27
 
28
- const whereFiltered = addDefaultStateFilter(where, tableQualifier, hasJoins, this.beflyMode);
28
+ const whereFiltered = addDefaultStateFilter(where, tableQualifier, hasLeftJoin, this.beflyMode);
29
29
  const builder = this.createSqlBuilder().select(fields).from(table).where(whereFiltered);
30
- this.applyJoins(builder, joins);
30
+ this.applyLeftJoins(builder, leftJoins);
31
31
 
32
32
  const { sql, params } = builder.toSelectSql();
33
33
  const executeRes = await this.execute(sql, params);
@@ -48,8 +48,8 @@ export const dataOpsMethods = {
48
48
  const prepared = await this.prepareQueryOptions(options, "getList.options");
49
49
  validatePageLimitRange(prepared, options.table);
50
50
 
51
- const hasJoins = Array.isArray(prepared.joins) && prepared.joins.length > 0;
52
- const whereFiltered = addDefaultStateFilter(prepared.where, prepared.tableQualifier, hasJoins, this.beflyMode);
51
+ const hasLeftJoin = Array.isArray(prepared.leftJoins) && prepared.leftJoins.length > 0;
52
+ const whereFiltered = addDefaultStateFilter(prepared.where, prepared.tableQualifier, hasLeftJoin, this.beflyMode);
53
53
  const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
54
54
  const total = countResult.total;
55
55
 
@@ -100,8 +100,8 @@ export const dataOpsMethods = {
100
100
  const prepareOptions = this.buildQueryOptions(options, { page: 1, limit: 10 });
101
101
  const prepared = await this.prepareQueryOptions(prepareOptions, "getAll.options");
102
102
 
103
- const hasJoins = Array.isArray(prepared.joins) && prepared.joins.length > 0;
104
- const whereFiltered = addDefaultStateFilter(prepared.where, prepared.tableQualifier, hasJoins, this.beflyMode);
103
+ const hasLeftJoin = Array.isArray(prepared.leftJoins) && prepared.leftJoins.length > 0;
104
+ const whereFiltered = addDefaultStateFilter(prepared.where, prepared.tableQualifier, hasLeftJoin, this.beflyMode);
105
105
  const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
106
106
  const total = countResult.total;
107
107
 
@@ -146,7 +146,7 @@ export const dataOpsMethods = {
146
146
  },
147
147
 
148
148
  async exists(options) {
149
- validateNoJoinReadOptions(options, "exists", "exists 不支持 joins(请使用显式 query 或拆分查询)");
149
+ validateNoLeftJoinReadOptions(options, "exists", "exists 不支持 leftJoin(请使用显式 query 或拆分查询)");
150
150
  const snakeTable = snakeCase(options.table);
151
151
  const snakeWhere = whereKeysToSnake(clearDeep(options.where || {}));
152
152
  const whereFiltered = addDefaultStateFilter(snakeWhere, snakeTable, false, this.beflyMode);
@@ -159,7 +159,7 @@ export const dataOpsMethods = {
159
159
  },
160
160
 
161
161
  async getFieldValue(options) {
162
- validateNoJoinReadOptions(options, "getFieldValue", "getFieldValue 不支持 joins(请使用 getOne/getList 并自行取字段)");
162
+ validateNoLeftJoinReadOptions(options, "getFieldValue", "getFieldValue 不支持 leftJoin(请使用 getOne/getList 并自行取字段)");
163
163
  const field = options.field;
164
164
  validateSafeFieldName(field);
165
165
 
@@ -167,7 +167,7 @@ export const dataOpsMethods = {
167
167
  table: options.table
168
168
  };
169
169
  if (options.where !== undefined) oneOptions.where = options.where;
170
- if (options.joins !== undefined) oneOptions.joins = options.joins;
170
+ if (options.leftJoin !== undefined) oneOptions.leftJoin = options.leftJoin;
171
171
  if (options.orderBy !== undefined) oneOptions.orderBy = options.orderBy;
172
172
  if (options.page !== undefined) oneOptions.page = options.page;
173
173
  if (options.limit !== undefined) oneOptions.limit = options.limit;
@@ -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
 
@@ -130,34 +130,58 @@ function validateOrderByItems(orderBy, label) {
130
130
  }
131
131
  }
132
132
 
133
- function validateJoins(joins, label) {
134
- if (!Array.isArray(joins)) {
133
+ function validateLeftJoinItems(leftJoin, label) {
134
+ if (!Array.isArray(leftJoin)) {
135
135
  throw new Error(`${label} 必须是数组`, {
136
136
  cause: null,
137
137
  code: "validation"
138
138
  });
139
139
  }
140
140
 
141
- for (let i = 0; i < joins.length; i++) {
142
- const join = joins[i];
143
- if (!isPlainObject(join)) {
144
- throw new Error(`${label}[${i}] 必须是对象`, {
141
+ for (let i = 0; i < leftJoin.length; i++) {
142
+ const joinItem = leftJoin[i];
143
+ if (!isNonEmptyString(joinItem)) {
144
+ throw new Error(`${label}[${i}] 必须是非空字符串`, {
145
145
  cause: null,
146
146
  code: "validation"
147
147
  });
148
148
  }
149
149
 
150
- assertNonEmptyString(join.table, `${label}[${i}].table`);
151
- assertNonEmptyString(join.on, `${label}[${i}].on`);
150
+ const parts = joinItem.split(/\s+ON\s+/i);
151
+ if (parts.length !== 2) {
152
+ throw new Error(`${label}[${i}] 必须是 "table alias ON left.field = right.field" 格式`, {
153
+ cause: null,
154
+ code: "validation"
155
+ });
156
+ }
152
157
 
153
- if (!isNullable(join.type)) {
154
- const type = isString(join.type) ? join.type.trim().toLowerCase() : "";
155
- if (type !== "left" && type !== "right" && type !== "inner") {
156
- throw new Error(`${label}[${i}].type 只允许 left/right/inner`, {
157
- cause: null,
158
- code: "validation"
159
- });
160
- }
158
+ const tableRef = parts[0]?.trim();
159
+ const on = parts[1]?.trim();
160
+ if (!isNonEmptyString(tableRef)) {
161
+ throw new Error(`${label}[${i}] 缺少 leftJoin 表名`, {
162
+ cause: null,
163
+ code: "validation"
164
+ });
165
+ }
166
+ if (!isNonEmptyString(on)) {
167
+ throw new Error(`${label}[${i}] 缺少 leftJoin 的 ON 条件`, {
168
+ cause: null,
169
+ code: "validation"
170
+ });
171
+ }
172
+
173
+ parseTableRef(tableRef);
174
+ }
175
+ }
176
+
177
+ function validateLegacyJoinOptions(options, label) {
178
+ const legacyJoinKeys = ["joins", "innerJoin", "rightJoin"];
179
+ for (const key of legacyJoinKeys) {
180
+ if (!isNullable(options[key])) {
181
+ throw new Error(`${label} 仅支持 leftJoin,${label}.${key} 已废弃`, {
182
+ cause: null,
183
+ code: "validation"
184
+ });
161
185
  }
162
186
  }
163
187
  }
@@ -187,8 +211,10 @@ export function validateQueryOptions(options, label) {
187
211
  validateOrderByItems(options.orderBy, `${label}.orderBy`);
188
212
  }
189
213
 
190
- if (!isNullable(options.joins)) {
191
- validateJoins(options.joins, `${label}.joins`);
214
+ validateLegacyJoinOptions(options, label);
215
+
216
+ if (!isNullable(options.leftJoin)) {
217
+ validateLeftJoinItems(options.leftJoin, `${label}.leftJoin`);
192
218
  }
193
219
 
194
220
  if (!isNullable(options.page) && (!isFiniteNumber(options.page) || Math.floor(options.page) !== options.page)) {
@@ -247,9 +273,10 @@ export function validateTableBatchDataOptions(table, dataList, label) {
247
273
  validateBatchDataList(dataList, `${label}.dataList`);
248
274
  }
249
275
 
250
- export function validateNoJoinReadOptions(options, label, joinErrorMessage) {
276
+ export function validateNoLeftJoinReadOptions(options, label, joinErrorMessage) {
251
277
  validateTableWhereOptions(options, label, false);
252
- validateNoJoins(options.joins, joinErrorMessage);
278
+ validateNoLeftJoin(options.leftJoin, joinErrorMessage);
279
+ validateLegacyJoinOptions(options, label);
253
280
  validateSimpleTableName(options.table, label);
254
281
  }
255
282
 
@@ -306,8 +333,8 @@ export function validatePageLimitRange(prepared, rawTable) {
306
333
  }
307
334
  }
308
335
 
309
- export function validateNoJoins(joins, message) {
310
- if (Array.isArray(joins) && joins.length > 0) {
336
+ export function validateNoLeftJoin(leftJoin, message) {
337
+ if (Array.isArray(leftJoin) && leftJoin.length > 0) {
311
338
  throw new Error(message, {
312
339
  cause: null,
313
340
  code: "validation"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.20.5",
4
- "gitHead": "523163fa281ad94542dda8e88551360c4cb8b200",
3
+ "version": "3.20.7",
4
+ "gitHead": "a337be6c93cbf8a8e70bf6ac32eeb30c57fba3a3",
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.5",
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