befly 3.10.18 → 3.11.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.
Files changed (223) hide show
  1. package/README.md +83 -307
  2. package/dist/befly.config.d.ts +7 -0
  3. package/{befly.config.ts → dist/befly.config.js} +11 -36
  4. package/dist/befly.js +15621 -0
  5. package/dist/befly.min.js +21 -0
  6. package/dist/checks/checkApi.d.ts +1 -0
  7. package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
  8. package/dist/checks/checkHook.d.ts +1 -0
  9. package/dist/checks/checkHook.js +86 -0
  10. package/dist/checks/checkMenu.d.ts +7 -0
  11. package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
  12. package/dist/checks/checkPlugin.d.ts +1 -0
  13. package/dist/checks/checkPlugin.js +86 -0
  14. package/dist/checks/checkTable.d.ts +6 -0
  15. package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
  16. package/dist/configs/presetFields.d.ts +4 -0
  17. package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
  18. package/dist/configs/presetRegexp.d.ts +145 -0
  19. package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
  20. package/dist/hooks/auth.d.ts +7 -0
  21. package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
  22. package/dist/hooks/cors.d.ts +11 -0
  23. package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
  24. package/dist/hooks/parser.d.ts +14 -0
  25. package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
  26. package/dist/hooks/permission.d.ts +14 -0
  27. package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
  28. package/dist/hooks/validator.d.ts +11 -0
  29. package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
  30. package/dist/index.d.ts +26 -0
  31. package/{main.ts → dist/index.js} +61 -100
  32. package/dist/lib/asyncContext.d.ts +21 -0
  33. package/dist/lib/asyncContext.js +27 -0
  34. package/dist/lib/cacheHelper.d.ts +95 -0
  35. package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
  36. package/dist/lib/cacheKeys.d.ts +23 -0
  37. package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
  38. package/dist/lib/cipher.d.ts +153 -0
  39. package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
  40. package/dist/lib/connect.d.ts +91 -0
  41. package/{lib/connect.ts → dist/lib/connect.js} +47 -88
  42. package/dist/lib/dbDialect.d.ts +87 -0
  43. package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
  44. package/dist/lib/dbHelper.d.ts +204 -0
  45. package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
  46. package/dist/lib/dbUtils.d.ts +68 -0
  47. package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
  48. package/dist/lib/jwt.d.ts +13 -0
  49. package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
  50. package/dist/lib/logger.d.ts +42 -0
  51. package/dist/lib/logger.js +1144 -0
  52. package/dist/lib/redisHelper.d.ts +185 -0
  53. package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
  54. package/dist/lib/sqlBuilder.d.ts +160 -0
  55. package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
  56. package/dist/lib/sqlCheck.d.ts +23 -0
  57. package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
  58. package/dist/lib/validator.d.ts +45 -0
  59. package/{lib/validator.ts → dist/lib/validator.js} +44 -61
  60. package/dist/loader/loadApis.d.ts +12 -0
  61. package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
  62. package/dist/loader/loadHooks.d.ts +7 -0
  63. package/dist/loader/loadHooks.js +35 -0
  64. package/dist/loader/loadPlugins.d.ts +8 -0
  65. package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
  66. package/dist/paths.d.ts +93 -0
  67. package/{paths.ts → dist/paths.js} +6 -19
  68. package/dist/plugins/cache.d.ts +16 -0
  69. package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
  70. package/dist/plugins/cipher.d.ts +12 -0
  71. package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
  72. package/dist/plugins/config.d.ts +12 -0
  73. package/dist/plugins/config.js +8 -0
  74. package/dist/plugins/db.d.ts +16 -0
  75. package/{plugins/db.ts → dist/plugins/db.js} +11 -17
  76. package/dist/plugins/jwt.d.ts +12 -0
  77. package/dist/plugins/jwt.js +12 -0
  78. package/dist/plugins/logger.d.ts +32 -0
  79. package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
  80. package/dist/plugins/redis.d.ts +16 -0
  81. package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
  82. package/dist/plugins/tool.d.ts +81 -0
  83. package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
  84. package/dist/router/api.d.ts +14 -0
  85. package/dist/router/api.js +107 -0
  86. package/dist/router/static.d.ts +9 -0
  87. package/{router/static.ts → dist/router/static.js} +20 -34
  88. package/dist/scripts/ensureDist.d.ts +1 -0
  89. package/dist/scripts/ensureDist.js +296 -0
  90. package/dist/sync/syncApi.d.ts +3 -0
  91. package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
  92. package/dist/sync/syncCache.d.ts +2 -0
  93. package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
  94. package/dist/sync/syncDev.d.ts +6 -0
  95. package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
  96. package/dist/sync/syncMenu.d.ts +14 -0
  97. package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
  98. package/dist/sync/syncTable.d.ts +151 -0
  99. package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
  100. package/{types → dist/types}/api.d.ts +12 -51
  101. package/dist/types/api.js +4 -0
  102. package/{types → dist/types}/befly.d.ts +32 -227
  103. package/dist/types/befly.js +4 -0
  104. package/{types → dist/types}/cache.d.ts +7 -15
  105. package/dist/types/cache.js +4 -0
  106. package/dist/types/cipher.d.ts +27 -0
  107. package/dist/types/cipher.js +7 -0
  108. package/{types → dist/types}/common.d.ts +8 -33
  109. package/dist/types/common.js +5 -0
  110. package/{types → dist/types}/context.d.ts +3 -5
  111. package/dist/types/context.js +4 -0
  112. package/{types → dist/types}/crypto.d.ts +0 -3
  113. package/dist/types/crypto.js +4 -0
  114. package/dist/types/database.d.ts +138 -0
  115. package/dist/types/database.js +4 -0
  116. package/dist/types/hook.d.ts +17 -0
  117. package/dist/types/hook.js +6 -0
  118. package/dist/types/jwt.d.ts +75 -0
  119. package/dist/types/jwt.js +4 -0
  120. package/dist/types/logger.d.ts +59 -0
  121. package/dist/types/logger.js +6 -0
  122. package/dist/types/plugin.d.ts +16 -0
  123. package/dist/types/plugin.js +6 -0
  124. package/dist/types/redis.d.ts +71 -0
  125. package/dist/types/redis.js +4 -0
  126. package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
  127. package/dist/types/roleApisCache.js +8 -0
  128. package/dist/types/sync.d.ts +92 -0
  129. package/dist/types/sync.js +4 -0
  130. package/dist/types/table.d.ts +34 -0
  131. package/dist/types/table.js +4 -0
  132. package/dist/types/validate.d.ts +67 -0
  133. package/dist/types/validate.js +4 -0
  134. package/dist/utils/calcPerfTime.d.ts +4 -0
  135. package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
  136. package/dist/utils/convertBigIntFields.d.ts +11 -0
  137. package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
  138. package/dist/utils/cors.d.ts +8 -0
  139. package/{utils/cors.ts → dist/utils/cors.js} +1 -3
  140. package/dist/utils/disableMenusGlob.d.ts +13 -0
  141. package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
  142. package/dist/utils/fieldClear.d.ts +11 -0
  143. package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
  144. package/dist/utils/getClientIp.d.ts +6 -0
  145. package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
  146. package/dist/utils/importDefault.d.ts +1 -0
  147. package/dist/utils/importDefault.js +29 -0
  148. package/dist/utils/isDirentDirectory.d.ts +2 -0
  149. package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
  150. package/dist/utils/loadMenuConfigs.d.ts +29 -0
  151. package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
  152. package/dist/utils/mergeAndConcat.d.ts +7 -0
  153. package/dist/utils/mergeAndConcat.js +72 -0
  154. package/dist/utils/processAtSymbol.d.ts +4 -0
  155. package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
  156. package/dist/utils/processInfo.d.ts +24 -0
  157. package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
  158. package/dist/utils/response.d.ts +20 -0
  159. package/{utils/response.ts → dist/utils/response.js} +28 -49
  160. package/dist/utils/scanAddons.d.ts +17 -0
  161. package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
  162. package/dist/utils/scanConfig.d.ts +26 -0
  163. package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
  164. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  165. package/dist/utils/scanCoreBuiltins.js +65 -0
  166. package/dist/utils/scanFiles.d.ts +30 -0
  167. package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
  168. package/dist/utils/scanSources.d.ts +10 -0
  169. package/dist/utils/scanSources.js +46 -0
  170. package/dist/utils/sortModules.d.ts +28 -0
  171. package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
  172. package/dist/utils/util.d.ts +84 -0
  173. package/dist/utils/util.js +262 -0
  174. package/package.json +26 -34
  175. package/.gitignore +0 -0
  176. package/bunfig.toml +0 -3
  177. package/checks/checkHook.ts +0 -48
  178. package/checks/checkPlugin.ts +0 -48
  179. package/configs/presetRegexp.ts +0 -225
  180. package/docs/README.md +0 -98
  181. package/docs/api/api.md +0 -1921
  182. package/docs/guide/examples.md +0 -926
  183. package/docs/guide/quickstart.md +0 -354
  184. package/docs/hooks/auth.md +0 -38
  185. package/docs/hooks/cors.md +0 -28
  186. package/docs/hooks/hook.md +0 -838
  187. package/docs/hooks/parser.md +0 -19
  188. package/docs/hooks/rateLimit.md +0 -47
  189. package/docs/infra/redis.md +0 -628
  190. package/docs/plugins/cipher.md +0 -61
  191. package/docs/plugins/database.md +0 -189
  192. package/docs/plugins/plugin.md +0 -986
  193. package/docs/reference/addon.md +0 -510
  194. package/docs/reference/config.md +0 -573
  195. package/docs/reference/logger.md +0 -495
  196. package/docs/reference/sync.md +0 -478
  197. package/docs/reference/table.md +0 -763
  198. package/docs/reference/validator.md +0 -620
  199. package/lib/asyncContext.ts +0 -43
  200. package/lib/logger.ts +0 -811
  201. package/loader/loadHooks.ts +0 -51
  202. package/plugins/config.ts +0 -13
  203. package/plugins/jwt.ts +0 -15
  204. package/router/api.ts +0 -130
  205. package/tsconfig.json +0 -8
  206. package/types/database.d.ts +0 -541
  207. package/types/hook.d.ts +0 -25
  208. package/types/jwt.d.ts +0 -118
  209. package/types/logger.d.ts +0 -65
  210. package/types/plugin.d.ts +0 -19
  211. package/types/redis.d.ts +0 -83
  212. package/types/sync.d.ts +0 -398
  213. package/types/table.d.ts +0 -216
  214. package/types/validate.d.ts +0 -69
  215. package/utils/arrayKeysToCamel.ts +0 -18
  216. package/utils/configTypes.ts +0 -3
  217. package/utils/genShortId.ts +0 -12
  218. package/utils/importDefault.ts +0 -21
  219. package/utils/keysToCamel.ts +0 -22
  220. package/utils/keysToSnake.ts +0 -22
  221. package/utils/pickFields.ts +0 -19
  222. package/utils/scanSources.ts +0 -64
  223. package/utils/sqlLog.ts +0 -37
