@qhr123/sa2kit 0.1.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.
- package/LICENSE +22 -0
- package/README.md +113 -0
- package/dist/chunk-3R73CUAY.js +80 -0
- package/dist/chunk-3R73CUAY.js.map +1 -0
- package/dist/chunk-4JTIYLS6.js +88 -0
- package/dist/chunk-4JTIYLS6.js.map +1 -0
- package/dist/chunk-6PRFP5EG.js +171 -0
- package/dist/chunk-6PRFP5EG.js.map +1 -0
- package/dist/chunk-C7VOMO3L.mjs +77 -0
- package/dist/chunk-C7VOMO3L.mjs.map +1 -0
- package/dist/chunk-KQGP6BTS.mjs +165 -0
- package/dist/chunk-KQGP6BTS.mjs.map +1 -0
- package/dist/chunk-M7CA3DTF.mjs +86 -0
- package/dist/chunk-M7CA3DTF.mjs.map +1 -0
- package/dist/chunk-WFWG4UZF.mjs +334 -0
- package/dist/chunk-WFWG4UZF.mjs.map +1 -0
- package/dist/chunk-WO3H4BMN.js +342 -0
- package/dist/chunk-WO3H4BMN.js.map +1 -0
- package/dist/hooks/index.d.mts +30 -0
- package/dist/hooks/index.d.ts +30 -0
- package/dist/hooks/index.js +17 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +4 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -0
- package/dist/logger/index.d.mts +125 -0
- package/dist/logger/index.d.ts +125 -0
- package/dist/logger/index.js +28 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/index.mjs +3 -0
- package/dist/logger/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +20 -0
- package/dist/storage/index.d.ts +20 -0
- package/dist/storage/index.js +12 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +3 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/types-BaZccpvk.d.mts +48 -0
- package/dist/types-BaZccpvk.d.ts +48 -0
- package/dist/utils/index.d.mts +170 -0
- package/dist/utils/index.d.ts +170 -0
- package/dist/utils/index.js +37 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +4 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/logger/types.ts
|
|
2
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
3
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
4
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
5
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
6
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
7
|
+
LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
|
|
8
|
+
return LogLevel2;
|
|
9
|
+
})(LogLevel || {});
|
|
10
|
+
|
|
11
|
+
// src/logger/console-adapter.ts
|
|
12
|
+
var ConsoleLoggerAdapter = class {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.colors = {
|
|
15
|
+
DEBUG: "\x1B[36m",
|
|
16
|
+
// Cyan
|
|
17
|
+
INFO: "\x1B[32m",
|
|
18
|
+
// Green
|
|
19
|
+
WARN: "\x1B[33m",
|
|
20
|
+
// Yellow
|
|
21
|
+
ERROR: "\x1B[31m",
|
|
22
|
+
// Red
|
|
23
|
+
RESET: "\x1B[0m"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
log(entry) {
|
|
27
|
+
const { level, message, timestamp, data, context, error } = entry;
|
|
28
|
+
let logMessage = "";
|
|
29
|
+
if (timestamp) {
|
|
30
|
+
logMessage += `[${this.formatTimestamp(timestamp)}] `;
|
|
31
|
+
}
|
|
32
|
+
const levelName = this.getLevelName(level);
|
|
33
|
+
logMessage += `${levelName}: `;
|
|
34
|
+
if (context) {
|
|
35
|
+
logMessage += `[${context}] `;
|
|
36
|
+
}
|
|
37
|
+
logMessage += message;
|
|
38
|
+
switch (level) {
|
|
39
|
+
case 0:
|
|
40
|
+
console.debug(this.colorize(logMessage, "DEBUG"), data || "");
|
|
41
|
+
break;
|
|
42
|
+
case 1:
|
|
43
|
+
console.info(this.colorize(logMessage, "INFO"), data || "");
|
|
44
|
+
break;
|
|
45
|
+
case 2:
|
|
46
|
+
console.warn(this.colorize(logMessage, "WARN"), data || "");
|
|
47
|
+
break;
|
|
48
|
+
case 3:
|
|
49
|
+
console.error(this.colorize(logMessage, "ERROR"), data || "");
|
|
50
|
+
if (error) {
|
|
51
|
+
console.error(error);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
formatTimestamp(date) {
|
|
57
|
+
return date.toISOString();
|
|
58
|
+
}
|
|
59
|
+
getLevelName(level) {
|
|
60
|
+
const names = ["DEBUG", "INFO", "WARN", "ERROR", "NONE"];
|
|
61
|
+
return names[level] || "UNKNOWN";
|
|
62
|
+
}
|
|
63
|
+
colorize(message, level) {
|
|
64
|
+
if (typeof process !== "undefined" && process.stdout?.isTTY) {
|
|
65
|
+
return `${this.colors[level]}${message}${this.colors.RESET}`;
|
|
66
|
+
}
|
|
67
|
+
return message;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/logger/Logger.ts
|
|
72
|
+
var Logger = class _Logger {
|
|
73
|
+
constructor(config, context) {
|
|
74
|
+
const isProduction = typeof process !== "undefined" ? process.env.NODE_ENV === "production" : false;
|
|
75
|
+
this.config = {
|
|
76
|
+
minLevel: config?.minLevel ?? (isProduction ? 1 : 0),
|
|
77
|
+
// INFO in prod, DEBUG in dev
|
|
78
|
+
enableTimestamp: config?.enableTimestamp ?? true,
|
|
79
|
+
enableContext: config?.enableContext ?? true,
|
|
80
|
+
environment: config?.environment ?? (isProduction ? "production" : "development"),
|
|
81
|
+
adapter: config?.adapter ?? new ConsoleLoggerAdapter()
|
|
82
|
+
};
|
|
83
|
+
this.adapter = this.config.adapter;
|
|
84
|
+
this.context = context;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 创建带上下文的子 Logger
|
|
88
|
+
*/
|
|
89
|
+
createChild(context) {
|
|
90
|
+
return new _Logger(this.config, context);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 调试日志
|
|
94
|
+
*/
|
|
95
|
+
debug(message, data) {
|
|
96
|
+
this.log(0, message, data);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 信息日志
|
|
100
|
+
*/
|
|
101
|
+
info(message, data) {
|
|
102
|
+
this.log(1, message, data);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 警告日志
|
|
106
|
+
*/
|
|
107
|
+
warn(message, data) {
|
|
108
|
+
this.log(2, message, data);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 错误日志
|
|
112
|
+
*/
|
|
113
|
+
error(message, error) {
|
|
114
|
+
this.log(
|
|
115
|
+
3,
|
|
116
|
+
// LogLevel.ERROR
|
|
117
|
+
message,
|
|
118
|
+
error instanceof Error ? void 0 : error,
|
|
119
|
+
error instanceof Error ? error : void 0
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 核心日志方法
|
|
124
|
+
*/
|
|
125
|
+
log(level, message, data, error) {
|
|
126
|
+
if (level < this.config.minLevel) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
|
|
130
|
+
const loggerDebug = localStorage.getItem("logger-debug");
|
|
131
|
+
if (loggerDebug === "false" && level < 3) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const entry = {
|
|
136
|
+
level,
|
|
137
|
+
message,
|
|
138
|
+
timestamp: this.config.enableTimestamp ? /* @__PURE__ */ new Date() : void 0,
|
|
139
|
+
data,
|
|
140
|
+
context: this.config.enableContext ? this.context : void 0,
|
|
141
|
+
error
|
|
142
|
+
};
|
|
143
|
+
this.adapter.log(entry);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 设置日志级别
|
|
147
|
+
*/
|
|
148
|
+
setLevel(level) {
|
|
149
|
+
this.config.minLevel = level;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 获取当前日志级别
|
|
153
|
+
*/
|
|
154
|
+
getLevel() {
|
|
155
|
+
return this.config.minLevel;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var logger = new Logger();
|
|
159
|
+
function createLogger(context, config) {
|
|
160
|
+
return new Logger(config, context);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export { ConsoleLoggerAdapter, LogLevel, Logger, createLogger, logger };
|
|
164
|
+
//# sourceMappingURL=chunk-KQGP6BTS.mjs.map
|
|
165
|
+
//# sourceMappingURL=chunk-KQGP6BTS.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logger/types.ts","../src/logger/console-adapter.ts","../src/logger/Logger.ts"],"names":["LogLevel"],"mappings":"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","file":"chunk-KQGP6BTS.mjs","sourcesContent":["/**\n * 日志级别\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4, // 禁用所有日志\n}\n\n/**\n * 日志条目\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: Date;\n data?: any;\n context?: string;\n error?: Error;\n}\n\n/**\n * 日志适配器接口\n * 不同平台实现不同的日志输出方式\n */\nexport interface LoggerAdapter {\n /**\n * 输出日志\n */\n log(entry: LogEntry): void;\n\n /**\n * 批量输出日志(可选)\n */\n logBatch?(entries: LogEntry[]): void;\n}\n\n/**\n * 日志配置\n */\nexport interface LoggerConfig {\n /**\n * 最小日志级别\n * 只有大于等于此级别的日志才会输出\n */\n minLevel: LogLevel;\n\n /**\n * 是否启用时间戳\n */\n enableTimestamp?: boolean;\n\n /**\n * 是否启用上下文(模块名)\n */\n enableContext?: boolean;\n\n /**\n * 环境(development/production)\n */\n environment?: 'development' | 'production';\n\n /**\n * 自定义适配器\n */\n adapter?: LoggerAdapter;\n}\n\n","import type { LoggerAdapter, LogEntry, LogLevel } from './types';\n\n/**\n * 控制台日志适配器\n * 使用 console.info/warn/error 输出日志\n */\nexport class ConsoleLoggerAdapter implements LoggerAdapter {\n private readonly colors = {\n DEBUG: '\\x1b[36m', // Cyan\n INFO: '\\x1b[32m', // Green\n WARN: '\\x1b[33m', // Yellow\n ERROR: '\\x1b[31m', // Red\n RESET: '\\x1b[0m',\n };\n\n log(entry: LogEntry): void {\n const { level, message, timestamp, data, context, error } = entry;\n\n // 构建日志消息\n let logMessage = '';\n\n // 添加时间戳\n if (timestamp) {\n logMessage += `[${this.formatTimestamp(timestamp)}] `;\n }\n\n // 添加日志级别\n const levelName = this.getLevelName(level);\n logMessage += `${levelName}: `;\n\n // 添加上下文\n if (context) {\n logMessage += `[${context}] `;\n }\n\n // 添加消息\n logMessage += message;\n\n // 根据日志级别选择输出方式\n switch (level) {\n case 0: // DEBUG\n console.debug(this.colorize(logMessage, 'DEBUG'), data || '');\n break;\n case 1: // INFO\n console.info(this.colorize(logMessage, 'INFO'), data || '');\n break;\n case 2: // WARN\n console.warn(this.colorize(logMessage, 'WARN'), data || '');\n break;\n case 3: // ERROR\n console.error(this.colorize(logMessage, 'ERROR'), data || '');\n if (error) {\n console.error(error);\n }\n break;\n }\n }\n\n private formatTimestamp(date: Date): string {\n return date.toISOString();\n }\n\n private getLevelName(level: LogLevel): string {\n const names = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE'];\n return names[level] || 'UNKNOWN';\n }\n\n private colorize(message: string, level: keyof typeof this.colors): string {\n // 只在支持颜色的环境中使用颜色\n if (typeof process !== 'undefined' && process.stdout?.isTTY) {\n return `${this.colors[level]}${message}${this.colors.RESET}`;\n }\n return message;\n }\n}\n\n","import type { LogLevel, LogEntry, LoggerConfig, LoggerAdapter } from './types';\nimport { ConsoleLoggerAdapter } from './console-adapter';\n\n/**\n * 统一日志管理类\n */\nexport class Logger {\n private config: Required<LoggerConfig>;\n private adapter: LoggerAdapter;\n private context?: string;\n\n constructor(config?: Partial<LoggerConfig>, context?: string) {\n const isProduction =\n typeof process !== 'undefined' ? process.env.NODE_ENV === 'production' : false;\n\n this.config = {\n minLevel: config?.minLevel ?? (isProduction ? 1 : 0), // INFO in prod, DEBUG in dev\n enableTimestamp: config?.enableTimestamp ?? true,\n enableContext: config?.enableContext ?? true,\n environment: config?.environment ?? (isProduction ? 'production' : 'development'),\n adapter: config?.adapter ?? new ConsoleLoggerAdapter(),\n };\n this.adapter = this.config.adapter;\n this.context = context;\n }\n\n /**\n * 创建带上下文的子 Logger\n */\n createChild(context: string): Logger {\n return new Logger(this.config, context);\n }\n\n /**\n * 调试日志\n */\n debug(message: string, data?: any): void {\n this.log(0, message, data); // LogLevel.DEBUG\n }\n\n /**\n * 信息日志\n */\n info(message: string, data?: any): void {\n this.log(1, message, data); // LogLevel.INFO\n }\n\n /**\n * 警告日志\n */\n warn(message: string, data?: any): void {\n this.log(2, message, data); // LogLevel.WARN\n }\n\n /**\n * 错误日志\n */\n error(message: string, error?: Error | any): void {\n this.log(\n 3, // LogLevel.ERROR\n message,\n error instanceof Error ? undefined : error,\n error instanceof Error ? error : undefined\n );\n }\n\n /**\n * 核心日志方法\n */\n private log(level: LogLevel, message: string, data?: any, error?: Error): void {\n // 检查日志级别\n if (level < this.config.minLevel) {\n return;\n }\n\n // 检查动态调试配置(仅在浏览器环境)\n if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {\n const loggerDebug = localStorage.getItem('logger-debug');\n // 如果明确设置为 false,则不输出(但 Error 级别始终输出)\n if (loggerDebug === 'false' && level < 3) {\n // level < ERROR\n return;\n }\n }\n\n const entry: LogEntry = {\n level,\n message,\n timestamp: this.config.enableTimestamp ? new Date() : (undefined as any),\n data,\n context: this.config.enableContext ? this.context : undefined,\n error,\n };\n\n this.adapter.log(entry);\n }\n\n /**\n * 设置日志级别\n */\n setLevel(level: LogLevel): void {\n this.config.minLevel = level;\n }\n\n /**\n * 获取当前日志级别\n */\n getLevel(): LogLevel {\n return this.config.minLevel;\n }\n}\n\n/**\n * 默认全局 Logger 实例\n */\nexport const logger = new Logger();\n\n/**\n * 创建带上下文的 Logger\n */\nexport function createLogger(context: string, config?: Partial<LoggerConfig>): Logger {\n return new Logger(config, context);\n}\n\n"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/storage/web-adapter.ts
|
|
2
|
+
var isBrowser = typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
|
3
|
+
var WebStorageAdapter = class {
|
|
4
|
+
async getItem(key) {
|
|
5
|
+
if (!isBrowser) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
return localStorage.getItem(key);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.error(`[WebStorage] Error getting item "${key}":`, error);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async setItem(key, value) {
|
|
16
|
+
if (!isBrowser) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
localStorage.setItem(key, value);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error(`[WebStorage] Error setting item "${key}":`, error);
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async removeItem(key) {
|
|
27
|
+
if (!isBrowser) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
localStorage.removeItem(key);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`[WebStorage] Error removing item "${key}":`, error);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async clear() {
|
|
38
|
+
if (!isBrowser) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
localStorage.clear();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error("[WebStorage] Error clearing storage:", error);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
addChangeListener(callback) {
|
|
49
|
+
if (!isBrowser) {
|
|
50
|
+
return () => {
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const handleStorageEvent = (e) => {
|
|
54
|
+
if (e.key) {
|
|
55
|
+
callback(e.key, e.newValue);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const handleCustomEvent = (e) => {
|
|
59
|
+
const customEvent = e;
|
|
60
|
+
callback(customEvent.detail.key, customEvent.detail.value);
|
|
61
|
+
};
|
|
62
|
+
window.addEventListener("storage", handleStorageEvent);
|
|
63
|
+
window.addEventListener("local-storage-change", handleCustomEvent);
|
|
64
|
+
return () => {
|
|
65
|
+
window.removeEventListener("storage", handleStorageEvent);
|
|
66
|
+
window.removeEventListener("local-storage-change", handleCustomEvent);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 触发自定义事件,通知同标签页的其他组件
|
|
71
|
+
*/
|
|
72
|
+
dispatchChange(key, value) {
|
|
73
|
+
if (!isBrowser) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
window.dispatchEvent(
|
|
77
|
+
new CustomEvent("local-storage-change", {
|
|
78
|
+
detail: { key, value }
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export { WebStorageAdapter };
|
|
85
|
+
//# sourceMappingURL=chunk-M7CA3DTF.mjs.map
|
|
86
|
+
//# sourceMappingURL=chunk-M7CA3DTF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/web-adapter.ts"],"names":[],"mappings":";AASA,IAAM,YAAY,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,OAAO,YAAA,KAAiB,WAAA;AAE3E,IAAM,oBAAN,MAAkD;AAAA,EACvD,MAAM,QAAQ,GAAA,EAAqC;AAEjD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,OAAO,YAAA,CAAa,QAAQ,GAAG,CAAA;AAAA,IACjC,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,iCAAA,EAAoC,GAAG,CAAA,EAAA,CAAA,EAAM,KAAK,CAAA;AAChE,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CAAQ,GAAA,EAAa,KAAA,EAA8B;AAEvD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,YAAA,CAAa,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,IACjC,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,iCAAA,EAAoC,GAAG,CAAA,EAAA,CAAA,EAAM,KAAK,CAAA;AAChE,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,GAAA,EAA4B;AAE3C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,YAAA,CAAa,WAAW,GAAG,CAAA;AAAA,IAC7B,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,kCAAA,EAAqC,GAAG,CAAA,EAAA,CAAA,EAAM,KAAK,CAAA;AACjE,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,KAAA,GAAuB;AAE3B,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,YAAA,CAAa,KAAA,EAAM;AAAA,IACrB,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,wCAAwC,KAAK,CAAA;AAC3D,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEA,kBAAkB,QAAA,EAAmE;AAEnF,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB;AAGA,IAAA,MAAM,kBAAA,GAAqB,CAAC,CAAA,KAAoB;AAC9C,MAAA,IAAI,EAAE,GAAA,EAAK;AACT,QAAA,QAAA,CAAS,CAAA,CAAE,GAAA,EAAK,CAAA,CAAE,QAAQ,CAAA;AAAA,MAC5B;AAAA,IACF,CAAA;AAGA,IAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAAa;AACtC,MAAA,MAAM,WAAA,GAAc,CAAA;AACpB,MAAA,QAAA,CAAS,WAAA,CAAY,MAAA,CAAO,GAAA,EAAK,WAAA,CAAY,OAAO,KAAK,CAAA;AAAA,IAC3D,CAAA;AAEA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,kBAAkB,CAAA;AACrD,IAAA,MAAA,CAAO,gBAAA,CAAiB,wBAAwB,iBAAiB,CAAA;AAGjE,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,kBAAkB,CAAA;AACxD,MAAA,MAAA,CAAO,mBAAA,CAAoB,wBAAwB,iBAAiB,CAAA;AAAA,IACtE,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAA,CAAe,KAAa,KAAA,EAA4B;AAEtD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AAEA,IAAA,MAAA,CAAO,aAAA;AAAA,MACL,IAAI,YAAY,sBAAA,EAAwB;AAAA,QACtC,MAAA,EAAQ,EAAE,GAAA,EAAK,KAAA;AAAM,OACtB;AAAA,KACH;AAAA,EACF;AACF","file":"chunk-M7CA3DTF.mjs","sourcesContent":["/**\n * Web 平台存储适配器 (localStorage)\n */\n\nimport type { StorageAdapter } from './types';\n\n/**\n * 检查是否在浏览器环境中\n */\nconst isBrowser = typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';\n\nexport class WebStorageAdapter implements StorageAdapter {\n async getItem(key: string): Promise<string | null> {\n // SSR 环境下返回 null\n if (!isBrowser) {\n return null;\n }\n\n try {\n return localStorage.getItem(key);\n } catch (error) {\n console.error(`[WebStorage] Error getting item \"${key}\":`, error);\n return null;\n }\n }\n\n async setItem(key: string, value: string): Promise<void> {\n // SSR 环境下静默忽略\n if (!isBrowser) {\n return;\n }\n\n try {\n localStorage.setItem(key, value);\n } catch (error) {\n console.error(`[WebStorage] Error setting item \"${key}\":`, error);\n throw error;\n }\n }\n\n async removeItem(key: string): Promise<void> {\n // SSR 环境下静默忽略\n if (!isBrowser) {\n return;\n }\n\n try {\n localStorage.removeItem(key);\n } catch (error) {\n console.error(`[WebStorage] Error removing item \"${key}\":`, error);\n throw error;\n }\n }\n\n async clear(): Promise<void> {\n // SSR 环境下静默忽略\n if (!isBrowser) {\n return;\n }\n\n try {\n localStorage.clear();\n } catch (error) {\n console.error('[WebStorage] Error clearing storage:', error);\n throw error;\n }\n }\n\n addChangeListener(callback: (key: string, value: string | null) => void): () => void {\n // SSR 环境下返回空函数\n if (!isBrowser) {\n return () => {};\n }\n\n // 监听 storage 事件(跨标签页)\n const handleStorageEvent = (e: StorageEvent) => {\n if (e.key) {\n callback(e.key, e.newValue);\n }\n };\n\n // 监听自定义事件(同标签页)\n const handleCustomEvent = (e: Event) => {\n const customEvent = e as CustomEvent;\n callback(customEvent.detail.key, customEvent.detail.value);\n };\n\n window.addEventListener('storage', handleStorageEvent);\n window.addEventListener('local-storage-change', handleCustomEvent);\n\n // 返回清理函数\n return () => {\n window.removeEventListener('storage', handleStorageEvent);\n window.removeEventListener('local-storage-change', handleCustomEvent);\n };\n }\n\n /**\n * 触发自定义事件,通知同标签页的其他组件\n */\n dispatchChange(key: string, value: string | null): void {\n // SSR 环境下静默忽略\n if (!isBrowser) {\n return;\n }\n\n window.dispatchEvent(\n new CustomEvent('local-storage-change', {\n detail: { key, value },\n })\n );\n }\n}\n\n"]}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { logger } from './chunk-KQGP6BTS.mjs';
|
|
2
|
+
|
|
3
|
+
// src/utils/time.ts
|
|
4
|
+
var formatTime = {
|
|
5
|
+
/**
|
|
6
|
+
* 将秒数转换为 MM:SS 格式
|
|
7
|
+
*/
|
|
8
|
+
toMinutesSeconds(seconds) {
|
|
9
|
+
const minutes = Math.floor(seconds / 60);
|
|
10
|
+
const remainingSeconds = Math.floor(seconds % 60);
|
|
11
|
+
return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
|
|
12
|
+
},
|
|
13
|
+
/**
|
|
14
|
+
* 将秒数转换为 HH:MM:SS 格式
|
|
15
|
+
*/
|
|
16
|
+
toHoursMinutesSeconds(seconds) {
|
|
17
|
+
const hours = Math.floor(seconds / 3600);
|
|
18
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
19
|
+
const remainingSeconds = Math.floor(seconds % 60);
|
|
20
|
+
if (hours > 0) {
|
|
21
|
+
return `${hours}:${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
|
|
22
|
+
}
|
|
23
|
+
return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* 格式化日期为用户友好的格式
|
|
27
|
+
*/
|
|
28
|
+
formatDate(date, locale = "zh-CN") {
|
|
29
|
+
const d = new Date(date);
|
|
30
|
+
const now = /* @__PURE__ */ new Date();
|
|
31
|
+
const diffMs = now.getTime() - d.getTime();
|
|
32
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
33
|
+
if (diffDays === 0) {
|
|
34
|
+
return locale === "zh-CN" ? "\u4ECA\u5929" : "Today";
|
|
35
|
+
} else if (diffDays === 1) {
|
|
36
|
+
return locale === "zh-CN" ? "\u6628\u5929" : "Yesterday";
|
|
37
|
+
} else if (diffDays < 7) {
|
|
38
|
+
return locale === "zh-CN" ? `${diffDays}\u5929\u524D` : `${diffDays} days ago`;
|
|
39
|
+
} else {
|
|
40
|
+
return d.toLocaleDateString(locale);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/utils/validators.ts
|
|
46
|
+
var validators = {
|
|
47
|
+
/**
|
|
48
|
+
* 验证邮箱格式
|
|
49
|
+
*/
|
|
50
|
+
isValidEmail(email) {
|
|
51
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
52
|
+
return emailRegex.test(email);
|
|
53
|
+
},
|
|
54
|
+
/**
|
|
55
|
+
* 验证密码强度
|
|
56
|
+
*/
|
|
57
|
+
isValidPassword(password) {
|
|
58
|
+
const errors = [];
|
|
59
|
+
if (password.length < 6) {
|
|
60
|
+
errors.push("Password must be at least 6 characters");
|
|
61
|
+
}
|
|
62
|
+
if (password.length > 50) {
|
|
63
|
+
errors.push("Password must not exceed 50 characters");
|
|
64
|
+
}
|
|
65
|
+
if (!/[a-zA-Z]/.test(password)) {
|
|
66
|
+
errors.push("Password must contain at least one letter");
|
|
67
|
+
}
|
|
68
|
+
if (!/[0-9]/.test(password)) {
|
|
69
|
+
errors.push("Password must contain at least one number");
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
isValid: errors.length === 0,
|
|
73
|
+
errors
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* 验证用户名格式
|
|
78
|
+
*/
|
|
79
|
+
isValidUsername(username) {
|
|
80
|
+
const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/;
|
|
81
|
+
return usernameRegex.test(username);
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* 验证文件大小
|
|
85
|
+
*/
|
|
86
|
+
isValidFileSize(size, maxSize) {
|
|
87
|
+
return size > 0 && size <= maxSize;
|
|
88
|
+
},
|
|
89
|
+
/**
|
|
90
|
+
* 验证文件类型
|
|
91
|
+
*/
|
|
92
|
+
isValidFileType(type, supportedTypes) {
|
|
93
|
+
return supportedTypes.includes(type);
|
|
94
|
+
},
|
|
95
|
+
/**
|
|
96
|
+
* 验证 URL 格式
|
|
97
|
+
*/
|
|
98
|
+
isValidUrl(url) {
|
|
99
|
+
try {
|
|
100
|
+
new URL(url);
|
|
101
|
+
return true;
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// src/utils/file.ts
|
|
109
|
+
var fileUtils = {
|
|
110
|
+
/**
|
|
111
|
+
* 格式化文件大小
|
|
112
|
+
*/
|
|
113
|
+
formatFileSize(bytes) {
|
|
114
|
+
if (bytes === 0) return "0 Bytes";
|
|
115
|
+
const k = 1024;
|
|
116
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
|
117
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
118
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
|
119
|
+
},
|
|
120
|
+
/**
|
|
121
|
+
* 获取文件扩展名
|
|
122
|
+
*/
|
|
123
|
+
getFileExtension(filename) {
|
|
124
|
+
return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
|
|
125
|
+
},
|
|
126
|
+
/**
|
|
127
|
+
* 生成唯一文件名
|
|
128
|
+
*/
|
|
129
|
+
generateUniqueFileName(originalName) {
|
|
130
|
+
const timestamp = Date.now();
|
|
131
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
132
|
+
const extension = this.getFileExtension(originalName);
|
|
133
|
+
const baseName = originalName.replace(`.${extension}`, "");
|
|
134
|
+
return extension ? `${baseName}_${timestamp}_${random}.${extension}` : `${baseName}_${timestamp}_${random}`;
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* 验证文件名是否有效
|
|
138
|
+
*/
|
|
139
|
+
isValidFilename(filename) {
|
|
140
|
+
const invalidChars = /[<>:"/\\|?*\x00-\x1F]/g;
|
|
141
|
+
return !invalidChars.test(filename) && filename.length > 0 && filename.length <= 255;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// src/utils/array.ts
|
|
146
|
+
var arrayUtils = {
|
|
147
|
+
/**
|
|
148
|
+
* 数组去重
|
|
149
|
+
*/
|
|
150
|
+
unique(array) {
|
|
151
|
+
return [...new Set(array)];
|
|
152
|
+
},
|
|
153
|
+
/**
|
|
154
|
+
* 数组分组
|
|
155
|
+
*/
|
|
156
|
+
groupBy(array, key) {
|
|
157
|
+
return array.reduce(
|
|
158
|
+
(groups, item) => {
|
|
159
|
+
const groupKey = String(item[key]);
|
|
160
|
+
const group = groups[groupKey] || [];
|
|
161
|
+
group.push(item);
|
|
162
|
+
groups[groupKey] = group;
|
|
163
|
+
return groups;
|
|
164
|
+
},
|
|
165
|
+
{}
|
|
166
|
+
);
|
|
167
|
+
},
|
|
168
|
+
/**
|
|
169
|
+
* 数组分页
|
|
170
|
+
*/
|
|
171
|
+
paginate(array, page, limit) {
|
|
172
|
+
const total = array.length;
|
|
173
|
+
const pages = Math.ceil(total / limit);
|
|
174
|
+
const start = (page - 1) * limit;
|
|
175
|
+
const end = start + limit;
|
|
176
|
+
const data = array.slice(start, end);
|
|
177
|
+
return {
|
|
178
|
+
data,
|
|
179
|
+
total,
|
|
180
|
+
page,
|
|
181
|
+
pages,
|
|
182
|
+
hasNext: page < pages,
|
|
183
|
+
hasPrev: page > 1
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* 数组随机排序
|
|
188
|
+
*/
|
|
189
|
+
shuffle(array) {
|
|
190
|
+
const shuffled = [...array];
|
|
191
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
192
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
193
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
194
|
+
}
|
|
195
|
+
return shuffled;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/utils/string.ts
|
|
200
|
+
var stringUtils = {
|
|
201
|
+
/**
|
|
202
|
+
* 截断文本
|
|
203
|
+
*/
|
|
204
|
+
truncate(text, length, suffix = "...") {
|
|
205
|
+
if (text.length <= length) return text;
|
|
206
|
+
return text.substring(0, length - suffix.length) + suffix;
|
|
207
|
+
},
|
|
208
|
+
/**
|
|
209
|
+
* 首字母大写
|
|
210
|
+
*/
|
|
211
|
+
capitalize(text) {
|
|
212
|
+
if (!text) return "";
|
|
213
|
+
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
|
|
214
|
+
},
|
|
215
|
+
/**
|
|
216
|
+
* 驼峰转下划线
|
|
217
|
+
*/
|
|
218
|
+
camelToSnake(text) {
|
|
219
|
+
return text.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
220
|
+
},
|
|
221
|
+
/**
|
|
222
|
+
* 下划线转驼峰
|
|
223
|
+
*/
|
|
224
|
+
snakeToCamel(text) {
|
|
225
|
+
return text.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
226
|
+
},
|
|
227
|
+
/**
|
|
228
|
+
* 生成随机字符串
|
|
229
|
+
*/
|
|
230
|
+
generateRandom(length) {
|
|
231
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
232
|
+
let result = "";
|
|
233
|
+
for (let i = 0; i < length; i++) {
|
|
234
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/utils/debug.ts
|
|
241
|
+
var debugUtils = {
|
|
242
|
+
/**
|
|
243
|
+
* 安全的 JSON 序列化
|
|
244
|
+
*/
|
|
245
|
+
safeStringify(obj) {
|
|
246
|
+
try {
|
|
247
|
+
return JSON.stringify(obj, null, 2);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
return `[Circular Reference or Invalid JSON: ${error}]`;
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
/**
|
|
253
|
+
* 性能计时器
|
|
254
|
+
*/
|
|
255
|
+
createTimer(label) {
|
|
256
|
+
const start = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
257
|
+
return {
|
|
258
|
+
end: () => {
|
|
259
|
+
const end = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
260
|
+
const duration = end - start;
|
|
261
|
+
const message = `${label || "Timer"}: ${duration.toFixed(2)}ms`;
|
|
262
|
+
logger.info(message);
|
|
263
|
+
return duration;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
},
|
|
267
|
+
/**
|
|
268
|
+
* 内存使用情况(仅在 Node.js 环境)
|
|
269
|
+
*/
|
|
270
|
+
getMemoryUsage() {
|
|
271
|
+
if (typeof process !== "undefined" && process.memoryUsage) {
|
|
272
|
+
const usage = process.memoryUsage();
|
|
273
|
+
return {
|
|
274
|
+
rss: fileUtils.formatFileSize(usage.rss),
|
|
275
|
+
heapTotal: fileUtils.formatFileSize(usage.heapTotal),
|
|
276
|
+
heapUsed: fileUtils.formatFileSize(usage.heapUsed),
|
|
277
|
+
external: fileUtils.formatFileSize(usage.external)
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/utils/error.ts
|
|
285
|
+
var errorUtils = {
|
|
286
|
+
/**
|
|
287
|
+
* 创建标准化的错误对象
|
|
288
|
+
*/
|
|
289
|
+
createError(code, message, details) {
|
|
290
|
+
const error = new Error(message);
|
|
291
|
+
error.code = code;
|
|
292
|
+
if (details) {
|
|
293
|
+
error.details = details;
|
|
294
|
+
}
|
|
295
|
+
return error;
|
|
296
|
+
},
|
|
297
|
+
/**
|
|
298
|
+
* 安全的错误信息提取
|
|
299
|
+
*/
|
|
300
|
+
extractErrorMessage(error) {
|
|
301
|
+
if (error instanceof Error) {
|
|
302
|
+
return error.message;
|
|
303
|
+
}
|
|
304
|
+
if (typeof error === "string") {
|
|
305
|
+
return error;
|
|
306
|
+
}
|
|
307
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
308
|
+
return String(error.message);
|
|
309
|
+
}
|
|
310
|
+
return "Unknown error";
|
|
311
|
+
},
|
|
312
|
+
/**
|
|
313
|
+
* 错误重试机制
|
|
314
|
+
*/
|
|
315
|
+
async retry(fn, maxAttempts = 3, delay = 1e3) {
|
|
316
|
+
let lastError;
|
|
317
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
318
|
+
try {
|
|
319
|
+
return await fn();
|
|
320
|
+
} catch (error) {
|
|
321
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
322
|
+
if (attempt === maxAttempts) {
|
|
323
|
+
throw lastError;
|
|
324
|
+
}
|
|
325
|
+
await new Promise((resolve) => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
throw lastError;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export { arrayUtils, debugUtils, errorUtils, fileUtils, formatTime, stringUtils, validators };
|
|
333
|
+
//# sourceMappingURL=chunk-WFWG4UZF.mjs.map
|
|
334
|
+
//# sourceMappingURL=chunk-WFWG4UZF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/time.ts","../src/utils/validators.ts","../src/utils/file.ts","../src/utils/array.ts","../src/utils/string.ts","../src/utils/debug.ts","../src/utils/error.ts"],"names":[],"mappings":"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","file":"chunk-WFWG4UZF.mjs","sourcesContent":["/**\n * 时间格式化工具\n */\n\nexport const formatTime = {\n /**\n * 将秒数转换为 MM:SS 格式\n */\n toMinutesSeconds(seconds: number): string {\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = Math.floor(seconds % 60);\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;\n },\n\n /**\n * 将秒数转换为 HH:MM:SS 格式\n */\n toHoursMinutesSeconds(seconds: number): string {\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const remainingSeconds = Math.floor(seconds % 60);\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;\n }\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;\n },\n\n /**\n * 格式化日期为用户友好的格式\n */\n formatDate(date: string | Date, locale = 'zh-CN'): string {\n const d = new Date(date);\n const now = new Date();\n const diffMs = now.getTime() - d.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays === 0) {\n return locale === 'zh-CN' ? '今天' : 'Today';\n } else if (diffDays === 1) {\n return locale === 'zh-CN' ? '昨天' : 'Yesterday';\n } else if (diffDays < 7) {\n return locale === 'zh-CN' ? `${diffDays}天前` : `${diffDays} days ago`;\n } else {\n return d.toLocaleDateString(locale);\n }\n },\n};\n\n","/**\n * 验证工具\n */\n\nexport const validators = {\n /**\n * 验证邮箱格式\n */\n isValidEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n },\n\n /**\n * 验证密码强度\n */\n isValidPassword(password: string): {\n isValid: boolean;\n errors: string[];\n } {\n const errors: string[] = [];\n\n if (password.length < 6) {\n errors.push('Password must be at least 6 characters');\n }\n\n if (password.length > 50) {\n errors.push('Password must not exceed 50 characters');\n }\n\n if (!/[a-zA-Z]/.test(password)) {\n errors.push('Password must contain at least one letter');\n }\n\n if (!/[0-9]/.test(password)) {\n errors.push('Password must contain at least one number');\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n },\n\n /**\n * 验证用户名格式\n */\n isValidUsername(username: string): boolean {\n const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/;\n return usernameRegex.test(username);\n },\n\n /**\n * 验证文件大小\n */\n isValidFileSize(size: number, maxSize: number): boolean {\n return size > 0 && size <= maxSize;\n },\n\n /**\n * 验证文件类型\n */\n isValidFileType(type: string, supportedTypes: string[]): boolean {\n return supportedTypes.includes(type);\n },\n\n /**\n * 验证 URL 格式\n */\n isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n },\n};\n\n","/**\n * 文件处理工具\n */\n\nexport const fileUtils = {\n /**\n * 格式化文件大小\n */\n formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n },\n\n /**\n * 获取文件扩展名\n */\n getFileExtension(filename: string): string {\n return filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);\n },\n\n /**\n * 生成唯一文件名\n */\n generateUniqueFileName(originalName: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 15);\n const extension = this.getFileExtension(originalName);\n const baseName = originalName.replace(`.${extension}`, '');\n return extension ? `${baseName}_${timestamp}_${random}.${extension}` : `${baseName}_${timestamp}_${random}`;\n },\n\n /**\n * 验证文件名是否有效\n */\n isValidFilename(filename: string): boolean {\n // 不允许包含特殊字符\n const invalidChars = /[<>:\"/\\\\|?*\\x00-\\x1F]/g;\n return !invalidChars.test(filename) && filename.length > 0 && filename.length <= 255;\n },\n};\n\n","/**\n * 数组和对象工具\n */\n\nexport const arrayUtils = {\n /**\n * 数组去重\n */\n unique<T>(array: T[]): T[] {\n return [...new Set(array)];\n },\n\n /**\n * 数组分组\n */\n groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {\n return array.reduce(\n (groups, item) => {\n const groupKey = String(item[key]);\n const group = groups[groupKey] || [];\n group.push(item);\n groups[groupKey] = group;\n return groups;\n },\n {} as Record<string, T[]>\n );\n },\n\n /**\n * 数组分页\n */\n paginate<T>(\n array: T[],\n page: number,\n limit: number\n ): {\n data: T[];\n total: number;\n page: number;\n pages: number;\n hasNext: boolean;\n hasPrev: boolean;\n } {\n const total = array.length;\n const pages = Math.ceil(total / limit);\n const start = (page - 1) * limit;\n const end = start + limit;\n const data = array.slice(start, end);\n\n return {\n data,\n total,\n page,\n pages,\n hasNext: page < pages,\n hasPrev: page > 1,\n };\n },\n\n /**\n * 数组随机排序\n */\n shuffle<T>(array: T[]): T[] {\n const shuffled = [...array];\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [shuffled[i], shuffled[j]] = [shuffled[j]!, shuffled[i]!];\n }\n return shuffled;\n },\n};\n\n","/**\n * 字符串工具\n */\n\nexport const stringUtils = {\n /**\n * 截断文本\n */\n truncate(text: string, length: number, suffix = '...'): string {\n if (text.length <= length) return text;\n return text.substring(0, length - suffix.length) + suffix;\n },\n\n /**\n * 首字母大写\n */\n capitalize(text: string): string {\n if (!text) return '';\n return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();\n },\n\n /**\n * 驼峰转下划线\n */\n camelToSnake(text: string): string {\n return text.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n },\n\n /**\n * 下划线转驼峰\n */\n snakeToCamel(text: string): string {\n return text.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n },\n\n /**\n * 生成随机字符串\n */\n generateRandom(length: number): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n },\n};\n\n","/**\n * 调试工具\n */\n\nimport { logger } from '../logger';\nimport { fileUtils } from './file';\n\nexport const debugUtils = {\n /**\n * 安全的 JSON 序列化\n */\n safeStringify(obj: any): string {\n try {\n return JSON.stringify(obj, null, 2);\n } catch (error) {\n return `[Circular Reference or Invalid JSON: ${error}]`;\n }\n },\n\n /**\n * 性能计时器\n */\n createTimer(label?: string) {\n const start = typeof performance !== 'undefined' ? performance.now() : Date.now();\n return {\n end: () => {\n const end = typeof performance !== 'undefined' ? performance.now() : Date.now();\n const duration = end - start;\n const message = `${label || 'Timer'}: ${duration.toFixed(2)}ms`;\n logger.info(message);\n return duration;\n },\n };\n },\n\n /**\n * 内存使用情况(仅在 Node.js 环境)\n */\n getMemoryUsage(): Record<string, string> | null {\n if (typeof process !== 'undefined' && process.memoryUsage) {\n const usage = process.memoryUsage();\n return {\n rss: fileUtils.formatFileSize(usage.rss),\n heapTotal: fileUtils.formatFileSize(usage.heapTotal),\n heapUsed: fileUtils.formatFileSize(usage.heapUsed),\n external: fileUtils.formatFileSize(usage.external),\n };\n }\n return null;\n },\n};\n\n","/**\n * 错误处理工具\n */\n\nexport const errorUtils = {\n /**\n * 创建标准化的错误对象\n */\n createError(\n code: string,\n message: string,\n details?: any\n ): Error & { code: string; details?: any } {\n const error = new Error(message) as Error & { code: string; details?: any };\n error.code = code;\n if (details) {\n error.details = details;\n }\n return error;\n },\n\n /**\n * 安全的错误信息提取\n */\n extractErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error === 'string') {\n return error;\n }\n if (error && typeof error === 'object' && 'message' in error) {\n return String(error.message);\n }\n return 'Unknown error';\n },\n\n /**\n * 错误重试机制\n */\n async retry<T>(fn: () => Promise<T>, maxAttempts = 3, delay = 1000): Promise<T> {\n let lastError: Error;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n if (attempt === maxAttempts) {\n throw lastError;\n }\n\n // 指数退避延迟\n await new Promise((resolve) => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));\n }\n }\n\n throw lastError!;\n },\n};\n\n"]}
|