befly 3.16.10 → 3.17.0

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.
Files changed (184) hide show
  1. package/README.md +0 -129
  2. package/befly.js +16413 -0
  3. package/befly.min.js +72 -0
  4. package/package.json +19 -29
  5. package/scripts/syncDb/context.js +99 -0
  6. package/scripts/syncDb/diff.js +133 -0
  7. package/scripts/syncDb/index.js +70 -0
  8. package/scripts/syncDb/query.js +26 -0
  9. package/scripts/syncDb/report.js +190 -0
  10. package/scripts/syncDb/transform.js +111 -0
  11. package/dist/befly.config.d.ts +0 -7
  12. package/dist/befly.config.js +0 -128
  13. package/dist/befly.js +0 -17348
  14. package/dist/befly.min.js +0 -23
  15. package/dist/checks/checkApi.d.ts +0 -1
  16. package/dist/checks/checkApi.js +0 -139
  17. package/dist/checks/checkConfig.d.ts +0 -9
  18. package/dist/checks/checkConfig.js +0 -255
  19. package/dist/checks/checkHook.d.ts +0 -1
  20. package/dist/checks/checkHook.js +0 -132
  21. package/dist/checks/checkMenu.d.ts +0 -3
  22. package/dist/checks/checkMenu.js +0 -106
  23. package/dist/checks/checkPlugin.d.ts +0 -1
  24. package/dist/checks/checkPlugin.js +0 -132
  25. package/dist/checks/checkTable.d.ts +0 -7
  26. package/dist/checks/checkTable.js +0 -431
  27. package/dist/configs/presetRegexp.d.ts +0 -145
  28. package/dist/configs/presetRegexp.js +0 -218
  29. package/dist/hooks/auth.d.ts +0 -3
  30. package/dist/hooks/auth.js +0 -24
  31. package/dist/hooks/cors.d.ts +0 -7
  32. package/dist/hooks/cors.js +0 -36
  33. package/dist/hooks/parser.d.ts +0 -10
  34. package/dist/hooks/parser.js +0 -76
  35. package/dist/hooks/permission.d.ts +0 -11
  36. package/dist/hooks/permission.js +0 -78
  37. package/dist/hooks/validator.d.ts +0 -7
  38. package/dist/hooks/validator.js +0 -52
  39. package/dist/index.d.ts +0 -28
  40. package/dist/index.js +0 -316
  41. package/dist/lib/asyncContext.d.ts +0 -21
  42. package/dist/lib/asyncContext.js +0 -27
  43. package/dist/lib/cacheHelper.d.ts +0 -128
  44. package/dist/lib/cacheHelper.js +0 -477
  45. package/dist/lib/cacheKeys.d.ts +0 -27
  46. package/dist/lib/cacheKeys.js +0 -37
  47. package/dist/lib/cipher.d.ts +0 -153
  48. package/dist/lib/cipher.js +0 -237
  49. package/dist/lib/connect.d.ts +0 -95
  50. package/dist/lib/connect.js +0 -313
  51. package/dist/lib/dbHelper.d.ts +0 -229
  52. package/dist/lib/dbHelper.js +0 -1099
  53. package/dist/lib/dbUtils.d.ts +0 -91
  54. package/dist/lib/dbUtils.js +0 -544
  55. package/dist/lib/jwt.d.ts +0 -13
  56. package/dist/lib/jwt.js +0 -77
  57. package/dist/lib/logger.d.ts +0 -46
  58. package/dist/lib/logger.js +0 -731
  59. package/dist/lib/redisHelper.d.ts +0 -193
  60. package/dist/lib/redisHelper.js +0 -598
  61. package/dist/lib/sqlBuilder.d.ts +0 -160
  62. package/dist/lib/sqlBuilder.js +0 -837
  63. package/dist/lib/sqlCheck.d.ts +0 -23
  64. package/dist/lib/sqlCheck.js +0 -119
  65. package/dist/lib/validator.d.ts +0 -45
  66. package/dist/lib/validator.js +0 -424
  67. package/dist/loader/loadApis.d.ts +0 -12
  68. package/dist/loader/loadApis.js +0 -71
  69. package/dist/loader/loadHooks.d.ts +0 -7
  70. package/dist/loader/loadHooks.js +0 -50
  71. package/dist/loader/loadPlugins.d.ts +0 -8
  72. package/dist/loader/loadPlugins.js +0 -69
  73. package/dist/paths.d.ts +0 -93
  74. package/dist/paths.js +0 -100
  75. package/dist/plugins/cache.d.ts +0 -10
  76. package/dist/plugins/cache.js +0 -24
  77. package/dist/plugins/cipher.d.ts +0 -7
  78. package/dist/plugins/cipher.js +0 -14
  79. package/dist/plugins/config.d.ts +0 -3
  80. package/dist/plugins/config.js +0 -9
  81. package/dist/plugins/db.d.ts +0 -10
  82. package/dist/plugins/db.js +0 -48
  83. package/dist/plugins/jwt.d.ts +0 -6
  84. package/dist/plugins/jwt.js +0 -13
  85. package/dist/plugins/logger.d.ts +0 -10
  86. package/dist/plugins/logger.js +0 -21
  87. package/dist/plugins/redis.d.ts +0 -10
  88. package/dist/plugins/redis.js +0 -40
  89. package/dist/plugins/tool.d.ts +0 -75
  90. package/dist/plugins/tool.js +0 -105
  91. package/dist/router/api.d.ts +0 -14
  92. package/dist/router/api.js +0 -109
  93. package/dist/router/static.d.ts +0 -9
  94. package/dist/router/static.js +0 -56
  95. package/dist/scripts/ensureDist.d.ts +0 -1
  96. package/dist/scripts/ensureDist.js +0 -296
  97. package/dist/sync/syncApi.d.ts +0 -3
  98. package/dist/sync/syncApi.js +0 -163
  99. package/dist/sync/syncCache.d.ts +0 -2
  100. package/dist/sync/syncCache.js +0 -14
  101. package/dist/sync/syncDev.d.ts +0 -6
  102. package/dist/sync/syncDev.js +0 -166
  103. package/dist/sync/syncMenu.d.ts +0 -14
  104. package/dist/sync/syncMenu.js +0 -308
  105. package/dist/sync/syncTable.d.ts +0 -126
  106. package/dist/sync/syncTable.js +0 -1129
  107. package/dist/types/api.d.ts +0 -177
  108. package/dist/types/api.js +0 -4
  109. package/dist/types/befly.d.ts +0 -231
  110. package/dist/types/befly.js +0 -4
  111. package/dist/types/cache.d.ts +0 -96
  112. package/dist/types/cache.js +0 -4
  113. package/dist/types/cipher.d.ts +0 -27
  114. package/dist/types/cipher.js +0 -7
  115. package/dist/types/common.d.ts +0 -127
  116. package/dist/types/common.js +0 -5
  117. package/dist/types/context.d.ts +0 -39
  118. package/dist/types/context.js +0 -4
  119. package/dist/types/coreError.d.ts +0 -31
  120. package/dist/types/coreError.js +0 -38
  121. package/dist/types/crypto.d.ts +0 -20
  122. package/dist/types/crypto.js +0 -4
  123. package/dist/types/database.d.ts +0 -182
  124. package/dist/types/database.js +0 -4
  125. package/dist/types/hook.d.ts +0 -30
  126. package/dist/types/hook.js +0 -19
  127. package/dist/types/jwt.d.ts +0 -76
  128. package/dist/types/jwt.js +0 -4
  129. package/dist/types/logger.d.ts +0 -95
  130. package/dist/types/logger.js +0 -6
  131. package/dist/types/plugin.d.ts +0 -27
  132. package/dist/types/plugin.js +0 -17
  133. package/dist/types/redis.d.ts +0 -80
  134. package/dist/types/redis.js +0 -4
  135. package/dist/types/roleApisCache.d.ts +0 -21
  136. package/dist/types/roleApisCache.js +0 -8
  137. package/dist/types/sync.d.ts +0 -93
  138. package/dist/types/sync.js +0 -4
  139. package/dist/types/table.d.ts +0 -34
  140. package/dist/types/table.js +0 -4
  141. package/dist/types/validate.d.ts +0 -113
  142. package/dist/types/validate.js +0 -4
  143. package/dist/utils/calcPerfTime.d.ts +0 -4
  144. package/dist/utils/calcPerfTime.js +0 -13
  145. package/dist/utils/cors.d.ts +0 -8
  146. package/dist/utils/cors.js +0 -17
  147. package/dist/utils/dbFieldRules.d.ts +0 -31
  148. package/dist/utils/dbFieldRules.js +0 -94
  149. package/dist/utils/fieldClear.d.ts +0 -11
  150. package/dist/utils/fieldClear.js +0 -57
  151. package/dist/utils/formatYmdHms.d.ts +0 -1
  152. package/dist/utils/formatYmdHms.js +0 -20
  153. package/dist/utils/getClientIp.d.ts +0 -6
  154. package/dist/utils/getClientIp.js +0 -39
  155. package/dist/utils/importDefault.d.ts +0 -1
  156. package/dist/utils/importDefault.js +0 -53
  157. package/dist/utils/isDirentDirectory.d.ts +0 -3
  158. package/dist/utils/isDirentDirectory.js +0 -18
  159. package/dist/utils/loadMenuConfigs.d.ts +0 -11
  160. package/dist/utils/loadMenuConfigs.js +0 -130
  161. package/dist/utils/loggerUtils.d.ts +0 -18
  162. package/dist/utils/loggerUtils.js +0 -171
  163. package/dist/utils/mergeAndConcat.d.ts +0 -7
  164. package/dist/utils/mergeAndConcat.js +0 -77
  165. package/dist/utils/normalizeFieldDefinition.d.ts +0 -18
  166. package/dist/utils/normalizeFieldDefinition.js +0 -27
  167. package/dist/utils/processInfo.d.ts +0 -26
  168. package/dist/utils/processInfo.js +0 -41
  169. package/dist/utils/response.d.ts +0 -20
  170. package/dist/utils/response.js +0 -96
  171. package/dist/utils/scanAddons.d.ts +0 -15
  172. package/dist/utils/scanAddons.js +0 -35
  173. package/dist/utils/scanCoreBuiltins.d.ts +0 -3
  174. package/dist/utils/scanCoreBuiltins.js +0 -72
  175. package/dist/utils/scanFiles.d.ts +0 -32
  176. package/dist/utils/scanFiles.js +0 -124
  177. package/dist/utils/scanSources.d.ts +0 -10
  178. package/dist/utils/scanSources.js +0 -46
  179. package/dist/utils/sortModules.d.ts +0 -28
  180. package/dist/utils/sortModules.js +0 -105
  181. package/dist/utils/sqlUtil.d.ts +0 -33
  182. package/dist/utils/sqlUtil.js +0 -146
  183. package/dist/utils/util.d.ts +0 -172
  184. package/dist/utils/util.js +0 -517
