befly 3.8.29 → 3.8.30

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 (52) hide show
  1. package/README.md +8 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkTable.ts +3 -2
  4. package/hooks/parser.ts +5 -3
  5. package/hooks/permission.ts +12 -5
  6. package/lib/cacheHelper.ts +76 -62
  7. package/lib/connect.ts +8 -35
  8. package/lib/dbHelper.ts +14 -11
  9. package/lib/jwt.ts +58 -437
  10. package/lib/logger.ts +76 -197
  11. package/lib/redisHelper.ts +163 -1
  12. package/lib/sqlBuilder.ts +2 -1
  13. package/lib/validator.ts +9 -8
  14. package/loader/loadApis.ts +4 -7
  15. package/loader/loadHooks.ts +2 -2
  16. package/loader/loadPlugins.ts +4 -4
  17. package/main.ts +4 -17
  18. package/package.json +9 -8
  19. package/paths.ts +0 -6
  20. package/plugins/db.ts +2 -2
  21. package/plugins/jwt.ts +5 -5
  22. package/plugins/redis.ts +1 -1
  23. package/router/api.ts +2 -2
  24. package/router/static.ts +1 -2
  25. package/sync/syncAll.ts +2 -2
  26. package/sync/syncApi.ts +10 -7
  27. package/sync/syncDb/apply.ts +1 -2
  28. package/sync/syncDb.ts +6 -10
  29. package/sync/syncDev.ts +10 -48
  30. package/sync/syncMenu.ts +11 -8
  31. package/tests/cacheHelper.test.ts +327 -0
  32. package/tests/dbHelper-columns.test.ts +5 -20
  33. package/tests/dbHelper-execute.test.ts +14 -68
  34. package/tests/fields-redis-cache.test.ts +5 -3
  35. package/tests/integration.test.ts +15 -26
  36. package/tests/jwt.test.ts +36 -94
  37. package/tests/logger.test.ts +32 -34
  38. package/tests/redisHelper.test.ts +270 -0
  39. package/tests/redisKeys.test.ts +76 -0
  40. package/tests/sync-connection.test.ts +0 -6
  41. package/tests/syncDb-constants.test.ts +12 -12
  42. package/tests/util.test.ts +5 -1
  43. package/types/befly.d.ts +2 -15
  44. package/types/common.d.ts +11 -93
  45. package/types/database.d.ts +216 -5
  46. package/types/index.ts +1 -0
  47. package/types/logger.d.ts +11 -41
  48. package/types/table.d.ts +213 -0
  49. package/hooks/_rateLimit.ts +0 -64
  50. package/lib/regexAliases.ts +0 -59
  51. package/lib/xml.ts +0 -383
  52. package/tests/xml.test.ts +0 -101
