befly 1.2.9 → 2.0.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/libs/jwt.js ADDED
@@ -0,0 +1,97 @@
1
+ import { createHmac } from 'crypto';
2
+
3
+ /**
4
+ * JWT基础工具类
5
+ * 提供JWT相关的基础工具方法
6
+ */
7
+ export class Jwt {
8
+ // 支持的算法映射
9
+ static ALGORITHMS = {
10
+ HS256: 'sha256',
11
+ HS384: 'sha384',
12
+ HS512: 'sha512'
13
+ };
14
+
15
+ /**
16
+ * Base64 URL 编码
17
+ * @param {string|Buffer} input - 需要编码的输入
18
+ * @returns {string} Base64 URL编码结果
19
+ */
20
+ static base64UrlEncode(input) {
21
+ const base64 = Buffer.isBuffer(input) ? input.toString('base64') : Buffer.from(input, 'utf8').toString('base64');
22
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
23
+ }
24
+
25
+ /**
26
+ * Base64 URL 解码
27
+ * @param {string} str - Base64 URL编码的字符串
28
+ * @returns {string} 解码后的字符串
29
+ */
30
+ static base64UrlDecode(str) {
31
+ const padding = 4 - (str.length % 4);
32
+ if (padding !== 4) str += '='.repeat(padding);
33
+ str = str.replace(/-/g, '+').replace(/_/g, '/');
34
+ return Buffer.from(str, 'base64').toString('utf8');
35
+ }
36
+
37
+ /**
38
+ * 解析过期时间为秒数
39
+ * @param {string|number} expiresIn - 过期时间表达式
40
+ * @returns {number} 过期时间(秒数)
41
+ */
42
+ static parseExpiration(expiresIn) {
43
+ if (typeof expiresIn === 'number') return expiresIn;
44
+ if (typeof expiresIn !== 'string') throw new Error('过期时间格式无效');
45
+
46
+ // 如果是纯数字字符串,直接转换为数字
47
+ const numericValue = parseInt(expiresIn);
48
+ if (!isNaN(numericValue) && numericValue.toString() === expiresIn) {
49
+ return numericValue;
50
+ }
51
+
52
+ // 支持毫秒(ms)和其他时间单位
53
+ const match = expiresIn.match(/^(\d+)(ms|[smhdwy])$/);
54
+ if (!match) throw new Error('过期时间格式无效');
55
+
56
+ const value = parseInt(match[1]);
57
+ const unit = match[2];
58
+
59
+ if (unit === 'ms') {
60
+ return Math.floor(value / 1000); // 毫秒转秒,向下取整
61
+ }
62
+
63
+ const multipliers = { s: 1, m: 60, h: 3600, d: 86400, w: 604800, y: 31536000 };
64
+ return value * multipliers[unit];
65
+ }
66
+
67
+ /**
68
+ * 创建HMAC签名
69
+ * @param {string} algorithm - 算法名称
70
+ * @param {string} secret - 密钥
71
+ * @param {string} data - 待签名数据
72
+ * @returns {string} Base64 URL编码的签名
73
+ */
74
+ static createSignature(algorithm, secret, data) {
75
+ const hashAlgorithm = this.ALGORITHMS[algorithm];
76
+ if (!hashAlgorithm) throw new Error(`不支持的算法: ${algorithm}`);
77
+
78
+ const hmac = createHmac(hashAlgorithm, secret);
79
+ hmac.update(data);
80
+ return this.base64UrlEncode(hmac.digest());
81
+ }
82
+
83
+ /**
84
+ * 常量时间字符串比较(防止时序攻击)
85
+ * @param {string} a - 字符串A
86
+ * @param {string} b - 字符串B
87
+ * @returns {boolean} 是否相等
88
+ */
89
+ static constantTimeCompare(a, b) {
90
+ if (a.length !== b.length) return false;
91
+ let result = 0;
92
+ for (let i = 0; i < a.length; i++) {
93
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
94
+ }
95
+ return result === 0;
96
+ }
97
+ }
package/libs/xml.js ADDED
@@ -0,0 +1,357 @@
1
+ /**
2
+ * XML 解析器
3
+ * 将 XML 字符串解析为 JSON 对象
4
+ */
5
+ class Xml {
6
+ /**
7
+ * 构造函数
8
+ * @param {Object} options - 解析选项
9
+ */
10
+ constructor(options = {}) {
11
+ // 默认配置
12
+ this.options = {
13
+ // 基本配置
14
+ ignoreAttributes: false, // 是否忽略属性
15
+ attributePrefix: '@', // 属性前缀
16
+ textKey: '#text', // 文本内容的键名
17
+
18
+ // 格式化配置
19
+ trimValues: true, // 是否去除首尾空格
20
+ parseBooleans: true, // 是否解析布尔值
21
+ parseNumbers: true, // 是否解析数字
22
+
23
+ // 高级配置
24
+ customParser: true, // 是否使用自定义解析器
25
+
26
+ // 合并用户选项
27
+ ...options
28
+ };
29
+ }
30
+
31
+ /**
32
+ * 解析 XML 字符串为 JSON 对象
33
+ * @param {string} xmlData - XML 字符串
34
+ * @returns {Object} 解析后的 JSON 对象
35
+ */
36
+ parse(xmlData) {
37
+ if (typeof xmlData !== 'string') {
38
+ throw new Error('无效的 XML 数据');
39
+ }
40
+
41
+ if (xmlData.trim() === '') {
42
+ throw new Error('XML 数据必须是非空字符串');
43
+ }
44
+
45
+ // 移除 XML 声明和注释
46
+ xmlData = xmlData
47
+ .replace(/<\?xml[^>]*\?>/g, '')
48
+ .replace(/<!--[\s\S]*?-->/g, '')
49
+ .trim();
50
+
51
+ if (!xmlData) {
52
+ throw new Error('XML 数据必须是非空字符串');
53
+ }
54
+
55
+ // 自定义解析
56
+ if (this.options.customParser) {
57
+ return this.#customParse(xmlData);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * 解析属性字符串
63
+ * @param {string} attributesStr - 属性字符串
64
+ * @param {Object} element - 元素对象
65
+ */
66
+ #parseAttributes(attributesStr, element) {
67
+ // 移除标签名,只保留属性部分
68
+ const attrPart = attributesStr.replace(/^\w+\s*/, '');
69
+ const attrRegex = /(\w+)=["']([^"']*)["']/g;
70
+ let match;
71
+
72
+ while ((match = attrRegex.exec(attrPart)) !== null) {
73
+ const attrName = this.options.attributePrefix + match[1];
74
+ const attrValue = match[2];
75
+
76
+ // 某些属性名通常应该保持为字符串(如ID)
77
+ if (match[1].toLowerCase().includes('id') || match[1] === 'key' || match[1] === 'ref') {
78
+ element[attrName] = attrValue;
79
+ } else {
80
+ // 其他属性进行类型解析
81
+ element[attrName] = this.#parseValue(attrValue);
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 自定义解析方法
88
+ * @param {string} xmlData - XML 数据
89
+ * @returns {Object} 解析结果
90
+ */
91
+ #customParse(xmlData) {
92
+ const result = this.#parseXmlElement(xmlData, 0).value;
93
+
94
+ // 如果结果只有一个根元素,返回该元素的内容
95
+ const keys = Object.keys(result);
96
+ if (keys.length === 1) {
97
+ return result[keys[0]];
98
+ }
99
+ return result;
100
+ }
101
+
102
+ /**
103
+ * 解析 XML 元素
104
+ * @param {string} xml - XML 字符串
105
+ * @param {number} start - 开始位置
106
+ * @returns {Object} 包含解析结果和结束位置的对象
107
+ */
108
+ #parseXmlElement(xml, start) {
109
+ const tagStart = xml.indexOf('<', start);
110
+ if (tagStart === -1) {
111
+ return { value: {}, end: xml.length };
112
+ }
113
+
114
+ const tagEnd = xml.indexOf('>', tagStart);
115
+ if (tagEnd === -1) {
116
+ throw new Error('格式错误:未找到标签结束符');
117
+ }
118
+
119
+ const fullTag = xml.slice(tagStart + 1, tagEnd);
120
+ const element = {};
121
+
122
+ // 自闭合标签
123
+ if (fullTag.endsWith('/')) {
124
+ const tagName = fullTag.slice(0, -1).split(/\s+/)[0];
125
+ if (!this.options.ignoreAttributes && fullTag.includes(' ')) {
126
+ this.#parseAttributes(fullTag.slice(0, -1), element);
127
+ }
128
+ return { value: { [tagName]: Object.keys(element).length > 0 ? element : '' }, end: tagEnd + 1 };
129
+ }
130
+
131
+ // 注释
132
+ if (fullTag.startsWith('!--')) {
133
+ const commentEnd = xml.indexOf('-->', tagEnd);
134
+ if (commentEnd === -1) {
135
+ throw new Error('格式错误:未找到注释结束符');
136
+ }
137
+ return this.#parseXmlElement(xml, commentEnd + 3);
138
+ }
139
+
140
+ // CDATA
141
+ if (fullTag.startsWith('![CDATA[')) {
142
+ const cdataEnd = xml.indexOf(']]>', tagEnd);
143
+ if (cdataEnd === -1) {
144
+ throw new Error('格式错误:未找到 CDATA 结束符');
145
+ }
146
+ const cdataContent = xml.slice(tagEnd + 1, cdataEnd);
147
+ return this.#parseXmlElement(xml, cdataEnd + 3);
148
+ }
149
+
150
+ const tagName = fullTag.split(/\s+/)[0];
151
+
152
+ // 解析属性
153
+ if (!this.options.ignoreAttributes && fullTag.includes(' ')) {
154
+ this.#parseAttributes(fullTag, element);
155
+ }
156
+
157
+ // 查找结束标签
158
+ const endTag = `</${tagName}>`;
159
+ const endTagStart = xml.indexOf(endTag, tagEnd + 1);
160
+
161
+ if (endTagStart === -1) {
162
+ throw new Error(`格式错误:未找到结束标签 ${endTag}`);
163
+ }
164
+
165
+ const content = xml.slice(tagEnd + 1, endTagStart);
166
+
167
+ // 如果内容为空
168
+ if (!content.trim()) {
169
+ return { value: { [tagName]: Object.keys(element).length > 0 ? element : '' }, end: endTagStart + endTag.length };
170
+ }
171
+
172
+ // 如果没有子标签,直接解析文本
173
+ if (!content.includes('<')) {
174
+ const textValue = this.#parseValue(content);
175
+ if (Object.keys(element).length === 0) {
176
+ return { value: { [tagName]: textValue }, end: endTagStart + endTag.length };
177
+ } else {
178
+ element[this.options.textKey] = textValue;
179
+ return { value: { [tagName]: element }, end: endTagStart + endTag.length };
180
+ }
181
+ } else {
182
+ // 解析子元素和混合内容
183
+ let pos = 0;
184
+ const texts = [];
185
+
186
+ while (pos < content.length) {
187
+ const nextTag = content.indexOf('<', pos);
188
+
189
+ if (nextTag === -1) {
190
+ // 没有更多标签,剩余都是文本
191
+ const text = content.slice(pos).trim();
192
+ if (text) texts.push(text);
193
+ break;
194
+ }
195
+
196
+ // 处理标签前的文本
197
+ if (nextTag > pos) {
198
+ const text = content.slice(pos, nextTag).trim();
199
+ if (text) texts.push(text);
200
+ }
201
+
202
+ // 解析子元素
203
+ const childResult = this.#parseXmlElement(content, nextTag);
204
+ const childObj = childResult.value;
205
+
206
+ // 合并子元素到当前元素
207
+ for (const [key, value] of Object.entries(childObj)) {
208
+ this.#addElement(element, key, value);
209
+ }
210
+
211
+ pos = childResult.end;
212
+ }
213
+
214
+ // 合并文本内容
215
+ if (texts.length > 0) {
216
+ const combinedText = texts.join(' ');
217
+ if (Object.keys(element).length === 0) {
218
+ return { value: { [tagName]: this.#parseValue(combinedText) }, end: endTagStart + endTag.length };
219
+ } else {
220
+ element[this.options.textKey] = this.#parseValue(combinedText);
221
+ }
222
+ }
223
+
224
+ return { value: { [tagName]: element }, end: endTagStart + endTag.length };
225
+ }
226
+ }
227
+
228
+ /**
229
+ * 添加元素到对象
230
+ * @param {Object} parent - 父对象
231
+ * @param {string} name - 元素名
232
+ * @param {any} value - 元素值
233
+ */
234
+ #addElement(parent, name, value) {
235
+ if (parent[name] === undefined) {
236
+ parent[name] = value;
237
+ } else if (Array.isArray(parent[name])) {
238
+ parent[name].push(value);
239
+ } else {
240
+ parent[name] = [parent[name], value];
241
+ }
242
+ }
243
+
244
+ /**
245
+ * 解析值的类型
246
+ * @param {string} value - 原始值
247
+ * @returns {any} 解析后的值
248
+ */
249
+ #parseValue(value) {
250
+ if (!value || typeof value !== 'string') {
251
+ return value;
252
+ }
253
+
254
+ // 去除首尾空格
255
+ if (this.options.trimValues) {
256
+ value = value.trim();
257
+ }
258
+
259
+ // 解析布尔值
260
+ if (this.options.parseBooleans) {
261
+ if (value === 'true') return true;
262
+ if (value === 'false') return false;
263
+ }
264
+
265
+ // 解析数字
266
+ if (this.options.parseNumbers) {
267
+ // 整数
268
+ if (/^-?\d+$/.test(value)) {
269
+ const num = parseInt(value, 10);
270
+ if (num.toString() === value) {
271
+ return num;
272
+ }
273
+ }
274
+ // 浮点数
275
+ if (/^-?\d*\.\d+$/.test(value)) {
276
+ const num = parseFloat(value);
277
+ if (!isNaN(num)) {
278
+ return num;
279
+ }
280
+ }
281
+ // 科学计数法
282
+ if (/^-?\d*\.?\d+e[+-]?\d+$/i.test(value)) {
283
+ const num = parseFloat(value);
284
+ if (!isNaN(num)) {
285
+ return num;
286
+ }
287
+ }
288
+ }
289
+
290
+ // 处理 XML 实体
291
+ return this.#decodeEntities(value);
292
+ }
293
+
294
+ /**
295
+ * 解码 XML 实体
296
+ * @param {string} value - 包含实体的字符串
297
+ * @returns {string} 解码后的字符串
298
+ */
299
+ #decodeEntities(value) {
300
+ const entities = {
301
+ '&amp;': '&',
302
+ '&lt;': '<',
303
+ '&gt;': '>',
304
+ '&quot;': '"',
305
+ '&apos;': "'"
306
+ };
307
+
308
+ return value.replace(/&[^;]+;/g, (entity) => {
309
+ return entities[entity] || entity;
310
+ });
311
+ }
312
+
313
+ /**
314
+ * 静态方法:快速解析 XML
315
+ * @param {string} xmlData - XML 数据
316
+ * @param {Object} options - 解析选项
317
+ * @returns {Object} 解析后的 JSON 对象
318
+ */
319
+ static parse(xmlData, options = {}) {
320
+ const parser = new Xml(options);
321
+ return parser.parse(xmlData);
322
+ }
323
+
324
+ /**
325
+ * 静态方法:解析 XML 并保留属性
326
+ * @param {string} xmlData - XML 数据
327
+ * @returns {Object} 解析后的 JSON 对象
328
+ */
329
+ static parseWithAttributes(xmlData) {
330
+ return Xml.parse(xmlData, { ignoreAttributes: false });
331
+ }
332
+
333
+ /**
334
+ * 静态方法:解析 XML 并忽略属性
335
+ * @param {string} xmlData - XML 数据
336
+ * @returns {Object} 解析后的 JSON 对象
337
+ */
338
+ static parseIgnoreAttributes(xmlData) {
339
+ return Xml.parse(xmlData, { ignoreAttributes: true });
340
+ }
341
+
342
+ /**
343
+ * 静态方法:验证 XML 格式
344
+ * @param {string} xmlData - XML 数据
345
+ * @returns {boolean} 是否有效
346
+ */
347
+ static validate(xmlData) {
348
+ try {
349
+ Xml.parse(xmlData);
350
+ return true;
351
+ } catch (error) {
352
+ return false;
353
+ }
354
+ }
355
+ }
356
+
357
+ export { Xml };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "1.2.9",
3
+ "version": "2.0.0",
4
4
  "description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -13,7 +13,9 @@
13
13
  ".": "./main.js"
14
14
  },
15
15
  "scripts": {
16
- "r": "bun publish --registry=https://registry.npmjs.org --access=public"
16
+ "r": "bun publish --registry=https://registry.npmjs.org --access=public",
17
+ "test": "bun test",
18
+ "test:jwt": "bun test tests/jwt.test.js"
17
19
  },
18
20
  "keywords": [
19
21
  "bun",
@@ -49,5 +51,5 @@
49
51
  "README.md",
50
52
  "vitest.config.js"
51
53
  ],
52
- "gitHead": "1c357b830083af697e71a120f9126b88a8f8fe6e"
54
+ "gitHead": "59aa632aa23cb30a332b268fed8a82cc752ba89b"
53
55
  }