befly 3.0.0 → 3.0.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 (60) hide show
  1. package/checks/conflict.ts +35 -114
  2. package/checks/table.ts +31 -63
  3. package/config/env.ts +3 -3
  4. package/config/fields.ts +55 -0
  5. package/config/regexAliases.ts +51 -0
  6. package/config/reserved.ts +1 -1
  7. package/main.ts +17 -71
  8. package/package.json +7 -28
  9. package/plugins/db.ts +11 -10
  10. package/plugins/redis.ts +5 -9
  11. package/scripts/syncDb/apply.ts +3 -3
  12. package/scripts/syncDb/constants.ts +2 -1
  13. package/scripts/syncDb/ddl.ts +15 -8
  14. package/scripts/syncDb/helpers.ts +3 -2
  15. package/scripts/syncDb/index.ts +23 -35
  16. package/scripts/syncDb/state.ts +8 -6
  17. package/scripts/syncDb/table.ts +32 -22
  18. package/scripts/syncDb/tableCreate.ts +9 -3
  19. package/scripts/syncDb/tests/constants.test.ts +2 -1
  20. package/scripts/syncDb.ts +10 -9
  21. package/types/addon.d.ts +53 -0
  22. package/types/api.d.ts +17 -14
  23. package/types/befly.d.ts +2 -6
  24. package/types/context.d.ts +7 -0
  25. package/types/database.d.ts +9 -14
  26. package/types/index.d.ts +442 -8
  27. package/types/index.ts +35 -56
  28. package/types/redis.d.ts +2 -0
  29. package/types/validator.d.ts +0 -2
  30. package/types/validator.ts +43 -0
  31. package/utils/colors.ts +117 -37
  32. package/utils/database.ts +348 -0
  33. package/utils/dbHelper.ts +687 -116
  34. package/utils/helper.ts +812 -0
  35. package/utils/index.ts +10 -23
  36. package/utils/logger.ts +78 -171
  37. package/utils/redisHelper.ts +135 -152
  38. package/{types/context.ts → utils/requestContext.ts} +3 -3
  39. package/utils/sqlBuilder.ts +142 -165
  40. package/utils/validate.ts +51 -9
  41. package/apis/health/info.ts +0 -64
  42. package/apis/tool/tokenCheck.ts +0 -51
  43. package/bin/befly.ts +0 -202
  44. package/bunfig.toml +0 -3
  45. package/plugins/tool.ts +0 -34
  46. package/scripts/syncDev.ts +0 -112
  47. package/system.ts +0 -149
  48. package/tables/_common.json +0 -21
  49. package/tables/admin.json +0 -10
  50. package/utils/addonHelper.ts +0 -60
  51. package/utils/api.ts +0 -23
  52. package/utils/datetime.ts +0 -51
  53. package/utils/errorHandler.ts +0 -68
  54. package/utils/objectHelper.ts +0 -68
  55. package/utils/pluginHelper.ts +0 -62
  56. package/utils/response.ts +0 -38
  57. package/utils/sqlHelper.ts +0 -447
  58. package/utils/tableHelper.ts +0 -167
  59. package/utils/tool.ts +0 -230
  60. package/utils/typeHelper.ts +0 -101