@@ -1,91 +0,0 @@
1
- import type { SqlValue, WhereConditions } from "../types/common";
2
- type BuildInsertRowOptions = {
3
- idMode: "timeId";
4
- data: Record<string, SqlValue>;
5
- id: number;
6
- now: number;
7
- } | {
8
- idMode: "autoId";
9
- data: Record<string, SqlValue>;
10
- now: number;
11
- };
12
- export declare class DbUtils {
13
- /**
14
- * 深度剔除 null/undefined(通用工具)。
15
- *
16
- * 说明:
17
- * - 仅处理 object 结构;数组默认原样返回(避免误处理 $in/$between 这类“值数组”)。
18
- * - 你可以通过 options.arrayObjectKeys 指定“哪些 key 的数组是对象列表,需要递归清理”。
19
- * 典型场景:where 的 $or/$and。
20
- * - 通过 options.depth 控制清理深度:
21
- * - 0(默认):递归处理(无限深度)
22
- * - N(正整数):最多处理 N 层(1 表示只处理当前这一层)
23
- * - 注意:数组本身不计入 depth(例如 where 的 $or/$and 是数组;遍历数组元素不会额外消耗层级)
24
- */
25
- static clearDeep(value: any, options?: {
26
- arrayObjectKeys?: string[];
27
- depth?: number;
28
- }): any;
29
- static parseTableRef(tableRef: string): {
30
- schema: string | null;
31
- table: string;
32
- alias: string | null;
33
- };
34
- /**
35
- * 规范化表引用:只 snakeCase schema/table,本身 alias 保持原样。
36
- * - 支持:table / table alias / schema.table / schema.table alias
37
- */
38
- static normalizeTableRef(tableRef: string): string;
39
- /**
40
- * JOIN 场景下主表的限定符:优先使用 alias;没有 alias 时使用 snakeCase(table)。
41
- * 用于构造类似 "o.state$gt" 的 where key,避免出现 "order o.state$gt" 这种带空格的非法 key。
42
- */
43
- static getJoinMainQualifier(tableRef: string): string;
44
- /**
45
- * 字段数组转下划线格式
46
- * 支持排除字段语法:['!password', '!token']
47
- *
48
- * 说明:exclude 模式需要表的所有字段名,因此通过 getTableColumns 回调获取
49
- */
50
- static fieldsToSnake(table: string, fields: string[], getTableColumns: (table: string) => Promise<string[]>): Promise<string[]>;
51
- static validateAndClassifyFields(fields?: string[]): {
52
- type: "all" | "include" | "exclude";
53
- fields: string[];
54
- };
55
- static orderByToSnake(orderBy: string[]): string[];
56
- static processJoinField(field: string): string;
57
- static processJoinWhereKey(key: string): string;
58
- static processJoinWhere(where: any): any;
59
- static processJoinOrderBy(orderBy: string[]): string[];
60
- static addDefaultStateFilter(where?: WhereConditions, table?: string, hasJoins?: boolean): WhereConditions;
61
- /**
62
- * Where 条件键名转下划线格式(递归处理嵌套)
63
- * 支持操作符字段(如 userId$gt)和逻辑操作符($or, $and)
64
- */
65
- static whereKeysToSnake(where: any): any;
66
- /**
67
- * 序列化数组字段(写入数据库前)
68
- * 将数组类型的字段转换为 JSON 字符串
69
- */
70
- static serializeArrayFields(data: Record<string, any>): Record<string, any>;
71
- /**
72
- * 反序列化数组字段(从数据库读取后)
73
- * 将 JSON 字符串转换回数组
74
- */
75
- static deserializeArrayFields<T = any>(data: Record<string, any> | null): T | null;
76
- static cleanAndSnakeAndSerializeWriteData(data: Record<string, any>, excludeValues?: any[]): Record<string, any>;
77
- static stripSystemFieldsForWrite(data: Record<string, any>, options: {
78
- allowState: boolean;
79
- }): Record<string, any>;
80
- static buildInsertRow(options: BuildInsertRowOptions): Record<string, any>;
81
- static buildUpdateRow(options: {
82
- data: Record<string, any>;
83
- now: number;
84
- allowState: boolean;
85
- }): Record<string, any>;
86
- static buildPartialUpdateData(options: {
87
- data: Record<string, any>;
88
- allowState: boolean;
89
- }): Record<string, any>;
90
- }
91
- export {};
@@ -1,544 +0,0 @@
1
- import { fieldClear } from "../utils/fieldClear";
2
- import { keysToSnake, snakeCase } from "../utils/util";
3
- import { SqlCheck } from "./sqlCheck";
4
- export class DbUtils {
5
- /**
6
- * 深度剔除 null/undefined(通用工具)。
7
- *
8
- * 说明:
9
- * - 仅处理 object 结构;数组默认原样返回(避免误处理 $in/$between 这类“值数组”)。
10
- * - 你可以通过 options.arrayObjectKeys 指定“哪些 key 的数组是对象列表,需要递归清理”。
11
- * 典型场景:where 的 $or/$and。
12
- * - 通过 options.depth 控制清理深度:
13
- * - 0(默认):递归处理(无限深度)
14
- * - N(正整数):最多处理 N 层(1 表示只处理当前这一层)
15
- * - 注意:数组本身不计入 depth(例如 where 的 $or/$and 是数组;遍历数组元素不会额外消耗层级)
16
- */
17
- static clearDeep(value, options) {
18
- const arrayObjectKeys = Array.isArray(options?.arrayObjectKeys) ? options.arrayObjectKeys : ["$or", "$and"];
19
- const arrayObjectKeySet = new Set();
20
- for (const key of arrayObjectKeys) {
21
- arrayObjectKeySet.add(key);
22
- }
23
- const depthRaw = typeof options?.depth === "number" && Number.isFinite(options.depth) ? Math.floor(options.depth) : 0;
24
- const depth = depthRaw < 0 ? 0 : depthRaw;
25
- const clearInternal = (input, remainingDepth) => {
26
- if (!input || typeof input !== "object") {
27
- return input;
28
- }
29
- if (Array.isArray(input)) {
30
- return input;
31
- }
32
- const canRecurse = remainingDepth === 0 || remainingDepth > 1;
33
- const childDepth = remainingDepth === 0 ? 0 : remainingDepth - 1;
34
- const result = {};
35
- for (const [key, item] of Object.entries(input)) {
36
- if (item === undefined || item === null) {
37
- continue;
38
- }
39
- if (arrayObjectKeySet.has(key)) {
40
- if (!Array.isArray(item)) {
41
- continue;
42
- }
43
- // 数组不计入 depth:递归进入数组元素时不消耗 remainingDepth
44
- const arrayChildDepth = remainingDepth;
45
- const outList = [];
46
- for (const child of item) {
47
- if (!child || typeof child !== "object" || Array.isArray(child)) {
48
- continue;
49
- }
50
- const cleaned = clearInternal(child, arrayChildDepth);
51
- if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) {
52
- continue;
53
- }
54
- if (Object.keys(cleaned).length === 0) {
55
- continue;
56
- }
57
- outList.push(cleaned);
58
- }
59
- if (outList.length > 0) {
60
- result[key] = outList;
61
- }
62
- continue;
63
- }
64
- if (typeof item === "object" && !Array.isArray(item)) {
65
- if (!canRecurse) {
66
- result[key] = item;
67
- continue;
68
- }
69
- const cleanedObj = clearInternal(item, childDepth);
70
- if (!cleanedObj || typeof cleanedObj !== "object" || Array.isArray(cleanedObj)) {
71
- continue;
72
- }
73
- if (Object.keys(cleanedObj).length === 0) {
74
- continue;
75
- }
76
- result[key] = cleanedObj;
77
- continue;
78
- }
79
- result[key] = item;
80
- }
81
- return result;
82
- };
83
- return clearInternal(value, depth);
84
- }
85
- static parseTableRef(tableRef) {
86
- if (typeof tableRef !== "string") {
87
- throw new Error(`tableRef 必须是字符串 (tableRef: ${String(tableRef)})`);
88
- }
89
- const trimmed = tableRef.trim();
90
- if (!trimmed) {
91
- throw new Error("tableRef 不能为空");
92
- }
93
- const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
94
- if (parts.length === 0) {
95
- throw new Error("tableRef 不能为空");
96
- }
97
- if (parts.length > 2) {
98
- throw new Error(`不支持的表引用格式(包含过多片段)。请使用最简形式:table 或 table alias 或 schema.table 或 schema.table alias (tableRef: ${trimmed})`);
99
- }
100
- const namePart = parts[0];
101
- if (typeof namePart !== "string" || namePart.trim() === "") {
102
- throw new Error(`tableRef 解析失败:缺少表名 (tableRef: ${trimmed})`);
103
- }
104
- let aliasPart = null;
105
- if (parts.length === 2) {
106
- const alias = parts[1];
107
- if (typeof alias !== "string" || alias.trim() === "") {
108
- throw new Error(`tableRef 解析失败:缺少 alias (tableRef: ${trimmed})`);
109
- }
110
- aliasPart = alias;
111
- }
112
- const nameSegments = namePart.split(".");
113
- if (nameSegments.length > 2) {
114
- throw new Error(`不支持的表引用格式(schema 层级过深) (tableRef: ${trimmed})`);
115
- }
116
- if (nameSegments.length === 2) {
117
- const schema = nameSegments[0];
118
- const table = nameSegments[1];
119
- if (typeof schema !== "string" || schema.trim() === "") {
120
- throw new Error(`tableRef 解析失败:schema 为空 (tableRef: ${trimmed})`);
121
- }
122
- if (typeof table !== "string" || table.trim() === "") {
123
- throw new Error(`tableRef 解析失败:table 为空 (tableRef: ${trimmed})`);
124
- }
125
- return { schema: schema, table: table, alias: aliasPart };
126
- }
127
- const table = nameSegments[0];
128
- if (typeof table !== "string" || table.trim() === "") {
129
- throw new Error(`tableRef 解析失败:table 为空 (tableRef: ${trimmed})`);
130
- }
131
- return { schema: null, table: table, alias: aliasPart };
132
- }
133
- /**
134
- * 规范化表引用:只 snakeCase schema/table,本身 alias 保持原样。
135
- * - 支持:table / table alias / schema.table / schema.table alias
136
- */
137
- static normalizeTableRef(tableRef) {
138
- const parsed = DbUtils.parseTableRef(tableRef);
139
- const schemaPart = parsed.schema ? snakeCase(parsed.schema) : null;
140
- const tablePart = snakeCase(parsed.table);
141
- let result = schemaPart ? `${schemaPart}.${tablePart}` : tablePart;
142
- if (parsed.alias) {
143
- result = `${result} ${parsed.alias}`;
144
- }
145
- return result;
146
- }
147
- /**
148
- * JOIN 场景下主表的限定符:优先使用 alias;没有 alias 时使用 snakeCase(table)。
149
- * 用于构造类似 "o.state$gt" 的 where key,避免出现 "order o.state$gt" 这种带空格的非法 key。
150
- */
151
- static getJoinMainQualifier(tableRef) {
152
- const parsed = DbUtils.parseTableRef(tableRef);
153
- if (parsed.alias) {
154
- return parsed.alias;
155
- }
156
- return snakeCase(parsed.table);
157
- }
158
- /**
159
- * 字段数组转下划线格式
160
- * 支持排除字段语法:['!password', '!token']
161
- *
162
- * 说明:exclude 模式需要表的所有字段名,因此通过 getTableColumns 回调获取
163
- */
164
- static async fieldsToSnake(table, fields, getTableColumns) {
165
- if (!fields || !Array.isArray(fields)) {
166
- return ["*"];
167
- }
168
- const classified = DbUtils.validateAndClassifyFields(fields);
169
- // 情况1:查询所有字段
170
- if (classified.type === "all") {
171
- return ["*"];
172
- }
173
- // 情况2:指定包含字段
174
- if (classified.type === "include") {
175
- return classified.fields.map((field) => {
176
- return DbUtils.processJoinField(field);
177
- });
178
- }
179
- // 情况3:排除字段
180
- if (classified.type === "exclude") {
181
- const allColumns = await getTableColumns(table);
182
- const excludeSnakeFields = classified.fields.map((f) => snakeCase(f));
183
- const resultFields = allColumns.filter((col) => !excludeSnakeFields.includes(col));
184
- if (resultFields.length === 0) {
185
- throw new Error(`排除字段后没有剩余字段可查询。表: ${table}, 排除: ${excludeSnakeFields.join(", ")}`);
186
- }
187
- return resultFields;
188
- }
189
- return ["*"];
190
- }
191
- static validateAndClassifyFields(fields) {
192
- // 情况1:空数组或 undefined,表示查询所有
193
- if (!fields || fields.length === 0) {
194
- return { type: "all", fields: [] };
195
- }
196
- // 检测是否有星号(禁止)
197
- if (fields.some((f) => f === "*")) {
198
- throw new Error("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段");
199
- }
200
- // 检测是否有空字符串或无效值
201
- if (fields.some((f) => !f || typeof f !== "string" || f.trim() === "")) {
202
- throw new Error("fields 不能包含空字符串或无效值");
203
- }
204
- // 统一禁止函数/表达式:复杂表达式请使用 selectRaw/whereRaw
205
- for (const rawField of fields) {
206
- const checkField = rawField.startsWith("!") ? rawField.substring(1) : rawField;
207
- SqlCheck.assertNoExprField(checkField);
208
- }
209
- // 统计包含字段和排除字段
210
- const includeFields = fields.filter((f) => !f.startsWith("!"));
211
- const excludeFields = fields.filter((f) => f.startsWith("!"));
212
- // 情况2:全部是包含字段
213
- if (includeFields.length > 0 && excludeFields.length === 0) {
214
- return { type: "include", fields: includeFields };
215
- }
216
- // 情况3:全部是排除字段
217
- if (excludeFields.length > 0 && includeFields.length === 0) {
218
- // 去掉感叹号前缀
219
- const cleanExcludeFields = excludeFields.map((f) => f.substring(1));
220
- return { type: "exclude", fields: cleanExcludeFields };
221
- }
222
- // 混用情况:报错
223
- throw new Error('fields 不能同时包含普通字段和排除字段(! 开头)。只能使用以下3种方式之一:\n1. 空数组 [] 或不传(查询所有)\n2. 全部指定字段 ["id", "name"]\n3. 全部排除字段 ["!password", "!token"]');
224
- }
225
- static orderByToSnake(orderBy) {
226
- if (!orderBy || !Array.isArray(orderBy)) {
227
- return orderBy;
228
- }
229
- return orderBy.map((item) => {
230
- if (typeof item !== "string" || !item.includes("#")) {
231
- return item;
232
- }
233
- const parts = item.split("#");
234
- if (parts.length !== 2) {
235
- return item;
236
- }
237
- const field = parts[0];
238
- const direction = parts[1];
239
- if (typeof field !== "string" || typeof direction !== "string") {
240
- return item;
241
- }
242
- return `${snakeCase(field.trim())}#${direction.trim()}`;
243
- });
244
- }
245
- static processJoinField(field) {
246
- // 统一禁止函数/表达式:复杂表达式请使用 SqlBuilder.selectRaw
247
- SqlCheck.assertNoExprField(field);
248
- // 跳过星号、已引用字段
249
- if (field === "*" || field.startsWith("`")) {
250
- return field;
251
- }
252
- // 处理别名 AS
253
- if (field.toUpperCase().includes(" AS ")) {
254
- const parts = field.split(/\s+AS\s+/i);
255
- const fieldPart = parts[0];
256
- const aliasPart = parts[1];
257
- if (typeof fieldPart !== "string" || typeof aliasPart !== "string") {
258
- return field;
259
- }
260
- return `${DbUtils.processJoinField(fieldPart.trim())} AS ${aliasPart.trim()}`;
261
- }
262
- // 处理表别名.字段名(JOIN 模式下,点号前面通常是别名,不应被 snakeCase 改写)
263
- if (field.includes(".")) {
264
- const parts = field.split(".");
265
- if (parts.length < 2) {
266
- return snakeCase(field);
267
- }
268
- // 防御:避免 a..b / a. / .a 等输入
269
- if (parts.some((p) => p.trim() === "")) {
270
- return field;
271
- }
272
- const fieldName = parts[parts.length - 1];
273
- const tableName = parts.slice(0, parts.length - 1).join(".");
274
- if (typeof fieldName !== "string" || typeof tableName !== "string") {
275
- return snakeCase(field);
276
- }
277
- return `${tableName.trim()}.${snakeCase(fieldName)}`;
278
- }
279
- // 普通字段
280
- return snakeCase(field);
281
- }
282
- static processJoinWhereKey(key) {
283
- // 保留逻辑操作符
284
- if (key === "$or" || key === "$and") {
285
- return key;
286
- }
287
- // 统一禁止函数/表达式:复杂表达式请使用 whereRaw
288
- SqlCheck.assertNoExprField(key);
289
- // 处理带操作符的字段名(如 user.userId$gt)
290
- if (key.includes("$")) {
291
- const lastDollarIndex = key.lastIndexOf("$");
292
- const fieldPart = key.substring(0, lastDollarIndex);
293
- const operator = key.substring(lastDollarIndex);
294
- if (fieldPart.includes(".")) {
295
- const parts = fieldPart.split(".");
296
- if (parts.length < 2) {
297
- return `${snakeCase(fieldPart)}${operator}`;
298
- }
299
- // 防御:避免 a..b / a. / .a 等输入
300
- if (parts.some((p) => p.trim() === "")) {
301
- return `${snakeCase(fieldPart)}${operator}`;
302
- }
303
- const fieldName = parts[parts.length - 1];
304
- const tableName = parts.slice(0, parts.length - 1).join(".");
305
- if (typeof fieldName !== "string" || typeof tableName !== "string") {
306
- return `${snakeCase(fieldPart)}${operator}`;
307
- }
308
- return `${tableName.trim()}.${snakeCase(fieldName)}${operator}`;
309
- }
310
- return `${snakeCase(fieldPart)}${operator}`;
311
- }
312
- // 处理表名.字段名
313
- if (key.includes(".")) {
314
- const parts = key.split(".");
315
- if (parts.length < 2) {
316
- return snakeCase(key);
317
- }
318
- // 防御:避免 a..b / a. / .a 等输入
319
- if (parts.some((p) => p.trim() === "")) {
320
- return snakeCase(key);
321
- }
322
- const fieldName = parts[parts.length - 1];
323
- const tableName = parts.slice(0, parts.length - 1).join(".");
324
- if (typeof fieldName !== "string" || typeof tableName !== "string") {
325
- return snakeCase(key);
326
- }
327
- return `${tableName.trim()}.${snakeCase(fieldName)}`;
328
- }
329
- // 普通字段
330
- return snakeCase(key);
331
- }
332
- static processJoinWhere(where) {
333
- if (!where || typeof where !== "object") {
334
- return where;
335
- }
336
- if (Array.isArray(where)) {
337
- return where.map((item) => DbUtils.processJoinWhere(item));
338
- }
339
- const result = {};
340
- for (const [key, value] of Object.entries(where)) {
341
- const newKey = DbUtils.processJoinWhereKey(key);
342
- if (key === "$or" || key === "$and") {
343
- result[newKey] = Array.isArray(value) ? value.map((item) => DbUtils.processJoinWhere(item)) : [];
344
- }
345
- else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
346
- result[newKey] = DbUtils.processJoinWhere(value);
347
- }
348
- else {
349
- result[newKey] = value;
350
- }
351
- }
352
- return result;
353
- }
354
- static processJoinOrderBy(orderBy) {
355
- if (!orderBy || !Array.isArray(orderBy)) {
356
- return orderBy;
357
- }
358
- return orderBy.map((item) => {
359
- if (typeof item !== "string" || !item.includes("#")) {
360
- return item;
361
- }
362
- const parts = item.split("#");
363
- if (parts.length !== 2) {
364
- return item;
365
- }
366
- const field = parts[0];
367
- const direction = parts[1];
368
- if (typeof field !== "string" || typeof direction !== "string") {
369
- return item;
370
- }
371
- return `${DbUtils.processJoinField(field.trim())}#${direction.trim()}`;
372
- });
373
- }
374
- static addDefaultStateFilter(where = {}, table, hasJoins = false) {
375
- // 如果用户已经指定了 state 条件,优先使用用户的条件
376
- const hasStateCondition = Object.keys(where).some((key) => key.startsWith("state") || key.includes(".state"));
377
- if (hasStateCondition) {
378
- return where;
379
- }
380
- // JOIN 查询时需要指定主表名前缀避免歧义
381
- if (hasJoins && table) {
382
- // table 可能带别名("order o"),这里只需要别名/主表引用本身,不做 snakeCase 改写
383
- const result = {};
384
- for (const [key, value] of Object.entries(where)) {
385
- result[key] = value;
386
- }
387
- result[`${table}.state$gt`] = 0;
388
- return result;
389
- }
390
- // 默认查询 state > 0 的数据
391
- const result = {};
392
- for (const [key, value] of Object.entries(where)) {
393
- result[key] = value;
394
- }
395
- result.state$gt = 0;
396
- return result;
397
- }
398
- /**
399
- * Where 条件键名转下划线格式(递归处理嵌套)
400
- * 支持操作符字段(如 userId$gt)和逻辑操作符($or, $and)
401
- */
402
- static whereKeysToSnake(where) {
403
- if (!where || typeof where !== "object") {
404
- return where;
405
- }
406
- // 处理数组($or, $and 等)
407
- if (Array.isArray(where)) {
408
- return where.map((item) => DbUtils.whereKeysToSnake(item));
409
- }
410
- const result = {};
411
- for (const [key, value] of Object.entries(where)) {
412
- // 保留 $or, $and 等逻辑操作符
413
- if (key === "$or" || key === "$and") {
414
- result[key] = Array.isArray(value) ? value.map((item) => DbUtils.whereKeysToSnake(item)) : [];
415
- continue;
416
- }
417
- // 统一禁止函数/表达式:复杂表达式请使用 whereRaw
418
- SqlCheck.assertNoExprField(key);
419
- // 处理带操作符的字段名(如 userId$gt)
420
- if (key.includes("$")) {
421
- const lastDollarIndex = key.lastIndexOf("$");
422
- const fieldName = key.substring(0, lastDollarIndex);
423
- const operator = key.substring(lastDollarIndex);
424
- const snakeKey = snakeCase(fieldName) + operator;
425
- result[snakeKey] = value;
426
- continue;
427
- }
428
- // 普通字段:转换键名,递归处理值(支持嵌套对象)
429
- const snakeKey = snakeCase(key);
430
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
431
- result[snakeKey] = DbUtils.whereKeysToSnake(value);
432
- }
433
- else {
434
- result[snakeKey] = value;
435
- }
436
- }
437
- return result;
438
- }
439
- /**
440
- * 序列化数组字段(写入数据库前)
441
- * 将数组类型的字段转换为 JSON 字符串
442
- */
443
- static serializeArrayFields(data) {
444
- const serialized = {};
445
- for (const [key, value] of Object.entries(data)) {
446
- if (value === null || value === undefined) {
447
- serialized[key] = value;
448
- continue;
449
- }
450
- if (Array.isArray(value)) {
451
- serialized[key] = JSON.stringify(value);
452
- continue;
453
- }
454
- serialized[key] = value;
455
- }
456
- return serialized;
457
- }
458
- /**
459
- * 反序列化数组字段(从数据库读取后)
460
- * 将 JSON 字符串转换回数组
461
- */
462
- static deserializeArrayFields(data) {
463
- if (!data) {
464
- return null;
465
- }
466
- const deserialized = {};
467
- for (const [key, value] of Object.entries(data)) {
468
- deserialized[key] = value;
469
- }
470
- for (const [key, value] of Object.entries(deserialized)) {
471
- if (typeof value !== "string") {
472
- continue;
473
- }
474
- if (value.startsWith("[") && value.endsWith("]")) {
475
- try {
476
- const parsed = JSON.parse(value);
477
- if (Array.isArray(parsed)) {
478
- deserialized[key] = parsed;
479
- }
480
- }
481
- catch {
482
- // 解析失败则保持原值
483
- }
484
- }
485
- }
486
- return deserialized;
487
- }
488
- static cleanAndSnakeAndSerializeWriteData(data, excludeValues = [null, undefined]) {
489
- const cleanData = fieldClear(data, { excludeValues: excludeValues });
490
- const snakeData = keysToSnake(cleanData);
491
- return DbUtils.serializeArrayFields(snakeData);
492
- }
493
- static stripSystemFieldsForWrite(data, options) {
494
- const result = {};
495
- for (const [key, value] of Object.entries(data)) {
496
- // 系统字段不可由用户覆盖
497
- if (key === "id")
498
- continue;
499
- if (key === "created_at")
500
- continue;
501
- if (key === "updated_at")
502
- continue;
503
- if (key === "deleted_at")
504
- continue;
505
- if (!options.allowState && key === "state")
506
- continue;
507
- result[key] = value;
508
- }
509
- return result;
510
- }
511
- static buildInsertRow(options) {
512
- const serializedData = DbUtils.cleanAndSnakeAndSerializeWriteData(options.data);
513
- const userData = DbUtils.stripSystemFieldsForWrite(serializedData, { allowState: false });
514
- const result = {};
515
- for (const [key, value] of Object.entries(userData)) {
516
- result[key] = value;
517
- }
518
- if (options.idMode === "timeId") {
519
- if (!Number.isFinite(options.id) || options.id <= 0) {
520
- throw new Error(`buildInsertRow(timeId) 失败:id 必须为 > 0 的有限 number (id: ${String(options.id)})`);
521
- }
522
- result["id"] = options.id;
523
- }
524
- // autoId 模式:不写入 id,交给 MySQL AUTO_INCREMENT 生成
525
- result["created_at"] = options.now;
526
- result["updated_at"] = options.now;
527
- result["state"] = 1;
528
- return result;
529
- }
530
- static buildUpdateRow(options) {
531
- const serializedData = DbUtils.cleanAndSnakeAndSerializeWriteData(options.data);
532
- const userData = DbUtils.stripSystemFieldsForWrite(serializedData, { allowState: options.allowState });
533
- const result = {};
534
- for (const [key, value] of Object.entries(userData)) {
535
- result[key] = value;
536
- }
537
- result["updated_at"] = options.now;
538
- return result;
539
- }
540
- static buildPartialUpdateData(options) {
541
- const serializedData = DbUtils.cleanAndSnakeAndSerializeWriteData(options.data);
542
- return DbUtils.stripSystemFieldsForWrite(serializedData, { allowState: options.allowState });
543
- }
544
- }
package/dist/lib/jwt.d.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * JWT 工具类 - 基于 fast-jwt 实现
3
- */
4
- import type { AuthConfig } from "../types/befly";
5
- import type { JwtPayload, JwtSignOptions, JwtVerifyOptions, JwtDecoded } from "../types/jwt";
6
- export declare class Jwt {
7
- private config;
8
- constructor(config?: AuthConfig);
9
- sign(payload: JwtPayload, options?: JwtSignOptions): string;
10
- verify<T = JwtPayload>(token: string, options?: JwtVerifyOptions): T;
11
- decode(token: string, complete?: false): JwtPayload;
12
- decode(token: string, complete: true): JwtDecoded;
13
- }