befly 2.0.14 → 2.1.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.
- package/.gitignore +1 -0
- package/checks/table.js +145 -80
- package/config/env.js +1 -0
- package/main.js +43 -60
- package/package.json +9 -13
- package/plugins/db.js +137 -178
- package/scripts/syncDb.js +367 -0
- package/tables/common.json +14 -14
- package/tables/tool.json +4 -4
- package/utils/util.js +117 -19
- package/utils/validate.js +102 -113
- package/USEAGE.md +0 -5
- package/bin/befly.js +0 -176
- package/scripts/dbSync.js +0 -714
- package/scripts/release.js +0 -258
package/utils/validate.js
CHANGED
|
@@ -1,28 +1,95 @@
|
|
|
1
1
|
import { isType } from './util.js';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// 验证字段名称是否为中文、数字、字母
|
|
4
|
+
const validateFieldName = (name) => {
|
|
5
|
+
const nameRegex = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/;
|
|
6
|
+
return nameRegex.test(name);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// 验证字段类型是否为指定的四种类型之一
|
|
10
|
+
const validateFieldType = (type) => {
|
|
11
|
+
const validTypes = ['string', 'number', 'text', 'array'];
|
|
12
|
+
return validTypes.includes(type);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// 验证最小值/最大值是否为null或数字
|
|
16
|
+
const validateMinMax = (value) => {
|
|
17
|
+
return value === 'null' || (!isNaN(parseFloat(value)) && isFinite(parseFloat(value)));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// 验证默认值是否为null、字符串或数字
|
|
21
|
+
const validateDefaultValue = (value) => {
|
|
22
|
+
if (value === 'null') return true;
|
|
23
|
+
// 检查是否为数字
|
|
24
|
+
if (!isNaN(parseFloat(value)) && isFinite(parseFloat(value))) return true;
|
|
25
|
+
// 其他情况视为字符串,都是有效的
|
|
26
|
+
return true;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// 验证索引标识是否为0或1
|
|
30
|
+
const validateIndex = (value) => {
|
|
31
|
+
return value === '0' || value === '1';
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// 验证正则表达式是否有效
|
|
35
|
+
const validateRegex = (value) => {
|
|
36
|
+
if (value === 'null') return true;
|
|
37
|
+
try {
|
|
38
|
+
new RegExp(value);
|
|
39
|
+
return true;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// 专门用于处理⚡分隔的字段规则
|
|
4
46
|
const parseFieldRule = (rule) => {
|
|
5
|
-
const allParts = rule.split('
|
|
47
|
+
const allParts = rule.split('⚡');
|
|
6
48
|
|
|
7
|
-
//
|
|
8
|
-
if (allParts.length
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
49
|
+
// 必须包含7个部分:显示名⚡类型⚡最小值⚡最大值⚡默认值⚡是否索引⚡正则约束
|
|
50
|
+
if (allParts.length !== 7) {
|
|
51
|
+
throw new Error(`字段规则格式错误,必须包含7个部分,当前包含${allParts.length}个部分`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 验证各个部分的格式
|
|
55
|
+
const [name, type, minValue, maxValue, defaultValue, isIndex, regexConstraint] = allParts;
|
|
56
|
+
|
|
57
|
+
// 第1个值:名称必须为中文、数字、字母
|
|
58
|
+
if (!validateFieldName(name)) {
|
|
59
|
+
throw new Error(`字段名称 "${name}" 格式错误,必须为中文、数字、字母`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 第2个值:字段类型必须为string,number,text,array之一
|
|
63
|
+
if (!validateFieldType(type)) {
|
|
64
|
+
throw new Error(`字段类型 "${type}" 格式错误,必须为string、number、text、array之一`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 第3个值:最小值必须为null或数字
|
|
68
|
+
if (!validateMinMax(minValue)) {
|
|
69
|
+
throw new Error(`最小值 "${minValue}" 格式错误,必须为null或数字`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 第4个值:最大值必须为null或数字
|
|
73
|
+
if (!validateMinMax(maxValue)) {
|
|
74
|
+
throw new Error(`最大值 "${maxValue}" 格式错误,必须为null或数字`);
|
|
14
75
|
}
|
|
15
76
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
77
|
+
// 第5个值:默认值必须为null、字符串或数字
|
|
78
|
+
if (!validateDefaultValue(defaultValue)) {
|
|
79
|
+
throw new Error(`默认值 "${defaultValue}" 格式错误,必须为null、字符串或数字`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 第6个值:是否创建索引必须为0或1
|
|
83
|
+
if (!validateIndex(isIndex)) {
|
|
84
|
+
throw new Error(`索引标识 "${isIndex}" 格式错误,必须为0或1`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 第7个值:必须为null或正则表达式
|
|
88
|
+
if (!validateRegex(regexConstraint)) {
|
|
89
|
+
throw new Error(`正则约束 "${regexConstraint}" 格式错误,必须为null或有效的正则表达式`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return allParts;
|
|
26
93
|
};
|
|
27
94
|
|
|
28
95
|
/**
|
|
@@ -161,94 +228,20 @@ export class Validator {
|
|
|
161
228
|
}
|
|
162
229
|
|
|
163
230
|
if (spec && spec.trim() !== '') {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
231
|
+
try {
|
|
232
|
+
const regExp = new RegExp(spec);
|
|
233
|
+
if (!regExp.test(String(value))) {
|
|
234
|
+
return `${name}(${fieldName})格式不正确`;
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return `${name}(${fieldName})的正则表达式格式错误`;
|
|
170
238
|
}
|
|
171
239
|
}
|
|
172
240
|
|
|
173
241
|
return null;
|
|
174
242
|
} catch (error) {
|
|
175
|
-
return `${name}(${fieldName})
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* 判断是否为枚举格式
|
|
181
|
-
*/
|
|
182
|
-
isEnumSpec(spec) {
|
|
183
|
-
return spec && spec.startsWith('enum#');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* 验证枚举值
|
|
188
|
-
*/
|
|
189
|
-
validateEnum(value, spec, name, fieldName, type) {
|
|
190
|
-
// 解析枚举值 "enum#1,2,3" -> ["1", "2", "3"]
|
|
191
|
-
const enumValues = spec
|
|
192
|
-
.substring(5)
|
|
193
|
-
.split(',')
|
|
194
|
-
.map((v) => v.trim());
|
|
195
|
-
|
|
196
|
-
if (type === 'number') {
|
|
197
|
-
// 数字类型:转换枚举值为数字进行比较
|
|
198
|
-
const numericEnumValues = enumValues.map((v) => parseFloat(v));
|
|
199
|
-
if (!numericEnumValues.includes(value)) {
|
|
200
|
-
return `${name}(${fieldName})必须是以下值之一: ${enumValues.join(', ')}`;
|
|
201
|
-
}
|
|
202
|
-
} else if (type === 'string') {
|
|
203
|
-
// 字符串类型:直接比较
|
|
204
|
-
if (!enumValues.includes(value)) {
|
|
205
|
-
return `${name}(${fieldName})必须是以下值之一: ${enumValues.join(', ')}`;
|
|
206
|
-
}
|
|
207
|
-
} else if (type === 'array') {
|
|
208
|
-
// 数组类型:检查每个元素是否在枚举值中
|
|
209
|
-
for (const item of value) {
|
|
210
|
-
if (!enumValues.includes(String(item))) {
|
|
211
|
-
return `${name}(${fieldName})中的元素"${item}"必须是以下值之一: ${enumValues.join(', ')}`;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
243
|
+
return `${name}(${fieldName})验证出错: ${error.message}`;
|
|
214
244
|
}
|
|
215
|
-
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* 验证数字表达式
|
|
221
|
-
*/
|
|
222
|
-
validateNumberExpression(value, name, spec, fieldName) {
|
|
223
|
-
const parts = spec.split('=');
|
|
224
|
-
if (parts.length !== 2) {
|
|
225
|
-
return `${name}(${fieldName})的计算规则必须包含等号`;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const leftExpression = parts[0].trim();
|
|
229
|
-
const rightValue = parseFloat(parts[1].trim());
|
|
230
|
-
|
|
231
|
-
if (isNaN(rightValue)) {
|
|
232
|
-
return `${name}(${fieldName})的计算规则右边必须是数字`;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const safePattern = /^[x\d\+\-\*\/\(\)\.\s]+$/;
|
|
236
|
-
if (!safePattern.test(leftExpression)) {
|
|
237
|
-
return `${name}(${fieldName})的表达式包含不安全的字符`;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let processedExpression = leftExpression.replace(/x/g, value.toString());
|
|
241
|
-
const leftResult = new Function('return ' + processedExpression)();
|
|
242
|
-
|
|
243
|
-
if (typeof leftResult !== 'number' || !isFinite(leftResult)) {
|
|
244
|
-
return `${name}(${fieldName})的表达式计算结果不是有效数字`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (Math.abs(leftResult - rightValue) > Number.EPSILON) {
|
|
248
|
-
return `${name}(${fieldName})不满足计算条件 ${spec}`;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return null;
|
|
252
245
|
}
|
|
253
246
|
|
|
254
247
|
/**
|
|
@@ -269,21 +262,19 @@ export class Validator {
|
|
|
269
262
|
}
|
|
270
263
|
|
|
271
264
|
if (spec && spec.trim() !== '') {
|
|
272
|
-
|
|
273
|
-
if (this.isEnumSpec(spec)) {
|
|
274
|
-
return this.validateEnum(value, spec, name, fieldName, 'string');
|
|
275
|
-
} else {
|
|
276
|
-
// 原有的正则表达式验证逻辑
|
|
265
|
+
try {
|
|
277
266
|
const regExp = new RegExp(spec);
|
|
278
267
|
if (!regExp.test(value)) {
|
|
279
268
|
return `${name}(${fieldName})格式不正确`;
|
|
280
269
|
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
return `${name}(${fieldName})的正则表达式格式错误`;
|
|
281
272
|
}
|
|
282
273
|
}
|
|
283
274
|
|
|
284
275
|
return null;
|
|
285
276
|
} catch (error) {
|
|
286
|
-
return `${name}(${fieldName})
|
|
277
|
+
return `${name}(${fieldName})验证出错: ${error.message}`;
|
|
287
278
|
}
|
|
288
279
|
}
|
|
289
280
|
|
|
@@ -305,23 +296,21 @@ export class Validator {
|
|
|
305
296
|
}
|
|
306
297
|
|
|
307
298
|
if (spec && spec.trim() !== '') {
|
|
308
|
-
|
|
309
|
-
if (this.isEnumSpec(spec)) {
|
|
310
|
-
return this.validateEnum(value, spec, name, fieldName, 'array');
|
|
311
|
-
} else {
|
|
312
|
-
// 原有的正则表达式验证逻辑
|
|
299
|
+
try {
|
|
313
300
|
const regExp = new RegExp(spec);
|
|
314
301
|
for (const item of value) {
|
|
315
302
|
if (!regExp.test(String(item))) {
|
|
316
303
|
return `${name}(${fieldName})中的元素"${item}"格式不正确`;
|
|
317
304
|
}
|
|
318
305
|
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
return `${name}(${fieldName})的正则表达式格式错误`;
|
|
319
308
|
}
|
|
320
309
|
}
|
|
321
310
|
|
|
322
311
|
return null;
|
|
323
312
|
} catch (error) {
|
|
324
|
-
return `${name}(${fieldName})
|
|
313
|
+
return `${name}(${fieldName})验证出错: ${error.message}`;
|
|
325
314
|
}
|
|
326
315
|
}
|
|
327
316
|
}
|
package/USEAGE.md
DELETED
package/bin/befly.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Befly CLI 工具
|
|
5
|
-
* 提供命令行接口来执行 befly 框架的各种脚本
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { spawn } from 'node:child_process';
|
|
9
|
-
import { resolve, join, dirname } from 'node:path';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
12
|
-
import { readdir } from 'node:fs/promises';
|
|
13
|
-
import { __dirroot, __dirscript } from '../system.js';
|
|
14
|
-
|
|
15
|
-
// 使用 system.js 中定义的路径变量
|
|
16
|
-
const BEFLY_ROOT = __dirroot;
|
|
17
|
-
const SCRIPTS_DIR = __dirscript;
|
|
18
|
-
|
|
19
|
-
// 显示帮助信息
|
|
20
|
-
const showHelp = async () => {
|
|
21
|
-
console.log(`
|
|
22
|
-
Befly CLI 工具 v2.0.12
|
|
23
|
-
|
|
24
|
-
用法:
|
|
25
|
-
befly <script-name> [args...]
|
|
26
|
-
|
|
27
|
-
可用的脚本:
|
|
28
|
-
`);
|
|
29
|
-
|
|
30
|
-
// 列出所有可用的脚本
|
|
31
|
-
if (existsSync(SCRIPTS_DIR)) {
|
|
32
|
-
try {
|
|
33
|
-
const fileList = await readdir(SCRIPTS_DIR);
|
|
34
|
-
const scripts = fileList
|
|
35
|
-
.filter((file) => file.endsWith('.js'))
|
|
36
|
-
.map((file) => file.replace('.js', ''))
|
|
37
|
-
.sort();
|
|
38
|
-
|
|
39
|
-
if (scripts.length > 0) {
|
|
40
|
-
scripts.forEach((script) => {
|
|
41
|
-
console.log(` befly ${script}`);
|
|
42
|
-
});
|
|
43
|
-
} else {
|
|
44
|
-
console.log(' (没有找到可用的脚本)');
|
|
45
|
-
}
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.log(` (无法读取脚本目录: ${error.message})`);
|
|
48
|
-
}
|
|
49
|
-
} else {
|
|
50
|
-
console.log(' (脚本目录不存在)');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
console.log(`
|
|
54
|
-
选项:
|
|
55
|
-
-h, --help 显示此帮助信息
|
|
56
|
-
-v, --version 显示版本信息
|
|
57
|
-
|
|
58
|
-
更多信息请访问: https://chensuiyi.me
|
|
59
|
-
`);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// 显示版本信息
|
|
63
|
-
const showVersion = () => {
|
|
64
|
-
try {
|
|
65
|
-
const packageJsonPath = join(BEFLY_ROOT, 'package.json');
|
|
66
|
-
if (existsSync(packageJsonPath)) {
|
|
67
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
68
|
-
console.log(`befly v${packageJson.version}`);
|
|
69
|
-
} else {
|
|
70
|
-
console.log('befly v2.0.12');
|
|
71
|
-
}
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.log('befly v2.0.12');
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// 执行脚本
|
|
78
|
-
const runScript = (scriptName, args = []) => {
|
|
79
|
-
// 检查脚本文件是否存在
|
|
80
|
-
const scriptPath = join(SCRIPTS_DIR, `${scriptName}.js`);
|
|
81
|
-
|
|
82
|
-
if (!existsSync(scriptPath)) {
|
|
83
|
-
console.error(`❌ 错误: 脚本 '${scriptName}' 不存在`);
|
|
84
|
-
console.error(`脚本路径: ${scriptPath}`);
|
|
85
|
-
console.error('');
|
|
86
|
-
console.error('运行 "befly --help" 查看可用的脚本列表');
|
|
87
|
-
process.exit(1);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
console.log(`🚀 执行脚本: ${scriptName}`);
|
|
91
|
-
console.log(`📁 脚本路径: ${scriptPath}`);
|
|
92
|
-
|
|
93
|
-
// 检测运行环境,优先使用 bun,回退到 node
|
|
94
|
-
const isWindows = process.platform === 'win32';
|
|
95
|
-
let runtime = 'bun';
|
|
96
|
-
let runtimeArgs = ['run'];
|
|
97
|
-
|
|
98
|
-
// 如果 bun 不可用,回退到 node
|
|
99
|
-
const testBun = spawn(isWindows ? 'bun.exe' : 'bun', ['--version'], {
|
|
100
|
-
stdio: 'ignore',
|
|
101
|
-
shell: isWindows
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
testBun.on('error', () => {
|
|
105
|
-
runtime = 'node';
|
|
106
|
-
runtimeArgs = [];
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
testBun.on('close', (code) => {
|
|
110
|
-
if (code !== 0) {
|
|
111
|
-
runtime = 'node';
|
|
112
|
-
runtimeArgs = [];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
console.log(`⚡ 使用运行时: ${runtime}`);
|
|
116
|
-
console.log('');
|
|
117
|
-
|
|
118
|
-
// 执行脚本
|
|
119
|
-
const child = spawn(isWindows ? `${runtime}.exe` : runtime, [...runtimeArgs, scriptPath, ...args], {
|
|
120
|
-
stdio: 'inherit',
|
|
121
|
-
shell: isWindows,
|
|
122
|
-
cwd: process.cwd() // 使用调用者的工作目录
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
child.on('error', (error) => {
|
|
126
|
-
console.error(`❌ 执行失败: ${error.message}`);
|
|
127
|
-
process.exit(1);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
child.on('close', (code) => {
|
|
131
|
-
if (code !== 0) {
|
|
132
|
-
console.error(`❌ 脚本执行失败,退出码: ${code}`);
|
|
133
|
-
process.exit(code);
|
|
134
|
-
} else {
|
|
135
|
-
console.log(`✅ 脚本执行完成`);
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// 主函数
|
|
142
|
-
const main = async () => {
|
|
143
|
-
const args = process.argv.slice(2);
|
|
144
|
-
|
|
145
|
-
// 没有参数或请求帮助
|
|
146
|
-
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
147
|
-
await showHelp();
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 显示版本
|
|
152
|
-
if (args.includes('-v') || args.includes('--version')) {
|
|
153
|
-
showVersion();
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// 执行脚本
|
|
158
|
-
const scriptName = args[0];
|
|
159
|
-
const scriptArgs = args.slice(1);
|
|
160
|
-
|
|
161
|
-
runScript(scriptName, scriptArgs);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// 错误处理
|
|
165
|
-
process.on('uncaughtException', (error) => {
|
|
166
|
-
console.error('❌ 未捕获的异常:', error.message);
|
|
167
|
-
process.exit(1);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
171
|
-
console.error('❌ 未处理的 Promise 拒绝:', reason);
|
|
172
|
-
process.exit(1);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// 启动 CLI
|
|
176
|
-
main();
|