@@ -1,167 +0,0 @@
1
- /**
2
- * Befly 表定义工具
3
- * 提供表字段规则解析功能
4
- */
5
-
6
- import path from 'node:path';
7
- import { existsSync, readFileSync } from 'node:fs';
8
- import { getProjectDir } from '../system.js';
9
- import { getAddonDir } from './addonHelper.js';
10
- import type { ParsedFieldRule } from '../types/common.js';
11
-
12
- /**
13
- * 解析字段规则字符串(以 | 分隔)
14
- * 用于解析表定义中的字段规则
15
- *
16
- * 规则格式:字段名|类型|最小值|最大值|默认值|索引|正则
17
- * 注意:只分割前6个|,第7个|之后的所有内容(包括|)都属于正则表达式
18
- *
19
- * @param rule - 字段规则字符串
20
- * @returns 解析后的字段规则对象
21
- *
22
- * @example
23
- * parseRule('用户名|string|2|50|null|1|null')
24
- * // {
25
- * // name: '用户名',
26
- * // type: 'string',
27
- * // min: 2,
28
- * // max: 50,
29
- * // default: 'null',
30
- * // index: 1,
31
- * // regex: null
32
- * // }
33
- *
34
- * parseRule('年龄|number|0|150|18|0|null')
35
- * // {
36
- * // name: '年龄',
37
- * // type: 'number',
38
- * // min: 0,
39
- * // max: 150,
40
- * // default: 18,
41
- * // index: 0,
42
- * // regex: null
43
- * // }
44
- *
45
- * parseRule('状态|string|1|20|active|1|^(active|inactive|pending)$')
46
- * // 正则表达式中的 | 会被保留
47
- */
48
- export const parseRule = (rule: string): ParsedFieldRule => {
49
- // 手动分割前6个|,剩余部分作为正则表达式
50
- // 这样可以确保正则表达式中的|不会被分割
51
- const parts: string[] = [];
52
- let currentPart = '';
53
- let pipeCount = 0;
54
-
55
- for (let i = 0; i < rule.length; i++) {
56
- if (rule[i] === '|' && pipeCount < 6) {
57
- parts.push(currentPart);
58
- currentPart = '';
59
- pipeCount++;
60
- } else {
61
- currentPart += rule[i];
62
- }
63
- }
64
- // 添加最后一部分(正则表达式)
65
- parts.push(currentPart);
66
-
67
- const [fieldName = '', fieldType = 'string', fieldMinStr = 'null', fieldMaxStr = 'null', fieldDefaultStr = 'null', fieldIndexStr = '0', fieldRegx = 'null'] = parts;
68
-
69
- const fieldIndex = Number(fieldIndexStr) as 0 | 1;
70
- const fieldMin = fieldMinStr !== 'null' ? Number(fieldMinStr) : null;
71
- const fieldMax = fieldMaxStr !== 'null' ? Number(fieldMaxStr) : null;
72
-
73
- let fieldDefault: any = fieldDefaultStr;
74
- if (fieldType === 'number' && fieldDefaultStr !== 'null') {
75
- fieldDefault = Number(fieldDefaultStr);
76
- }
77
-
78
- return {
79
- name: fieldName,
80
- type: fieldType as 'string' | 'number' | 'text' | 'array',
81
- min: fieldMin,
82
- max: fieldMax,
83
- default: fieldDefault,
84
- index: fieldIndex,
85
- regex: fieldRegx !== 'null' ? fieldRegx : null
86
- };
87
- };
88
-
89
- /**
90
- * 从表定义中加载字段
91
- * @param tableName - 表名
92
- * @param options - 可选配置
93
- * @returns 字段定义对象
94
- */
95
- export function loadTableFields(
96
- tableName: string,
97
- options?: {
98
- source?: 'core' | 'project' | 'addon';
99
- addonName?: string;
100
- }
101
- ): Record<string, string> {
102
- const source = options?.source || 'project';
103
- const addonName = options?.addonName || '';
104
-
105
- let tableFilePath: string;
106
-
107
- // 根据来源确定表定义文件路径
108
- if (source === 'core') {
109
- tableFilePath = path.join(import.meta.dir, '../tables', `${tableName}.json`);
110
- } else if (source === 'addon') {
111
- if (!addonName) {
112
- throw new Error('addon 来源必须提供 addonName 参数');
113
- }
114
- tableFilePath = path.join(getAddonDir(addonName, 'tables'), `${tableName}.json`);
115
- } else {
116
- tableFilePath = path.join(getProjectDir('tables'), `${tableName}.json`);
117
- }
118
-
119
- // 检查文件是否存在
120
- if (!existsSync(tableFilePath)) {
121
- throw new Error(`表定义文件不存在: ${tableFilePath}`);
122
- }
123
-
124
- try {
125
- // 读取并解析 JSON 文件
126
- const fileContent = readFileSync(tableFilePath, 'utf-8');
127
- const fields = JSON.parse(fileContent);
128
-
129
- // 验证是否为对象
130
- if (typeof fields !== 'object' || fields === null || Array.isArray(fields)) {
131
- throw new Error(`表定义文件格式错误: ${tableFilePath}`);
132
- }
133
-
134
- return fields;
135
- } catch (error: any) {
136
- throw new Error(`加载表定义失败 (${tableName}): ${error.message}`);
137
- }
138
- }
139
-
140
- /**
141
- * 从表定义中选择部分字段
142
- * @param tableName - 表名
143
- * @param fieldNames - 要选择的字段名数组
144
- * @param options - 可选配置
145
- * @returns 选择的字段定义对象
146
- */
147
- export function pickTableFields(
148
- tableName: string,
149
- fieldNames: string[],
150
- options?: {
151
- source?: 'core' | 'project' | 'addon';
152
- addonName?: string;
153
- }
154
- ): Record<string, string> {
155
- const allFields = loadTableFields(tableName, options);
156
- const pickedFields: Record<string, string> = {};
157
-
158
- for (const fieldName of fieldNames) {
159
- if (fieldName in allFields) {
160
- pickedFields[fieldName] = allFields[fieldName];
161
- } else {
162
- throw new Error(`表 ${tableName} 中不存在字段: ${fieldName}`);
163
- }
164
- }
165
-
166
- return pickedFields;
167
- }
package/utils/tool.ts DELETED
@@ -1,230 +0,0 @@
1
- /**
2
- * Tool 工具类 - TypeScript 版本
3
- * 提供数据处理的便捷方法
4
- */
5
-
6
- import { omitFields } from './index.js';
7
- import type { BeflyContext } from '../types/befly.js';
8
-
9
- /**
10
- * 数据对象类型
11
- */
12
- type DataObject = Record<string, any>;
13
-
14
- /**
15
- * 工具类:通过构造函数注入 befly
16
- */
17
- export class Tool {
18
- private befly: BeflyContext;
19
-
20
- /**
21
- * 构造函数
22
- * @param befly - Befly 上下文
23
- */
24
- constructor(befly: BeflyContext) {
25
- this.befly = befly;
26
- }
27
-
28
- /**
29
- * 处理更新数据
30
- * - 移除 id、created_at、deleted_at 字段
31
- * - 添加 updated_at 时间戳
32
- * @param data - 原始数据
33
- * @returns 处理后的数据
34
- */
35
- async updData(data: DataObject): Promise<DataObject> {
36
- const now = Date.now();
37
- const cleaned = omitFields(data ?? {}, ['id', 'created_at', 'deleted_at'], [undefined]);
38
- return { ...cleaned, updated_at: now };
39
- }
40
-
41
- /**
42
- * 处理插入数据
43
- * - 生成唯一 ID
44
- * - 添加 created_at 和 updated_at 时间戳
45
- * - 移除 undefined 字段
46
- * @param data - 原始数据(支持单个对象或数组)
47
- * @returns 处理后的数据
48
- */
49
- async insData(data: DataObject | DataObject[]): Promise<DataObject | DataObject[]> {
50
- const now = Date.now();
51
- const genId = async (): Promise<number> => await this.befly.redis.genTimeID();
52
-
53
- if (Array.isArray(data)) {
54
- return await Promise.all(
55
- data.map(async (item) => ({
56
- ...omitFields(item ?? {}, [], [undefined]),
57
- id: await genId(),
58
- created_at: now,
59
- updated_at: now,
60
- state: 0
61
- }))
62
- );
63
- } else {
64
- const cleaned = omitFields(data ?? {}, [], [undefined]);
65
- return {
66
- ...cleaned,
67
- id: await genId(),
68
- created_at: now,
69
- updated_at: now,
70
- state: 0
71
- };
72
- }
73
- }
74
-
75
- /**
76
- * 处理删除数据(软删除)
77
- * - 设置 deleted_at 时间戳
78
- * - 添加 updated_at 时间戳
79
- * @returns 处理后的数据
80
- */
81
- async delData(): Promise<DataObject> {
82
- const now = Date.now();
83
- return {
84
- deleted_at: now,
85
- updated_at: now,
86
- state: 1
87
- };
88
- }
89
-
90
- /**
91
- * 批量生成 ID
92
- * @param count - 生成数量
93
- * @returns ID 数组
94
- */
95
- async genIds(count: number): Promise<number[]> {
96
- const ids: number[] = [];
97
- for (let i = 0; i < count; i++) {
98
- ids.push(await this.befly.redis.genTimeID());
99
- }
100
- return ids;
101
- }
102
-
103
- /**
104
- * 清理数据对象
105
- * - 移除 undefined、null、空字符串
106
- * @param data - 原始数据
107
- * @param removeNull - 是否移除 null
108
- * @param removeEmptyString - 是否移除空字符串
109
- * @returns 清理后的数据
110
- */
111
- cleanData(data: DataObject, removeNull: boolean = true, removeEmptyString: boolean = true): DataObject {
112
- const result: DataObject = {};
113
-
114
- for (const [key, value] of Object.entries(data)) {
115
- // 跳过 undefined
116
- if (value === undefined) continue;
117
-
118
- // 跳过 null(如果配置)
119
- if (removeNull && value === null) continue;
120
-
121
- // 跳过空字符串(如果配置)
122
- if (removeEmptyString && value === '') continue;
123
-
124
- result[key] = value;
125
- }
126
-
127
- return result;
128
- }
129
-
130
- /**
131
- * 合并数据对象(深度合并)
132
- * @param target - 目标对象
133
- * @param sources - 源对象(可多个)
134
- * @returns 合并后的对象
135
- */
136
- mergeData(target: DataObject, ...sources: DataObject[]): DataObject {
137
- const result = { ...target };
138
-
139
- for (const source of sources) {
140
- for (const [key, value] of Object.entries(source)) {
141
- if (value !== undefined) {
142
- result[key] = value;
143
- }
144
- }
145
- }
146
-
147
- return result;
148
- }
149
-
150
- /**
151
- * 提取指定字段
152
- * @param data - 原始数据
153
- * @param fields - 字段列表
154
- * @returns 提取后的数据
155
- */
156
- pickFields(data: DataObject, fields: string[]): DataObject {
157
- const result: DataObject = {};
158
-
159
- for (const field of fields) {
160
- if (field in data) {
161
- result[field] = data[field];
162
- }
163
- }
164
-
165
- return result;
166
- }
167
-
168
- /**
169
- * 排除指定字段
170
- * @param data - 原始数据
171
- * @param fields - 字段列表
172
- * @returns 排除后的数据
173
- */
174
- omitFields(data: DataObject, fields: string[]): DataObject {
175
- const result = { ...data };
176
-
177
- for (const field of fields) {
178
- delete result[field];
179
- }
180
-
181
- return result;
182
- }
183
-
184
- /**
185
- * 重命名字段
186
- * @param data - 原始数据
187
- * @param mapping - 字段映射 { oldKey: newKey }
188
- * @returns 重命名后的数据
189
- */
190
- renameFields(data: DataObject, mapping: Record<string, string>): DataObject {
191
- const result = { ...data };
192
-
193
- for (const [oldKey, newKey] of Object.entries(mapping)) {
194
- if (oldKey in result) {
195
- result[newKey] = result[oldKey];
196
- delete result[oldKey];
197
- }
198
- }
199
-
200
- return result;
201
- }
202
-
203
- /**
204
- * 转换字段类型
205
- * @param data - 原始数据
206
- * @param conversions - 转换规则 { field: 'number' | 'string' | 'boolean' }
207
- * @returns 转换后的数据
208
- */
209
- convertFields(data: DataObject, conversions: Record<string, 'number' | 'string' | 'boolean'>): DataObject {
210
- const result = { ...data };
211
-
212
- for (const [field, type] of Object.entries(conversions)) {
213
- if (field in result && result[field] !== null && result[field] !== undefined) {
214
- switch (type) {
215
- case 'number':
216
- result[field] = Number(result[field]);
217
- break;
218
- case 'string':
219
- result[field] = String(result[field]);
220
- break;
221
- case 'boolean':
222
- result[field] = Boolean(result[field]);
223
- break;
224
- }
225
- }
226
- }
227
-
228
- return result;
229
- }
230
- }
@@ -1,101 +0,0 @@
1
- /**
2
- * Befly 类型判断工具
3
- * 提供各种类型检查和判断功能
4
- */
5
-
6
- /**
7
- * 类型判断
8
- * @param value - 要判断的值
9
- * @param type - 期望的类型
10
- * @returns 是否匹配指定类型
11
- *
12
- * @example
13
- * isType(123, 'number') // true
14
- * isType('hello', 'string') // true
15
- * isType([], 'array') // true
16
- * isType({}, 'object') // true
17
- * isType(null, 'null') // true
18
- * isType(undefined, 'undefined') // true
19
- * isType(NaN, 'nan') // true
20
- * isType(42, 'integer') // true
21
- * isType(3.14, 'float') // true
22
- * isType(10, 'positive') // true
23
- * isType(-5, 'negative') // true
24
- * isType(0, 'zero') // true
25
- * isType('', 'empty') // true
26
- * isType(null, 'empty') // true
27
- * isType(true, 'truthy') // true
28
- * isType(false, 'falsy') // true
29
- * isType('str', 'primitive') // true
30
- * isType({}, 'reference') // true
31
- */
32
- export const isType = (value: any, type: string): boolean => {
33
- const actualType = Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
34
- const expectedType = String(type).toLowerCase();
35
-
36
- // 语义类型单独处理
37
- switch (expectedType) {
38
- case 'function':
39
- return typeof value === 'function';
40
- case 'nan':
41
- return typeof value === 'number' && Number.isNaN(value);
42
- case 'empty':
43
- return value === '' || value === null || value === undefined;
44
- case 'integer':
45
- return Number.isInteger(value);
46
- case 'float':
47
- return typeof value === 'number' && !Number.isInteger(value) && !Number.isNaN(value);
48
- case 'positive':
49
- return typeof value === 'number' && value > 0;
50
- case 'negative':
51
- return typeof value === 'number' && value < 0;
52
- case 'zero':
53
- return value === 0;
54
- case 'truthy':
55
- return !!value;
56
- case 'falsy':
57
- return !value;
58
- case 'primitive':
59
- return value !== Object(value);
60
- case 'reference':
61
- return value === Object(value);
62
- default:
63
- return actualType === expectedType;
64
- }
65
- };
66
-
67
- /**
68
- * 判断是否为空对象
69
- * @param obj - 要判断的值
70
- * @returns 是否为空对象
71
- *
72
- * @example
73
- * isEmptyObject({}) // true
74
- * isEmptyObject({ a: 1 }) // false
75
- * isEmptyObject([]) // false
76
- * isEmptyObject(null) // false
77
- */
78
- export const isEmptyObject = (obj: any): boolean => {
79
- if (!isType(obj, 'object')) {
80
- return false;
81
- }
82
- return Object.keys(obj).length === 0;
83
- };
84
-
85
- /**
86
- * 判断是否为空数组
87
- * @param arr - 要判断的值
88
- * @returns 是否为空数组
89
- *
90
- * @example
91
- * isEmptyArray([]) // true
92
- * isEmptyArray([1, 2]) // false
93
- * isEmptyArray({}) // false
94
- * isEmptyArray(null) // false
95
- */
96
- export const isEmptyArray = (arr: any): boolean => {
97
- if (!isType(arr, 'array')) {
98
- return false;
99
- }
100
- return arr.length === 0;
101
- };