package/lib/xml.ts DELETED
@@ -1,383 +0,0 @@
1
- /**
2
- * XML 解析器 - TypeScript 版本
3
- * 将 XML 字符串解析为 JSON 对象
4
- */
5
-
6
- /**
7
- * XML 解析选项
8
- */
9
- interface XmlParseOptions {
10
- /** 是否忽略属性 */
11
- ignoreAttributes?: boolean;
12
- /** 属性前缀 */
13
- attributePrefix?: string;
14
- /** 文本内容的键名 */
15
- textKey?: string;
16
- /** 是否去除首尾空格 */
17
- trimValues?: boolean;
18
- /** 是否解析布尔值 */
19
- parseBooleans?: boolean;
20
- /** 是否解析数字 */
21
- parseNumbers?: boolean;
22
- /** 是否使用自定义解析器 */
23
- customParser?: boolean;
24
- }
25
-
26
- /**
27
- * 解析结果接口(内部使用)
28
- */
29
- interface ParseResult {
30
- value: Record<string, any>;
31
- end: number;
32
- }
33
-
34
- /**
35
- * XML 解析器类
36
- */
37
- export class Xml {
38
- private options: Required<XmlParseOptions>;
39
-
40
- /**
41
- * 构造函数
42
- * @param options - 解析选项
43
- */
44
- constructor(options: XmlParseOptions = {}) {
45
- // 默认配置
46
- this.options = {
47
- ignoreAttributes: false,
48
- attributePrefix: '@',
49
- textKey: '#text',
50
- trimValues: true,
51
- parseBooleans: true,
52
- parseNumbers: true,
53
- customParser: true,
54
- ...options
55
- };
56
- }
57
-
58
- /**
59
- * 解析 XML 字符串为 JSON 对象
60
- * @param xmlData - XML 字符串
61
- * @returns 解析后的 JSON 对象
62
- */
63
- parse(xmlData: string): Record<string, any> {
64
- if (typeof xmlData !== 'string') {
65
- throw new Error('无效的 XML 数据');
66
- }
67
-
68
- if (xmlData.trim() === '') {
69
- throw new Error('XML 数据必须是非空字符串');
70
- }
71
-
72
- // 移除 XML 声明和注释
73
- xmlData = xmlData
74
- .replace(/<\?xml[^>]*\?>/g, '')
75
- .replace(/<!--[\s\S]*?-->/g, '')
76
- .trim();
77
-
78
- if (!xmlData) {
79
- throw new Error('XML 数据必须是非空字符串');
80
- }
81
-
82
- // 自定义解析
83
- if (this.options.customParser) {
84
- return this.customParse(xmlData);
85
- }
86
-
87
- return {};
88
- }
89
-
90
- /**
91
- * 解析属性字符串
92
- * @param attributesStr - 属性字符串
93
- * @param element - 元素对象
94
- */
95
- private parseAttributes(attributesStr: string, element: Record<string, any>): void {
96
- // 移除标签名,只保留属性部分
97
- const attrPart = attributesStr.replace(/^\w+\s*/, '');
98
- const attrRegex = /(\w+)=["']([^"']*)["']/g;
99
- let match: RegExpExecArray | null;
100
-
101
- while ((match = attrRegex.exec(attrPart)) !== null) {
102
- const attrName = this.options.attributePrefix + match[1];
103
- const attrValue = match[2];
104
-
105
- // 某些属性名通常应该保持为字符串(如ID)
106
- if (match[1].toLowerCase().includes('id') || match[1] === 'key' || match[1] === 'ref') {
107
- element[attrName] = attrValue;
108
- } else {
109
- // 其他属性进行类型解析
110
- element[attrName] = this.parseValue(attrValue);
111
- }
112
- }
113
- }
114
-
115
- /**
116
- * 自定义解析方法
117
- * @param xmlData - XML 数据
118
- * @returns 解析结果
119
- */
120
- private customParse(xmlData: string): Record<string, any> {
121
- const result = this.parseXmlElement(xmlData, 0).value;
122
-
123
- // 如果结果只有一个根元素,返回该元素的内容
124
- const keys = Object.keys(result);
125
- if (keys.length === 1) {
126
- return result[keys[0]];
127
- }
128
- return result;
129
- }
130
-
131
- /**
132
- * 解析 XML 元素
133
- * @param xml - XML 字符串
134
- * @param start - 开始位置
135
- * @returns 包含解析结果和结束位置的对象
136
- */
137
- private parseXmlElement(xml: string, start: number): ParseResult {
138
- const tagStart = xml.indexOf('<', start);
139
- if (tagStart === -1) {
140
- return { value: {}, end: xml.length };
141
- }
142
-
143
- const tagEnd = xml.indexOf('>', tagStart);
144
- if (tagEnd === -1) {
145
- throw new Error('格式错误:未找到标签结束符');
146
- }
147
-
148
- const fullTag = xml.slice(tagStart + 1, tagEnd);
149
- const element: Record<string, any> = {};
150
-
151
- // 自闭合标签
152
- if (fullTag.endsWith('/')) {
153
- const tagName = fullTag.slice(0, -1).split(/\s+/)[0];
154
- if (!this.options.ignoreAttributes && fullTag.includes(' ')) {
155
- this.parseAttributes(fullTag.slice(0, -1), element);
156
- }
157
- return { value: { [tagName]: Object.keys(element).length > 0 ? element : '' }, end: tagEnd + 1 };
158
- }
159
-
160
- // 注释
161
- if (fullTag.startsWith('!--')) {
162
- const commentEnd = xml.indexOf('-->', tagEnd);
163
- if (commentEnd === -1) {
164
- throw new Error('格式错误:未找到注释结束符');
165
- }
166
- return this.parseXmlElement(xml, commentEnd + 3);
167
- }
168
-
169
- // CDATA
170
- if (fullTag.startsWith('![CDATA[')) {
171
- const cdataEnd = xml.indexOf(']]>', tagEnd);
172
- if (cdataEnd === -1) {
173
- throw new Error('格式错误:未找到 CDATA 结束符');
174
- }
175
- return this.parseXmlElement(xml, cdataEnd + 3);
176
- }
177
-
178
- const tagName = fullTag.split(/\s+/)[0];
179
-
180
- // 解析属性
181
- if (!this.options.ignoreAttributes && fullTag.includes(' ')) {
182
- this.parseAttributes(fullTag, element);
183
- }
184
-
185
- // 查找结束标签
186
- const endTag = `</${tagName}>`;
187
- const endTagStart = xml.indexOf(endTag, tagEnd + 1);
188
-
189
- if (endTagStart === -1) {
190
- throw new Error(`格式错误:未找到结束标签 ${endTag}`);
191
- }
192
-
193
- const content = xml.slice(tagEnd + 1, endTagStart);
194
-
195
- // 如果内容为空
196
- if (!content.trim()) {
197
- return { value: { [tagName]: Object.keys(element).length > 0 ? element : '' }, end: endTagStart + endTag.length };
198
- }
199
-
200
- // 如果没有子标签,直接解析文本
201
- if (!content.includes('<')) {
202
- const textValue = this.parseValue(content);
203
- if (Object.keys(element).length === 0) {
204
- return { value: { [tagName]: textValue }, end: endTagStart + endTag.length };
205
- } else {
206
- element[this.options.textKey] = textValue;
207
- return { value: { [tagName]: element }, end: endTagStart + endTag.length };
208
- }
209
- } else {
210
- // 解析子元素和混合内容
211
- let pos = 0;
212
- const texts: string[] = [];
213
-
214
- while (pos < content.length) {
215
- const nextTag = content.indexOf('<', pos);
216
-
217
- if (nextTag === -1) {
218
- // 没有更多标签,剩余都是文本
219
- const text = content.slice(pos).trim();
220
- if (text) texts.push(text);
221
- break;
222
- }
223
-
224
- // 处理标签前的文本
225
- if (nextTag > pos) {
226
- const text = content.slice(pos, nextTag).trim();
227
- if (text) texts.push(text);
228
- }
229
-
230
- // 解析子元素
231
- const childResult = this.parseXmlElement(content, nextTag);
232
- const childObj = childResult.value;
233
-
234
- // 合并子元素到当前元素
235
- for (const [key, value] of Object.entries(childObj)) {
236
- this.addElement(element, key, value);
237
- }
238
-
239
- pos = childResult.end;
240
- }
241
-
242
- // 合并文本内容
243
- if (texts.length > 0) {
244
- const combinedText = texts.join(' ');
245
- if (Object.keys(element).length === 0) {
246
- return { value: { [tagName]: this.parseValue(combinedText) }, end: endTagStart + endTag.length };
247
- } else {
248
- element[this.options.textKey] = this.parseValue(combinedText);
249
- }
250
- }
251
-
252
- return { value: { [tagName]: element }, end: endTagStart + endTag.length };
253
- }
254
- }
255
-
256
- /**
257
- * 添加元素到对象
258
- * @param parent - 父对象
259
- * @param name - 元素名
260
- * @param value - 元素值
261
- */
262
- private addElement(parent: Record<string, any>, name: string, value: any): void {
263
- if (parent[name] === undefined) {
264
- parent[name] = value;
265
- } else if (Array.isArray(parent[name])) {
266
- parent[name].push(value);
267
- } else {
268
- parent[name] = [parent[name], value];
269
- }
270
- }
271
-
272
- /**
273
- * 解析值的类型
274
- * @param value - 原始值
275
- * @returns 解析后的值
276
- */
277
- private parseValue(value: string): string | number | boolean {
278
- if (!value || typeof value !== 'string') {
279
- return value;
280
- }
281
-
282
- // 去除首尾空格
283
- if (this.options.trimValues) {
284
- value = value.trim();
285
- }
286
-
287
- // 解析布尔值
288
- if (this.options.parseBooleans) {
289
- if (value === 'true') return true;
290
- if (value === 'false') return false;
291
- }
292
-
293
- // 解析数字
294
- if (this.options.parseNumbers) {
295
- // 整数
296
- if (/^-?\d+$/.test(value)) {
297
- const num = parseInt(value, 10);
298
- if (num.toString() === value) {
299
- return num;
300
- }
301
- }
302
- // 浮点数
303
- if (/^-?\d*\.\d+$/.test(value)) {
304
- const num = parseFloat(value);
305
- if (!isNaN(num)) {
306
- return num;
307
- }
308
- }
309
- // 科学计数法
310
- if (/^-?\d*\.?\d+e[+-]?\d+$/i.test(value)) {
311
- const num = parseFloat(value);
312
- if (!isNaN(num)) {
313
- return num;
314
- }
315
- }
316
- }
317
-
318
- // 处理 XML 实体
319
- return this.decodeEntities(value);
320
- }
321
-
322
- /**
323
- * 解码 XML 实体
324
- * @param value - 包含实体的字符串
325
- * @returns 解码后的字符串
326
- */
327
- private decodeEntities(value: string): string {
328
- const entities: Record<string, string> = {
329
- '&amp;': '&',
330
- '&lt;': '<',
331
- '&gt;': '>',
332
- '&quot;': '"',
333
- '&apos;': "'"
334
- };
335
-
336
- return value.replace(/&[^;]+;/g, (entity) => {
337
- return entities[entity] || entity;
338
- });
339
- }
340
-
341
- /**
342
- * 静态方法:快速解析 XML
343
- * @param xmlData - XML 数据
344
- * @param options - 解析选项
345
- * @returns 解析后的 JSON 对象
346
- */
347
- static parse(xmlData: string, options: XmlParseOptions = {}): Record<string, any> {
348
- const parser = new Xml(options);
349
- return parser.parse(xmlData);
350
- }
351
-
352
- /**
353
- * 静态方法:解析 XML 并保留属性
354
- * @param xmlData - XML 数据
355
- * @returns 解析后的 JSON 对象
356
- */
357
- static parseWithAttributes(xmlData: string): Record<string, any> {
358
- return Xml.parse(xmlData, { ignoreAttributes: false });
359
- }
360
-
361
- /**
362
- * 静态方法:解析 XML 并忽略属性
363
- * @param xmlData - XML 数据
364
- * @returns 解析后的 JSON 对象
365
- */
366
- static parseIgnoreAttributes(xmlData: string): Record<string, any> {
367
- return Xml.parse(xmlData, { ignoreAttributes: true });
368
- }
369
-
370
- /**
371
- * 静态方法:验证 XML 格式
372
- * @param xmlData - XML 数据
373
- * @returns 是否有效
374
- */
375
- static validate(xmlData: string): boolean {
376
- try {
377
- Xml.parse(xmlData);
378
- return true;
379
- } catch (error) {
380
- return false;
381
- }
382
- }
383
- }
package/tests/xml.test.ts DELETED
@@ -1,101 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { Xml } from '../lib/xml';
3
-
4
- describe('Xml - 基本解析', () => {
5
- test('解析简单元素', () => {
6
- const xml = new Xml();
7
- const result = xml.parse('<root>Hello</root>');
8
- expect(result).toBe('Hello');
9
- });
10
-
11
- test('解析嵌套元素', () => {
12
- const xml = new Xml();
13
- const result = xml.parse('<root><child>Value</child></root>');
14
- expect(result).toEqual({ child: 'Value' });
15
- });
16
-
17
- test('解析多个同名元素', () => {
18
- const xml = new Xml();
19
- const result = xml.parse('<root><item>A</item><item>B</item></root>');
20
- expect(result).toEqual({ item: ['A', 'B'] });
21
- });
22
-
23
- test('解析空元素', () => {
24
- const xml = new Xml();
25
- const result = xml.parse('<root></root>');
26
- expect(result).toBe('');
27
- });
28
-
29
- test('解析自闭合标签', () => {
30
- const xml = new Xml();
31
- const result = xml.parse('<root><item/></root>');
32
- expect(result).toEqual({ item: '' });
33
- });
34
- });
35
-
36
- describe('Xml - 属性解析', () => {
37
- test('解析元素属性', () => {
38
- const xml = new Xml();
39
- const result = xml.parse('<root id="1">Value</root>') as any;
40
- expect(result['@id']).toBe('1');
41
- expect(result['#text']).toBe('Value');
42
- });
43
-
44
- test('解析多个属性', () => {
45
- const xml = new Xml();
46
- const result = xml.parse('<user id="123" name="John">Content</user>') as any;
47
- expect(result['@id']).toBe('123');
48
- expect(result['@name']).toBe('John');
49
- expect(result['#text']).toBe('Content');
50
- });
51
-
52
- test('忽略属性', () => {
53
- const xml = new Xml({ ignoreAttributes: true });
54
- const result = xml.parse('<root id="1">Value</root>');
55
- expect(result).toBe('Value');
56
- });
57
- });
58
-
59
- describe('Xml - 数值解析', () => {
60
- test('自动解析数字', () => {
61
- const xml = new Xml();
62
- const result = xml.parse('<root>123</root>');
63
- expect(result).toBe(123);
64
- });
65
-
66
- test('自动解析布尔值', () => {
67
- const xml = new Xml();
68
- const result1 = xml.parse('<root>true</root>');
69
- const result2 = xml.parse('<root>false</root>');
70
- expect(result1).toBe(true);
71
- expect(result2).toBe(false);
72
- });
73
-
74
- test('禁用数字解析', () => {
75
- const xml = new Xml({ parseNumbers: false });
76
- const result = xml.parse('<root>123</root>');
77
- expect(result).toBe('123');
78
- });
79
- });
80
-
81
- describe('Xml - 错误处理', () => {
82
- test('拒绝非字符串输入', () => {
83
- const xml = new Xml();
84
- expect(() => xml.parse(123 as any)).toThrow('无效的 XML 数据');
85
- });
86
-
87
- test('拒绝空字符串', () => {
88
- const xml = new Xml();
89
- expect(() => xml.parse('')).toThrow('非空字符串');
90
- });
91
-
92
- test('拒绝只有空格的字符串', () => {
93
- const xml = new Xml();
94
- expect(() => xml.parse(' ')).toThrow('非空字符串');
95
- });
96
-
97
- test('检测未闭合标签', () => {
98
- const xml = new Xml();
99
- expect(() => xml.parse('<root><item>')).toThrow('未找到结束标签');
100
- });
101
- });