@@ -1,45 +1,26 @@
1
- export type DbDialectName = "mysql" | "postgresql" | "sqlite";
2
-
3
- let DIALECT_CACHE: Map<DbDialectName, DbDialect> | null = null;
4
-
1
+ let DIALECT_CACHE = null;
5
2
  /**
6
3
  * 获取方言实例(内部缓存,避免到处 new)。
7
4
  *
8
5
  * 约束:dialect 实例应当是纯逻辑对象(无连接/无 IO/无状态副作用),可全局复用。
9
6
  */
10
- export function getDialectByName(name: DbDialectName): DbDialect {
7
+ export function getDialectByName(name) {
11
8
  const unknownDialectError = new Error(`未知数据库方言: ${String(name)}`);
12
9
  if (name !== "mysql" && name !== "postgresql" && name !== "sqlite") {
13
10
  throw unknownDialectError;
14
11
  }
15
-
16
12
  if (!DIALECT_CACHE) {
17
- DIALECT_CACHE = new Map<DbDialectName, DbDialect>();
13
+ DIALECT_CACHE = new Map();
18
14
  DIALECT_CACHE.set("mysql", new MySqlDialect());
19
15
  DIALECT_CACHE.set("postgresql", new PostgresDialect());
20
16
  DIALECT_CACHE.set("sqlite", new SqliteDialect());
21
17
  }
22
-
23
18
  const dialect = DIALECT_CACHE.get(name);
24
19
  if (!dialect) {
25
20
  throw unknownDialectError;
26
21
  }
27
-
28
22
  return dialect;
29
23
  }
30
-
31
- export type SqlTextQuery = {
32
- sql: string;
33
- params: unknown[];
34
- };
35
-
36
- export type SyncTableColumnsInfoQuery = {
37
- columns: SqlTextQuery;
38
- comments?: SqlTextQuery;
39
- };
40
-
41
- export type SyncTableIndexesQuery = SqlTextQuery;
42
-
43
24
  /**
44
25
  * syncTable 专用:获取“列元信息”查询。
45
26
  *
@@ -47,35 +28,30 @@ export type SyncTableIndexesQuery = SqlTextQuery;
47
28
  * - 这里仅负责 SQL + 参数构造(方言差异);
48
29
  * - 解析为 ColumnInfo 的逻辑仍放在 syncTable.ts(保持同步算法聚合)。
49
30
  */
50
- export function getSyncTableColumnsInfoQuery(options: { dialect: DbDialectName; table: string; dbName: string; schema?: string }): SyncTableColumnsInfoQuery {
31
+ export function getSyncTableColumnsInfoQuery(options) {
51
32
  if (options.dialect === "mysql") {
52
- const columns: SqlTextQuery = {
33
+ const columns = {
53
34
  sql: "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION",
54
35
  params: [options.dbName, options.table]
55
36
  };
56
37
  return { columns: columns };
57
38
  }
58
-
59
39
  if (options.dialect === "postgresql") {
60
40
  const schema = options.schema && options.schema.trim() !== "" ? options.schema : "public";
61
- const columns: SqlTextQuery = {
41
+ const columns = {
62
42
  sql: "SELECT column_name, data_type, character_maximum_length, is_nullable, column_default FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position",
63
43
  params: [schema, options.table]
64
44
  };
65
-
66
- const comments: SqlTextQuery = {
45
+ const comments = {
67
46
  sql: "SELECT a.attname AS column_name, col_description(c.oid, a.attnum) AS column_comment FROM pg_class c JOIN pg_attribute a ON a.attrelid = c.oid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'r' AND n.nspname = ? AND c.relname = ? AND a.attnum > 0",
68
47
  params: [schema, options.table]
69
48
  };
70
-
71
49
  return { columns: columns, comments: comments };
72
50
  }
73
-
74
51
  const sqlite = getDialectByName("sqlite");
75
52
  const columns = sqlite.getTableColumnsQuery(options.table);
76
53
  return { columns: columns };
77
54
  }
78
-
79
55
  /**
80
56
  * syncTable 专用:获取“索引元信息”查询(只负责 SQL + 参数构造)。
81
57
  *
@@ -83,14 +59,13 @@ export function getSyncTableColumnsInfoQuery(options: { dialect: DbDialectName;
83
59
  * - 仅下沉 MySQL / PostgreSQL;SQLite 仍走 PRAGMA(需要多次查询)。
84
60
  * - 解析(比如 PG indexdef 解析列名)仍留在 syncTable.ts。
85
61
  */
86
- export function getSyncTableIndexesQuery(options: { dialect: DbDialectName; table: string; dbName: string; schema?: string }): SyncTableIndexesQuery {
62
+ export function getSyncTableIndexesQuery(options) {
87
63
  if (options.dialect === "mysql") {
88
64
  return {
89
65
  sql: "SELECT INDEX_NAME, COLUMN_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND INDEX_NAME != 'PRIMARY' ORDER BY INDEX_NAME",
90
66
  params: [options.dbName, options.table]
91
67
  };
92
68
  }
93
-
94
69
  if (options.dialect === "postgresql") {
95
70
  const schema = options.schema && options.schema.trim() !== "" ? options.schema : "public";
96
71
  return {
@@ -98,76 +73,39 @@ export function getSyncTableIndexesQuery(options: { dialect: DbDialectName; tabl
98
73
  params: [schema, options.table]
99
74
  };
100
75
  }
101
-
102
76
  throw new Error(`getSyncTableIndexesQuery 不支持方言: ${String(options.dialect)}`);
103
77
  }
104
-
105
- export interface DbDialect {
106
- name: DbDialectName;
107
-
108
- /**
109
- * 转义/引用 SQL 标识符(表名/字段名)。
110
- * 注意:仅用于单个标识符片段,不支持包含空格、点号、函数等复杂表达式。
111
- */
112
- quoteIdent(identifier: string): string;
113
-
114
- /**
115
- * 获取表字段列表的查询。
116
- * 约定:返回结果应能通过 getTableColumnsFromResult 提取列名。
117
- */
118
- getTableColumnsQuery(table: string, schema?: string): SqlTextQuery;
119
-
120
- /** 从 getTableColumnsQuery 的结果中提取列名数组 */
121
- getTableColumnsFromResult(result: any): string[];
122
-
123
- /** 检查表是否存在的查询 */
124
- tableExistsQuery(table: string, schema?: string): SqlTextQuery;
125
-
126
- /**
127
- * 是否支持 schema.table 形式(仅用于校验;具体 quoting 仍由 SqlBuilder 处理)。
128
- */
129
- supportsSchema: boolean;
130
- }
131
-
132
- export class MySqlDialect implements DbDialect {
133
- name: DbDialectName = "mysql";
134
- supportsSchema: boolean = true;
135
-
136
- quoteIdent(identifier: string): string {
78
+ export class MySqlDialect {
79
+ name = "mysql";
80
+ supportsSchema = true;
81
+ quoteIdent(identifier) {
137
82
  if (typeof identifier !== "string") {
138
83
  throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`);
139
84
  }
140
-
141
85
  const trimmed = identifier.trim();
142
86
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
143
87
  throw new Error(`无效的 SQL 标识符: ${trimmed}`);
144
88
  }
145
-
146
89
  return `\`${trimmed}\``;
147
90
  }
148
-
149
- getTableColumnsQuery(table: string, _schema?: string): SqlTextQuery {
91
+ getTableColumnsQuery(table, _schema) {
150
92
  const quotedTable = this.quoteIdent(table);
151
93
  return { sql: `SHOW COLUMNS FROM ${quotedTable}`, params: [] };
152
94
  }
153
-
154
- getTableColumnsFromResult(result: any): string[] {
95
+ getTableColumnsFromResult(result) {
155
96
  if (!Array.isArray(result)) {
156
97
  return [];
157
98
  }
158
-
159
- const columnNames: string[] = [];
99
+ const columnNames = [];
160
100
  for (const row of result) {
161
101
  const name = row?.Field;
162
102
  if (typeof name === "string" && name.length > 0) {
163
103
  columnNames.push(name);
164
104
  }
165
105
  }
166
-
167
106
  return columnNames;
168
107
  }
169
-
170
- tableExistsQuery(table: string, schema?: string): SqlTextQuery {
108
+ tableExistsQuery(table, schema) {
171
109
  if (typeof schema === "string" && schema.trim() !== "") {
172
110
  return {
173
111
  sql: "SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
@@ -177,26 +115,21 @@ export class MySqlDialect implements DbDialect {
177
115
  return { sql: "SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", params: [table] };
178
116
  }
179
117
  }
180
-
181
- export class PostgresDialect implements DbDialect {
182
- name: DbDialectName = "postgresql";
183
- supportsSchema: boolean = true;
184
-
185
- quoteIdent(identifier: string): string {
118
+ export class PostgresDialect {
119
+ name = "postgresql";
120
+ supportsSchema = true;
121
+ quoteIdent(identifier) {
186
122
  if (typeof identifier !== "string") {
187
123
  throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`);
188
124
  }
189
-
190
125
  const trimmed = identifier.trim();
191
126
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
192
127
  throw new Error(`无效的 SQL 标识符: ${trimmed}`);
193
128
  }
194
-
195
129
  // PostgreSQL 使用双引号引用标识符
196
130
  return `"${trimmed}"`;
197
131
  }
198
-
199
- getTableColumnsQuery(table: string, schema?: string): SqlTextQuery {
132
+ getTableColumnsQuery(table, schema) {
200
133
  if (typeof schema === "string" && schema.trim() !== "") {
201
134
  return {
202
135
  sql: "SELECT column_name FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position",
@@ -205,24 +138,20 @@ export class PostgresDialect implements DbDialect {
205
138
  }
206
139
  return { sql: "SELECT column_name FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = ? ORDER BY ordinal_position", params: [table] };
207
140
  }
208
-
209
- getTableColumnsFromResult(result: any): string[] {
141
+ getTableColumnsFromResult(result) {
210
142
  if (!Array.isArray(result)) {
211
143
  return [];
212
144
  }
213
-
214
- const columnNames: string[] = [];
145
+ const columnNames = [];
215
146
  for (const row of result) {
216
147
  const name = row?.column_name;
217
148
  if (typeof name === "string" && name.length > 0) {
218
149
  columnNames.push(name);
219
150
  }
220
151
  }
221
-
222
152
  return columnNames;
223
153
  }
224
-
225
- tableExistsQuery(table: string, schema?: string): SqlTextQuery {
154
+ tableExistsQuery(table, schema) {
226
155
  if (typeof schema === "string" && schema.trim() !== "") {
227
156
  return {
228
157
  sql: "SELECT COUNT(*)::int as count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
@@ -232,51 +161,42 @@ export class PostgresDialect implements DbDialect {
232
161
  return { sql: "SELECT COUNT(*)::int as count FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = ?", params: [table] };
233
162
  }
234
163
  }
235
-
236
- export class SqliteDialect implements DbDialect {
237
- name: DbDialectName = "sqlite";
164
+ export class SqliteDialect {
165
+ name = "sqlite";
238
166
  // SQLite 的“schema”语义与 MySQL/PG 不同:常规使用下没有独立 schema;
239
167
  // 虽然存在 main/temp/attached db 的 database_name.table_name 形式,但这里
240
168
  // 统一视为不支持传统 schema.table 校验语义。
241
- supportsSchema: boolean = false;
242
-
243
- quoteIdent(identifier: string): string {
169
+ supportsSchema = false;
170
+ quoteIdent(identifier) {
244
171
  if (typeof identifier !== "string") {
245
172
  throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`);
246
173
  }
247
-
248
174
  const trimmed = identifier.trim();
249
175
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
250
176
  throw new Error(`无效的 SQL 标识符: ${trimmed}`);
251
177
  }
252
-
253
178
  // SQLite 也支持双引号引用标识符
254
179
  return `"${trimmed}"`;
255
180
  }
256
-
257
- getTableColumnsQuery(table: string, _schema?: string): SqlTextQuery {
181
+ getTableColumnsQuery(table, _schema) {
258
182
  // PRAGMA 不支持参数占位符;此处通过 quoteIdent 限制输入并安全拼接
259
183
  const quotedTable = this.quoteIdent(table);
260
184
  return { sql: `PRAGMA table_info(${quotedTable})`, params: [] };
261
185
  }
262
-
263
- getTableColumnsFromResult(result: any): string[] {
186
+ getTableColumnsFromResult(result) {
264
187
  if (!Array.isArray(result)) {
265
188
  return [];
266
189
  }
267
-
268
- const columnNames: string[] = [];
190
+ const columnNames = [];
269
191
  for (const row of result) {
270
192
  const name = row?.name;
271
193
  if (typeof name === "string" && name.length > 0) {
272
194
  columnNames.push(name);
273
195
  }
274
196
  }
275
-
276
197
  return columnNames;
277
198
  }
278
-
279
- tableExistsQuery(table: string, _schema?: string): SqlTextQuery {
199
+ tableExistsQuery(table, _schema) {
280
200
  return {
281
201
  sql: "SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name = ?",
282
202
  params: [table]
@@ -0,0 +1,204 @@
1
+ /**
2
+ * 数据库助手 - TypeScript 版本
3
+ * 提供数据库 CRUD 操作的封装
4
+ */
5
+ import type { WhereConditions } from "../types/common";
6
+ import type { QueryOptions, InsertOptions, UpdateOptions, DeleteOptions, ListResult, AllResult, TransactionCallback, DbResult, ListSql } from "../types/database";
7
+ import type { DbDialect } from "./dbDialect";
8
+ type RedisCacheLike = {
9
+ getObject<T = any>(key: string): Promise<T | null>;
10
+ setObject<T = any>(key: string, obj: T, ttl?: number | null): Promise<string | null>;
11
+ genTimeID(): Promise<number>;
12
+ };
13
+ /**
14
+ * 数据库助手类
15
+ */
16
+ export declare class DbHelper {
17
+ private redis;
18
+ private dialect;
19
+ private sql;
20
+ private isTransaction;
21
+ /**
22
+ * 构造函数
23
+ * @param redis - Redis 实例
24
+ * @param sql - Bun SQL 客户端(可选,用于事务)
25
+ */
26
+ constructor(options: {
27
+ redis: RedisCacheLike;
28
+ sql?: any | null;
29
+ dialect?: DbDialect;
30
+ });
31
+ private createSqlBuilder;
32
+ private getTableColumns;
33
+ private prepareQueryOptions;
34
+ /**
35
+ * 为 builder 添加 JOIN
36
+ */
37
+ private applyJoins;
38
+ private executeWithConn;
39
+ /**
40
+ * 执行原生 SQL(内部工具/同步脚本专用)
41
+ *
42
+ * - 复用当前 DbHelper 持有的连接/事务
43
+ * - 统一走 executeWithConn,保持参数校验与错误行为一致
44
+ */
45
+ unsafe(sqlStr: string, params?: any[]): Promise<DbResult<any>>;
46
+ /**
47
+ * 检查表是否存在
48
+ * @param tableName - 表名(支持小驼峰,会自动转换为下划线)
49
+ * @returns 表是否存在
50
+ */
51
+ tableExists(tableName: string): Promise<DbResult<boolean>>;
52
+ /**
53
+ * 查询记录数
54
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
55
+ * @param options.where - 查询条件
56
+ * @param options.joins - 多表联查选项
57
+ * @example
58
+ * // 查询总数
59
+ * const count = await db.getCount({ table: 'user' });
60
+ * // 查询指定条件的记录数
61
+ * const activeCount = await db.getCount({ table: 'user', where: { state: 1 } });
62
+ * // 联查计数
63
+ * const count = await db.getCount({
64
+ * table: 'order o',
65
+ * joins: [{ table: 'user u', on: 'o.user_id = u.id' }],
66
+ * where: { 'o.state': 1 }
67
+ * });
68
+ */
69
+ getCount(options: Omit<QueryOptions, "fields" | "page" | "limit" | "orderBy">): Promise<DbResult<number>>;
70
+ /**
71
+ * 查询单条数据
72
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换;联查时可带别名如 'order o')
73
+ * @param options.fields - 字段列表(联查时需带表别名,如 'o.id', 'u.username')
74
+ * @param options.joins - 多表联查选项
75
+ * @example
76
+ * // 单表查询
77
+ * getOne({ table: 'userProfile', fields: ['userId', 'userName'] })
78
+ * // 联查
79
+ * getOne({
80
+ * table: 'order o',
81
+ * joins: [{ table: 'user u', on: 'o.user_id = u.id' }],
82
+ * fields: ['o.id', 'o.totalAmount', 'u.username'],
83
+ * where: { 'o.id': 1 }
84
+ * })
85
+ */
86
+ getOne<T extends Record<string, any> = Record<string, any>>(options: QueryOptions): Promise<DbResult<T | null>>;
87
+ /**
88
+ * 查询列表(带分页)
89
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换;联查时可带别名)
90
+ * @param options.fields - 字段列表(联查时需带表别名)
91
+ * @param options.joins - 多表联查选项
92
+ * @example
93
+ * // 单表分页
94
+ * getList({ table: 'userProfile', fields: ['userId', 'userName', 'createdAt'] })
95
+ * // 联查分页
96
+ * getList({
97
+ * table: 'order o',
98
+ * joins: [
99
+ * { table: 'user u', on: 'o.user_id = u.id' },
100
+ * { table: 'product p', on: 'o.product_id = p.id' }
101
+ * ],
102
+ * fields: ['o.id', 'o.totalAmount', 'u.username', 'p.name AS productName'],
103
+ * where: { 'o.status': 'paid' },
104
+ * orderBy: ['o.createdAt#DESC'],
105
+ * page: 1,
106
+ * limit: 10
107
+ * })
108
+ */
109
+ getList<T extends Record<string, any> = Record<string, any>>(options: QueryOptions): Promise<DbResult<ListResult<T>, ListSql>>;
110
+ /**
111
+ * 查询所有数据(不分页,有上限保护)
112
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换;联查时可带别名)
113
+ * @param options.fields - 字段列表(联查时需带表别名)
114
+ * @param options.joins - 多表联查选项
115
+ * ⚠️ 警告:此方法会查询大量数据,建议使用 getList 分页查询
116
+ * @example
117
+ * // 单表查询
118
+ * getAll({ table: 'userProfile', fields: ['userId', 'userName'] })
119
+ * // 联查
120
+ * getAll({
121
+ * table: 'order o',
122
+ * joins: [{ table: 'user u', on: 'o.user_id = u.id' }],
123
+ * fields: ['o.id', 'u.username'],
124
+ * where: { 'o.state': 1 }
125
+ * })
126
+ */
127
+ getAll<T extends Record<string, any> = Record<string, any>>(options: Omit<QueryOptions, "page" | "limit">): Promise<DbResult<AllResult<T>, ListSql>>;
128
+ /**
129
+ * 插入数据(自动生成 ID、时间戳、state)
130
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
131
+ */
132
+ insData(options: InsertOptions): Promise<DbResult<number>>;
133
+ /**
134
+ * 批量插入数据(真正的批量操作)
135
+ * 使用 INSERT INTO ... VALUES (...), (...), (...) 语法
136
+ * 自动生成系统字段并包装在事务中
137
+ * @param table - 表名(支持小驼峰或下划线格式,会自动转换)
138
+ */
139
+ insBatch(table: string, dataList: Record<string, any>[]): Promise<DbResult<number[]>>;
140
+ delForceBatch(table: string, ids: number[]): Promise<DbResult<number>>;
141
+ updBatch(table: string, dataList: Array<{
142
+ id: number;
143
+ data: Record<string, any>;
144
+ }>): Promise<DbResult<number>>;
145
+ /**
146
+ * 更新数据(强制更新时间戳,系统字段不可修改)
147
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
148
+ */
149
+ updData(options: UpdateOptions): Promise<DbResult<number>>;
150
+ /**
151
+ * 软删除数据(deleted_at 设置为当前时间,state 设置为 0)
152
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
153
+ */
154
+ delData(options: DeleteOptions): Promise<DbResult<number>>;
155
+ /**
156
+ * 硬删除数据(物理删除,不可恢复)
157
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
158
+ */
159
+ delForce(options: Omit<DeleteOptions, "hard">): Promise<DbResult<number>>;
160
+ /**
161
+ * 禁用数据(设置 state=2)
162
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
163
+ */
164
+ disableData(options: Omit<DeleteOptions, "hard">): Promise<DbResult<number>>;
165
+ /**
166
+ * 启用数据(设置 state=1)
167
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
168
+ */
169
+ enableData(options: Omit<DeleteOptions, "hard">): Promise<DbResult<number>>;
170
+ /**
171
+ * 执行事务
172
+ * 使用 Bun SQL 的 begin 方法开启事务
173
+ */
174
+ trans<T = any>(callback: TransactionCallback<T>): Promise<T>;
175
+ /**
176
+ * 执行原始 SQL
177
+ */
178
+ query(sql: string, params?: any[]): Promise<DbResult<any>>;
179
+ /**
180
+ * 检查数据是否存在(优化性能)
181
+ * @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
182
+ */
183
+ exists(options: Omit<QueryOptions, "fields" | "orderBy" | "page" | "limit">): Promise<DbResult<boolean>>;
184
+ /**
185
+ * 查询单个字段值(带字段名验证)
186
+ * @param field - 字段名(支持小驼峰或下划线格式)
187
+ */
188
+ getFieldValue<T = any>(options: Omit<QueryOptions, "fields"> & {
189
+ field: string;
190
+ }): Promise<DbResult<T | null>>;
191
+ /**
192
+ * 自增字段(安全实现,防止 SQL 注入)
193
+ * @param table - 表名(支持小驼峰或下划线格式,会自动转换)
194
+ * @param field - 字段名(支持小驼峰或下划线格式,会自动转换)
195
+ */
196
+ increment(table: string, field: string, where: WhereConditions, value?: number): Promise<DbResult<number>>;
197
+ /**
198
+ * 自减字段
199
+ * @param table - 表名(支持小驼峰或下划线格式,会自动转换)
200
+ * @param field - 字段名(支持小驼峰或下划线格式,会自动转换)
201
+ */
202
+ decrement(table: string, field: string, where: WhereConditions, value?: number): Promise<DbResult<number>>;
203
+ }
204
+ export {};