befly 2.3.3 → 3.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.
Files changed (93) hide show
  1. package/apis/health/info.ts +64 -0
  2. package/apis/tool/tokenCheck.ts +51 -0
  3. package/bin/befly.ts +202 -0
  4. package/checks/conflict.ts +408 -0
  5. package/checks/{table.js → table.ts} +139 -61
  6. package/config/env.ts +218 -0
  7. package/config/reserved.ts +96 -0
  8. package/main.ts +101 -0
  9. package/package.json +44 -8
  10. package/plugins/{db.js → db.ts} +24 -11
  11. package/plugins/logger.ts +28 -0
  12. package/plugins/redis.ts +51 -0
  13. package/plugins/tool.ts +34 -0
  14. package/scripts/syncDb/apply.ts +171 -0
  15. package/scripts/syncDb/constants.ts +70 -0
  16. package/scripts/syncDb/ddl.ts +182 -0
  17. package/scripts/syncDb/helpers.ts +172 -0
  18. package/scripts/syncDb/index.ts +215 -0
  19. package/scripts/syncDb/schema.ts +199 -0
  20. package/scripts/syncDb/sqlite.ts +50 -0
  21. package/scripts/syncDb/state.ts +104 -0
  22. package/scripts/syncDb/table.ts +204 -0
  23. package/scripts/syncDb/tableCreate.ts +142 -0
  24. package/scripts/syncDb/tests/constants.test.ts +104 -0
  25. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  26. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  27. package/scripts/syncDb/types.ts +92 -0
  28. package/scripts/syncDb/version.ts +73 -0
  29. package/scripts/syncDb.ts +9 -0
  30. package/scripts/{syncDev.js → syncDev.ts} +41 -25
  31. package/system.ts +149 -0
  32. package/tables/_common.json +21 -0
  33. package/tables/admin.json +10 -0
  34. package/tsconfig.json +58 -0
  35. package/types/api.d.ts +246 -0
  36. package/types/befly.d.ts +234 -0
  37. package/types/common.d.ts +215 -0
  38. package/types/context.ts +167 -0
  39. package/types/crypto.d.ts +23 -0
  40. package/types/database.d.ts +278 -0
  41. package/types/index.d.ts +16 -0
  42. package/types/index.ts +459 -0
  43. package/types/jwt.d.ts +99 -0
  44. package/types/logger.d.ts +43 -0
  45. package/types/plugin.d.ts +109 -0
  46. package/types/redis.d.ts +44 -0
  47. package/types/tool.d.ts +67 -0
  48. package/types/validator.d.ts +45 -0
  49. package/utils/addonHelper.ts +60 -0
  50. package/utils/api.ts +23 -0
  51. package/utils/{colors.js → colors.ts} +79 -21
  52. package/utils/crypto.ts +308 -0
  53. package/utils/datetime.ts +51 -0
  54. package/utils/dbHelper.ts +142 -0
  55. package/utils/errorHandler.ts +68 -0
  56. package/utils/index.ts +46 -0
  57. package/utils/jwt.ts +493 -0
  58. package/utils/logger.ts +284 -0
  59. package/utils/objectHelper.ts +68 -0
  60. package/utils/pluginHelper.ts +62 -0
  61. package/utils/redisHelper.ts +338 -0
  62. package/utils/response.ts +38 -0
  63. package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
  64. package/utils/sqlHelper.ts +447 -0
  65. package/utils/tableHelper.ts +167 -0
  66. package/utils/tool.ts +230 -0
  67. package/utils/typeHelper.ts +101 -0
  68. package/utils/validate.ts +451 -0
  69. package/utils/{xml.js → xml.ts} +100 -74
  70. package/.npmrc +0 -3
  71. package/.prettierignore +0 -2
  72. package/.prettierrc +0 -11
  73. package/apis/health/info.js +0 -49
  74. package/apis/tool/tokenCheck.js +0 -29
  75. package/bin/befly.js +0 -109
  76. package/config/env.js +0 -64
  77. package/main.js +0 -579
  78. package/plugins/logger.js +0 -14
  79. package/plugins/redis.js +0 -32
  80. package/plugins/tool.js +0 -8
  81. package/scripts/syncDb.js +0 -752
  82. package/system.js +0 -118
  83. package/tables/common.json +0 -16
  84. package/tables/tool.json +0 -6
  85. package/utils/api.js +0 -27
  86. package/utils/crypto.js +0 -260
  87. package/utils/index.js +0 -334
  88. package/utils/jwt.js +0 -387
  89. package/utils/logger.js +0 -143
  90. package/utils/redisHelper.js +0 -74
  91. package/utils/sqlManager.js +0 -471
  92. package/utils/tool.js +0 -31
  93. package/utils/validate.js +0 -226
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Redis 相关类型定义
3
+ */
4
+
5
+ import { RedisClient as BunRedisClient } from 'bun';
6
+
7
+ /**
8
+ * Redis 客户端类型
9
+ */
10
+ export type RedisClient = BunRedisClient;
11
+
12
+ /**
13
+ * Redis 键前缀
14
+ */
15
+ export type RedisKeyPrefix = string;
16
+
17
+ /**
18
+ * Redis TTL(秒)
19
+ */
20
+ export type RedisTTL = number | null;
21
+
22
+ /**
23
+ * Redis 助手接口
24
+ */
25
+ export interface RedisHelper {
26
+ /** 设置对象到 Redis */
27
+ setObject<T = any>(key: string, obj: T, ttl?: RedisTTL): Promise<string | null>;
28
+ /** 从 Redis 获取对象 */
29
+ getObject<T = any>(key: string): Promise<T | null>;
30
+ /** 从 Redis 删除对象 */
31
+ delObject(key: string): Promise<void>;
32
+ /** 生成基于时间的唯一 ID */
33
+ genTimeID(): Promise<number>;
34
+ /** 设置字符串值 */
35
+ setString(key: string, value: string, ttl?: RedisTTL): Promise<string | null>;
36
+ /** 获取字符串值 */
37
+ getString(key: string): Promise<string | null>;
38
+ /** 检查键是否存在 */
39
+ exists(key: string): Promise<number>;
40
+ /** 设置过期时间 */
41
+ expire(key: string, seconds: number): Promise<number>;
42
+ /** 获取剩余过期时间 */
43
+ ttl(key: string): Promise<number>;
44
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * 工具函数相关类型定义
3
+ */
4
+
5
+ /**
6
+ * 日期格式化类型
7
+ */
8
+ export type DateFormat = 'YYYY-MM-DD' | 'YYYY-MM-DD HH:mm:ss' | 'HH:mm:ss' | 'YYYY/MM/DD' | 'MM-DD' | string;
9
+
10
+ /**
11
+ * 颜色代码
12
+ */
13
+ export type ColorCode = 'reset' | 'bright' | 'dim' | 'underscore' | 'blink' | 'reverse' | 'hidden' | 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white';
14
+
15
+ /**
16
+ * 分页参数
17
+ */
18
+ export interface PaginationParams {
19
+ /** 页码(从 1 开始) */
20
+ page?: number;
21
+ /** 每页数量 */
22
+ limit?: number;
23
+ }
24
+
25
+ /**
26
+ * 分页结果
27
+ */
28
+ export interface PaginationResult<T = any> {
29
+ /** 数据列表 */
30
+ list: T[];
31
+ /** 总条数 */
32
+ total: number;
33
+ /** 当前页码 */
34
+ page: number;
35
+ /** 每页数量 */
36
+ limit: number;
37
+ /** 总页数 */
38
+ pages: number;
39
+ /** 是否有下一页 */
40
+ hasNext: boolean;
41
+ /** 是否有上一页 */
42
+ hasPrev: boolean;
43
+ }
44
+
45
+ /**
46
+ * 文件类型
47
+ */
48
+ export type FileType = 'image' | 'video' | 'audio' | 'document' | 'archive' | 'other';
49
+
50
+ /**
51
+ * MIME 类型
52
+ */
53
+ export type MimeType = string;
54
+
55
+ /**
56
+ * 文件上传选项
57
+ */
58
+ export interface FileUploadOptions {
59
+ /** 允许的文件类型 */
60
+ allowedTypes?: string[];
61
+ /** 最大文件大小(字节) */
62
+ maxSize?: number;
63
+ /** 保存目录 */
64
+ saveDir?: string;
65
+ /** 文件名生成函数 */
66
+ filename?: (originalName: string) => string;
67
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 验证器相关类型定义
3
+ */
4
+
5
+ /**
6
+ * 字段验证错误信息
7
+ */
8
+ export type ValidationError = string | null;
9
+
10
+ /**
11
+ * 字段类型
12
+ */
13
+ export type FieldType = 'string' | 'number' | 'text' | 'array';
14
+
15
+ /**
16
+ * 验证结果
17
+ */
18
+ export interface ValidationResult {
19
+ /** 验证状态码:0=成功,1=失败 */
20
+ code: 0 | 1;
21
+ /** 字段验证结果 */
22
+ fields: Record<string, any>;
23
+ }
24
+
25
+ /**
26
+ * 字段规则
27
+ */
28
+ export interface FieldRule {
29
+ /** 字段名 */
30
+ name: string;
31
+ /** 字段类型 */
32
+ type: FieldType;
33
+ /** 最小值/长度 */
34
+ min: number | null;
35
+ /** 最大值/长度 */
36
+ max: number | null;
37
+ /** 默认值 */
38
+ default: any;
39
+ /** 是否索引 */
40
+ index: 0 | 1;
41
+ /** 正则约束 */
42
+ regex: string | null;
43
+ }
44
+
45
+ export type FieldType = 'string' | 'number' | 'text' | 'array';
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Addon 辅助工具函数
3
+ * 提供 addon 扫描、路径获取等功能
4
+ */
5
+
6
+ import fs from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { getProjectRoot } from '../system.js';
9
+
10
+ /**
11
+ * 获取 addons 目录路径
12
+ */
13
+ export const getAddonsDir = (): string => {
14
+ return join(getProjectRoot(), 'addons');
15
+ };
16
+
17
+ /**
18
+ * 扫描所有可用的 addon
19
+ * @returns addon 名称数组(过滤掉 _ 开头的目录)
20
+ */
21
+ export const scanAddons = (): string[] => {
22
+ const addonsDir = getAddonsDir();
23
+ if (!fs.existsSync(addonsDir)) {
24
+ return [];
25
+ }
26
+
27
+ try {
28
+ return fs
29
+ .readdirSync(addonsDir)
30
+ .filter((name) => {
31
+ const fullPath = join(addonsDir, name);
32
+ const stat = fs.statSync(fullPath);
33
+ const isDir = stat.isDirectory();
34
+ const notSkip = !name.startsWith('_'); // 跳过 _ 开头的目录
35
+ return isDir && notSkip;
36
+ })
37
+ .sort(); // 按字母顺序排序
38
+ } catch (error) {
39
+ return [];
40
+ }
41
+ };
42
+
43
+ /**
44
+ * 获取 addon 的指定子目录路径
45
+ * @param addonName - addon 名称
46
+ * @param subDir - 子目录名称(apis, checks, plugins, tables, types, config)
47
+ */
48
+ export const getAddonDir = (addonName: string, subDir: string): string => {
49
+ return join(getAddonsDir(), addonName, subDir);
50
+ };
51
+
52
+ /**
53
+ * 检查 addon 是否存在指定子目录
54
+ * @param addonName - addon 名称
55
+ * @param subDir - 子目录名称
56
+ */
57
+ export const hasAddonDir = (addonName: string, subDir: string): boolean => {
58
+ const dir = getAddonDir(addonName, subDir);
59
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
60
+ };
package/utils/api.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * API 工具类 - TypeScript 版本
3
+ * 提供 API 路由定义的便捷方法
4
+ */
5
+
6
+ import type { ApiRoute, ApiOptions } from '../types/api.js';
7
+
8
+ /**
9
+ * 定义 API 路由(主函数)
10
+ * @param name - 接口名称
11
+ * @param options - 接口配置选项
12
+ * @returns API 路由定义
13
+ */
14
+ export function Api(name: string, options: ApiOptions): ApiRoute {
15
+ return {
16
+ method: options.method || 'POST',
17
+ name: name,
18
+ auth: options.auth ?? false,
19
+ fields: options.fields ?? {},
20
+ required: options.required ?? [],
21
+ handler: async (befly, ctx, req) => await options.handler(befly, ctx, req)
22
+ };
23
+ }
@@ -1,19 +1,68 @@
1
- const p = process || {},
2
- argv = p.argv || [],
3
- env = p.env || {};
1
+ /**
2
+ * 终端颜色工具 - TypeScript 版本
3
+ */
4
+
5
+ /**
6
+ * 格式化函数类型
7
+ */
8
+ type Formatter = (input: string | number) => string;
9
+
10
+ /**
11
+ * 颜色工具接口
12
+ */
13
+ interface ColorsInterface {
14
+ isColorSupported: boolean;
15
+ reset: Formatter;
16
+ bold: Formatter;
17
+ dim: Formatter;
18
+ italic: Formatter;
19
+ underline: Formatter;
20
+ inverse: Formatter;
21
+ hidden: Formatter;
22
+ strikethrough: Formatter;
23
+ black: Formatter;
24
+ red: Formatter;
25
+ green: Formatter;
26
+ yellow: Formatter;
27
+ blue: Formatter;
28
+ magenta: Formatter;
29
+ cyan: Formatter;
30
+ white: Formatter;
31
+ gray: Formatter;
32
+ bgBlack: Formatter;
33
+ bgRed: Formatter;
34
+ bgGreen: Formatter;
35
+ bgYellow: Formatter;
36
+ bgBlue: Formatter;
37
+ bgMagenta: Formatter;
38
+ bgCyan: Formatter;
39
+ bgWhite: Formatter;
40
+ }
41
+
42
+ interface Process {
43
+ argv?: string[];
44
+ env?: Record<string, string | undefined>;
45
+ platform?: string;
46
+ stdout?: { isTTY?: boolean };
47
+ }
48
+
49
+ const p: Process = (typeof process !== 'undefined' ? process : {}) as Process;
50
+ const argv = p.argv || [];
51
+ const env = p.env || {};
52
+
4
53
  const isColorSupported = !(!!env.NO_COLOR || argv.includes('--no-color')) && (!!env.FORCE_COLOR || argv.includes('--color') || p.platform === 'win32' || ((p.stdout || {}).isTTY && env.TERM !== 'dumb') || !!env.CI);
5
54
 
6
55
  const formatter =
7
- (open, close, replace = open) =>
8
- (input) => {
9
- let string = '' + input,
10
- index = string.indexOf(close, open.length);
56
+ (open: string, close: string, replace: string = open): Formatter =>
57
+ (input: string | number): string => {
58
+ const string = '' + input;
59
+ const index = string.indexOf(close, open.length);
11
60
  return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
12
61
  };
13
62
 
14
- const replaceClose = (string, close, replace, index) => {
15
- let result = '',
16
- cursor = 0;
63
+ const replaceClose = (string: string, close: string, replace: string, index: number): string => {
64
+ let result = '';
65
+ let cursor = 0;
17
66
  do {
18
67
  result += string.substring(cursor, index) + replace;
19
68
  cursor = index + close.length;
@@ -22,9 +71,10 @@ const replaceClose = (string, close, replace, index) => {
22
71
  return result + string.substring(cursor);
23
72
  };
24
73
 
25
- const createColors = (enabled = isColorSupported) => {
26
- let f = enabled ? formatter : () => String;
27
- return {
74
+ const createColors = (enabled: boolean = isColorSupported): ColorsInterface => {
75
+ const f = enabled ? formatter : (): Formatter => String as Formatter;
76
+
77
+ const baseColors = {
28
78
  isColorSupported: enabled,
29
79
  reset: f('\x1b[0m', '\x1b[0m'),
30
80
  bold: f('\x1b[1m', '\x1b[22m', '\x1b[22m\x1b[1m'),
@@ -70,14 +120,22 @@ const createColors = (enabled = isColorSupported) => {
70
120
  bgBlueBright: f('\x1b[104m', '\x1b[49m'),
71
121
  bgMagentaBright: f('\x1b[105m', '\x1b[49m'),
72
122
  bgCyanBright: f('\x1b[106m', '\x1b[49m'),
73
- bgWhiteBright: f('\x1b[107m', '\x1b[49m')
123
+ bgWhiteBright: f('\x1b[107m', '\x1b[49m'),
124
+
125
+ info: '',
126
+ success: '',
127
+ warn: '',
128
+ error: ''
74
129
  };
75
- };
76
130
 
77
- const colors = createColors();
78
- colors.info = colors.blue('i');
79
- colors.success = colors.green('√');
80
- colors.warn = colors.yellow('‼');
81
- colors.error = colors.red('x');
131
+ // 添加图标
132
+ baseColors.info = baseColors.blue('i');
133
+ baseColors.success = baseColors.green('√');
134
+ baseColors.warn = baseColors.yellow('‼');
135
+ baseColors.error = baseColors.red('x');
136
+
137
+ return baseColors;
138
+ };
82
139
 
83
- export { colors };
140
+ export const colors = createColors();
141
+ export { colors as Colors }; // 别名导出,保持向后兼容
@@ -0,0 +1,308 @@
1
+ /**
2
+ * 加密工具类 - TypeScript 版本
3
+ * 提供各种哈希、HMAC、密码加密等功能
4
+ */
5
+
6
+ import { createSign } from 'node:crypto';
7
+ import type { EncodingType, HashAlgorithm, PasswordHashOptions } from '../types/crypto';
8
+
9
+ /**
10
+ * 加密工具类
11
+ */
12
+ export class Crypto2 {
13
+ /**
14
+ * MD5 哈希
15
+ * @param data - 要哈希的数据
16
+ * @param encoding - 输出编码
17
+ * @returns MD5 哈希值
18
+ */
19
+ static md5(data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
20
+ const hasher = new Bun.CryptoHasher('md5');
21
+ hasher.update(data);
22
+ return hasher.digest(encoding);
23
+ }
24
+
25
+ /**
26
+ * HMAC-MD5 签名
27
+ * @param key - 密钥
28
+ * @param data - 要签名的数据
29
+ * @param encoding - 输出编码
30
+ * @returns HMAC-MD5 签名
31
+ */
32
+ static hmacMd5(key: string | Uint8Array, data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
33
+ const hasher = new Bun.CryptoHasher('md5', key);
34
+ hasher.update(data);
35
+ return hasher.digest(encoding);
36
+ }
37
+
38
+ /**
39
+ * SHA-1 哈希
40
+ * @param data - 要哈希的数据
41
+ * @param encoding - 输出编码
42
+ * @returns SHA-1 哈希值
43
+ */
44
+ static sha1(data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
45
+ const hasher = new Bun.CryptoHasher('sha1');
46
+ hasher.update(data);
47
+ return hasher.digest(encoding);
48
+ }
49
+
50
+ /**
51
+ * HMAC-SHA1 签名
52
+ * @param key - 密钥
53
+ * @param data - 要签名的数据
54
+ * @param encoding - 输出编码
55
+ * @returns HMAC-SHA1 签名
56
+ */
57
+ static hmacSha1(key: string | Uint8Array, data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
58
+ const hasher = new Bun.CryptoHasher('sha1', key);
59
+ hasher.update(data);
60
+ return hasher.digest(encoding);
61
+ }
62
+
63
+ /**
64
+ * SHA-256 哈希
65
+ * @param data - 要哈希的数据
66
+ * @param encoding - 输出编码
67
+ * @returns SHA-256 哈希值
68
+ */
69
+ static sha256(data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
70
+ const hasher = new Bun.CryptoHasher('sha256');
71
+ hasher.update(data);
72
+ return hasher.digest(encoding);
73
+ }
74
+
75
+ /**
76
+ * RSA-SHA256 签名
77
+ * @param data - 要签名的数据
78
+ * @param privateKey - 私钥
79
+ * @param encoding - 输出编码
80
+ * @returns RSA-SHA256 签名
81
+ */
82
+ static rsaSha256(data: string, privateKey: string | Buffer, encoding: BufferEncoding = 'hex'): string {
83
+ const sign = createSign('RSA-SHA256');
84
+ sign.update(data);
85
+ const signature = sign.sign(privateKey, encoding);
86
+ return signature;
87
+ }
88
+
89
+ /**
90
+ * HMAC-SHA256 签名
91
+ * @param key - 密钥
92
+ * @param data - 要签名的数据
93
+ * @param encoding - 输出编码
94
+ * @returns HMAC-SHA256 签名
95
+ */
96
+ static hmacSha256(key: string | Uint8Array, data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
97
+ const hasher = new Bun.CryptoHasher('sha256', key);
98
+ hasher.update(data);
99
+ return hasher.digest(encoding);
100
+ }
101
+
102
+ /**
103
+ * SHA-512 哈希
104
+ * @param data - 要哈希的数据
105
+ * @param encoding - 输出编码
106
+ * @returns SHA-512 哈希值
107
+ */
108
+ static sha512(data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
109
+ const hasher = new Bun.CryptoHasher('sha512');
110
+ hasher.update(data);
111
+ return hasher.digest(encoding);
112
+ }
113
+
114
+ /**
115
+ * HMAC-SHA512 签名
116
+ * @param key - 密钥
117
+ * @param data - 要签名的数据
118
+ * @param encoding - 输出编码
119
+ * @returns HMAC-SHA512 签名
120
+ */
121
+ static hmacSha512(key: string | Uint8Array, data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
122
+ const hasher = new Bun.CryptoHasher('sha512', key);
123
+ hasher.update(data);
124
+ return hasher.digest(encoding);
125
+ }
126
+
127
+ /**
128
+ * 通用哈希方法
129
+ * @param algorithm - 算法名称
130
+ * @param data - 要哈希的数据
131
+ * @param encoding - 输出编码
132
+ * @returns 哈希值
133
+ */
134
+ static hash(algorithm: HashAlgorithm, data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
135
+ const hasher = new Bun.CryptoHasher(algorithm);
136
+ hasher.update(data);
137
+ return hasher.digest(encoding);
138
+ }
139
+
140
+ /**
141
+ * 通用 HMAC 方法
142
+ * @param algorithm - 算法名称
143
+ * @param key - 密钥
144
+ * @param data - 要签名的数据
145
+ * @param encoding - 输出编码
146
+ * @returns HMAC 签名
147
+ */
148
+ static hmac(algorithm: HashAlgorithm, key: string | Uint8Array, data: string | Uint8Array, encoding: EncodingType = 'hex'): string {
149
+ const hasher = new Bun.CryptoHasher(algorithm, key);
150
+ hasher.update(data);
151
+ return hasher.digest(encoding);
152
+ }
153
+
154
+ /**
155
+ * 文件哈希
156
+ * @param filePath - 文件路径
157
+ * @param algorithm - 算法名称
158
+ * @param encoding - 输出编码
159
+ * @returns 文件哈希值
160
+ */
161
+ static async hashFile(filePath: string, algorithm: HashAlgorithm = 'sha256', encoding: EncodingType = 'hex'): Promise<string> {
162
+ const file = Bun.file(filePath);
163
+ const hasher = new Bun.CryptoHasher(algorithm);
164
+
165
+ const stream = file.stream();
166
+ const reader = stream.getReader();
167
+
168
+ try {
169
+ while (true) {
170
+ const { done, value } = await reader.read();
171
+ if (done) break;
172
+ hasher.update(value);
173
+ }
174
+ return hasher.digest(encoding);
175
+ } finally {
176
+ reader.releaseLock();
177
+ }
178
+ }
179
+
180
+ /**
181
+ * 创建流式哈希器
182
+ * @param algorithm - 算法名称
183
+ * @param key - 可选的 HMAC 密钥
184
+ * @returns 流式哈希器实例
185
+ */
186
+ static createHasher(algorithm: HashAlgorithm, key: string | Uint8Array | null = null): StreamHasher {
187
+ return new StreamHasher(algorithm, key);
188
+ }
189
+
190
+ /**
191
+ * 密码哈希 (使用 Argon2)
192
+ * @param password - 密码
193
+ * @param options - 选项
194
+ * @returns 哈希后的密码
195
+ */
196
+ static async hashPassword(password: string, options: PasswordHashOptions = {}): Promise<string> {
197
+ // 设置默认算法为 bcrypt
198
+ const finalOptions = {
199
+ algorithm: 'bcrypt',
200
+ ...options
201
+ } as any;
202
+ return await Bun.password.hash(password, finalOptions);
203
+ }
204
+
205
+ /**
206
+ * 验证密码
207
+ * @param password - 原始密码
208
+ * @param hash - 哈希值
209
+ * @returns 验证结果
210
+ */
211
+ static async verifyPassword(password: string, hash: string): Promise<boolean> {
212
+ return await Bun.password.verify(password, hash);
213
+ }
214
+
215
+ /**
216
+ * Base64 编码
217
+ * @param data - 要编码的数据
218
+ * @returns Base64 编码的字符串
219
+ */
220
+ static base64Encode(data: string): string {
221
+ return Buffer.from(data, 'utf8').toString('base64');
222
+ }
223
+
224
+ /**
225
+ * Base64 解码
226
+ * @param data - Base64 编码的字符串
227
+ * @returns 解码后的字符串
228
+ */
229
+ static base64Decode(data: string): string {
230
+ return Buffer.from(data, 'base64').toString('utf8');
231
+ }
232
+
233
+ /**
234
+ * 生成随机十六进制字符串
235
+ * @param length - 字符串长度
236
+ * @returns 随机十六进制字符串
237
+ */
238
+ static randomString(length: number): string {
239
+ const bytes = Math.ceil(length / 2);
240
+ const randomBytes = crypto.getRandomValues(new Uint8Array(bytes));
241
+ let result = '';
242
+ for (let i = 0; i < randomBytes.length; i++) {
243
+ result += randomBytes[i].toString(16).padStart(2, '0');
244
+ }
245
+ return result.slice(0, length);
246
+ }
247
+
248
+ /**
249
+ * 快速哈希 (非密码学)
250
+ * @param data - 数据
251
+ * @param seed - 种子值
252
+ * @returns 64位哈希值
253
+ */
254
+ static fastHash(data: string | Uint8Array, seed: number = 0): number {
255
+ return Bun.hash(data, seed);
256
+ }
257
+ }
258
+
259
+ /**
260
+ * 流式哈希器类
261
+ */
262
+ export class StreamHasher {
263
+ private hasher: any;
264
+ private finalized: boolean = false;
265
+
266
+ constructor(algorithm: HashAlgorithm, key: string | Uint8Array | null = null) {
267
+ this.hasher = new Bun.CryptoHasher(algorithm, key);
268
+ }
269
+
270
+ /**
271
+ * 更新数据
272
+ * @param data - 数据
273
+ * @returns 支持链式调用
274
+ */
275
+ update(data: string | Uint8Array): this {
276
+ if (this.finalized) {
277
+ throw new Error('哈希器已经完成,不能再更新数据');
278
+ }
279
+ this.hasher.update(data);
280
+ return this;
281
+ }
282
+
283
+ /**
284
+ * 生成最终哈希值
285
+ * @param encoding - 输出编码
286
+ * @returns 哈希值
287
+ */
288
+ digest(encoding: EncodingType = 'hex'): string {
289
+ if (this.finalized) {
290
+ throw new Error('哈希器已经完成');
291
+ }
292
+ this.finalized = true;
293
+ return this.hasher.digest(encoding);
294
+ }
295
+
296
+ /**
297
+ * 复制哈希器
298
+ * @returns 新的哈希器实例
299
+ */
300
+ copy(): StreamHasher {
301
+ if (this.finalized) {
302
+ throw new Error('不能复制已完成的哈希器');
303
+ }
304
+ const newHasher = new StreamHasher('md5'); // 临时算法
305
+ newHasher.hasher = this.hasher.copy();
306
+ return newHasher;
307
+ }
308
+ }