id-scanner-lib 1.5.0 → 1.6.2
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/README.md +378 -59
- package/dist/id-scanner-lib.esm.js +195 -10
- package/dist/id-scanner-lib.esm.js.map +1 -1
- package/dist/id-scanner-lib.js +4812 -14709
- package/dist/id-scanner-lib.js.map +1 -1
- package/dist/types/browser-image-compression.d.ts +19 -0
- package/dist/types/tesseract.d.ts +280 -0
- package/package.json +21 -11
- package/src/core/camera-manager.ts +16 -1
- package/src/core/config.ts +37 -0
- package/src/core/errors.ts +3 -3
- package/src/core/event-emitter.test.ts +42 -0
- package/src/core/loading-state.test.ts +67 -0
- package/src/core/loading-state.ts +156 -0
- package/src/core/logger.test.ts +49 -0
- package/src/core/module-manager.ts +2 -4
- package/src/core/scanner-factory.ts +8 -9
- package/src/index.ts +3 -2
- package/src/modules/face/face-detector.ts +123 -66
- package/src/modules/id-card/anti-fake-detector.ts +2 -2
- package/src/modules/id-card/ocr-worker.ts +1 -1
- package/src/modules/qrcode/qr-code-scanner.ts +2 -1
- package/src/modules/qrcode/types.ts +111 -7
- package/src/types/common.test.ts +99 -0
- package/src/types/common.ts +166 -0
- package/src/utils/camera.test.ts +30 -0
- package/src/utils/camera.ts +4 -1
- package/src/utils/error-handler.test.ts +137 -0
- package/src/utils/error-handler.ts +110 -0
- package/src/utils/index.test.ts +186 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/retry.test.ts +142 -0
- package/src/utils/retry.ts +282 -0
- package/src/utils/utils.test.ts +171 -0
- package/src/version.ts +1 -1
- package/dist/types/core/base-module.d.ts +0 -44
- package/dist/types/core/camera-manager.d.ts +0 -258
- package/dist/types/core/config.d.ts +0 -88
- package/dist/types/core/errors.d.ts +0 -111
- package/dist/types/core/event-emitter.d.ts +0 -55
- package/dist/types/core/logger.d.ts +0 -277
- package/dist/types/core/module-manager.d.ts +0 -78
- package/dist/types/core/plugin-manager.d.ts +0 -158
- package/dist/types/core/resource-manager.d.ts +0 -246
- package/dist/types/core/result.d.ts +0 -83
- package/dist/types/core/scanner-factory.d.ts +0 -93
- package/dist/types/index.bundle.d.ts +0 -1303
- package/dist/types/index.d.ts +0 -86
- package/dist/types/interfaces/external-types.d.ts +0 -174
- package/dist/types/interfaces/face-detection.d.ts +0 -293
- package/dist/types/interfaces/scanner-module.d.ts +0 -280
- package/dist/types/modules/face/face-detector.d.ts +0 -170
- package/dist/types/modules/face/index.d.ts +0 -56
- package/dist/types/modules/face/liveness-detector.d.ts +0 -177
- package/dist/types/modules/face/types.d.ts +0 -136
- package/dist/types/modules/id-card/anti-fake-detector.d.ts +0 -170
- package/dist/types/modules/id-card/id-card-detector.d.ts +0 -131
- package/dist/types/modules/id-card/index.d.ts +0 -89
- package/dist/types/modules/id-card/ocr-processor.d.ts +0 -110
- package/dist/types/modules/id-card/ocr-worker.d.ts +0 -31
- package/dist/types/modules/id-card/types.d.ts +0 -181
- package/dist/types/modules/qrcode/index.d.ts +0 -51
- package/dist/types/modules/qrcode/qr-code-scanner.d.ts +0 -64
- package/dist/types/modules/qrcode/types.d.ts +0 -67
- package/dist/types/utils/camera.d.ts +0 -81
- package/dist/types/utils/image-processing.d.ts +0 -176
- package/dist/types/utils/index.d.ts +0 -175
- package/dist/types/utils/performance.d.ts +0 -81
- package/dist/types/utils/resource-manager.d.ts +0 -53
- package/dist/types/utils/types.d.ts +0 -166
- package/dist/types/utils/worker.d.ts +0 -52
- package/dist/types/version.d.ts +0 -7
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 重试工具
|
|
3
|
+
* @description 提供重试逻辑功能
|
|
4
|
+
* @module utils/retry
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 重试选项
|
|
9
|
+
*/
|
|
10
|
+
export interface RetryOptions {
|
|
11
|
+
/** 最大重试次数 */
|
|
12
|
+
maxAttempts?: number;
|
|
13
|
+
/** 初始等待时间(ms) */
|
|
14
|
+
initialDelay?: number;
|
|
15
|
+
/** 最大等待时间(ms) */
|
|
16
|
+
maxDelay?: number;
|
|
17
|
+
/** 指数退避因子 */
|
|
18
|
+
backoffFactor?: number;
|
|
19
|
+
/** 是否随机抖动 */
|
|
20
|
+
jitter?: boolean;
|
|
21
|
+
/** 重试条件 */
|
|
22
|
+
shouldRetry?: (error: unknown) => boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 默认重试选项
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_OPTIONS: Required<RetryOptions> = {
|
|
29
|
+
maxAttempts: 3,
|
|
30
|
+
initialDelay: 1000,
|
|
31
|
+
maxDelay: 10000,
|
|
32
|
+
backoffFactor: 2,
|
|
33
|
+
jitter: true,
|
|
34
|
+
shouldRetry: () => true
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 带重试的异步函数包装器
|
|
39
|
+
* @param fn 要执行的异步函数
|
|
40
|
+
* @param options 重试选项
|
|
41
|
+
* @returns 函数结果
|
|
42
|
+
*/
|
|
43
|
+
export async function withRetry<T>(
|
|
44
|
+
fn: () => Promise<T>,
|
|
45
|
+
options: RetryOptions = {}
|
|
46
|
+
): Promise<T> {
|
|
47
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
48
|
+
let lastError: unknown;
|
|
49
|
+
let delay = opts.initialDelay;
|
|
50
|
+
|
|
51
|
+
for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
52
|
+
try {
|
|
53
|
+
return await fn();
|
|
54
|
+
} catch (error) {
|
|
55
|
+
lastError = error;
|
|
56
|
+
|
|
57
|
+
// 检查是否应该重试
|
|
58
|
+
if (!opts.shouldRetry(error)) {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 如果不是最后一次尝试,等待后重试
|
|
63
|
+
if (attempt < opts.maxAttempts) {
|
|
64
|
+
// 添加随机抖动
|
|
65
|
+
const jitterAmount = opts.jitter ? Math.random() * delay : 0;
|
|
66
|
+
const waitTime = Math.min(delay + jitterAmount, opts.maxDelay);
|
|
67
|
+
|
|
68
|
+
await sleep(waitTime);
|
|
69
|
+
|
|
70
|
+
// 指数退避
|
|
71
|
+
delay = Math.min(delay * opts.backoffFactor, opts.maxDelay);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw lastError;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 睡眠函数
|
|
81
|
+
* @param ms 毫秒数
|
|
82
|
+
*/
|
|
83
|
+
function sleep(ms: number): Promise<void> {
|
|
84
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 创建指数退避重试函数
|
|
89
|
+
* @param options 重试选项
|
|
90
|
+
* @returns 包装后的重试函数
|
|
91
|
+
*/
|
|
92
|
+
export function createRetryable<T extends (...args: any[]) => Promise<any>>(
|
|
93
|
+
fn: T,
|
|
94
|
+
options: RetryOptions = {}
|
|
95
|
+
): T {
|
|
96
|
+
const wrapped = (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
|
97
|
+
return withRetry(() => fn(...args), options);
|
|
98
|
+
};
|
|
99
|
+
Object.defineProperty(wrapped, 'name', { value: fn.name, configurable: true });
|
|
100
|
+
return wrapped as T;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 异步缓存
|
|
105
|
+
* @description 用于缓存异步函数的结果
|
|
106
|
+
*/
|
|
107
|
+
export class AsyncCache<T> {
|
|
108
|
+
private cache: Map<string, { value: T; expiry: number }> = new Map();
|
|
109
|
+
private defaultTTL: number;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param defaultTTL 默认过期时间(毫秒)
|
|
113
|
+
*/
|
|
114
|
+
constructor(defaultTTL: number = 5 * 60 * 1000) {
|
|
115
|
+
this.defaultTTL = defaultTTL;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 获取缓存值
|
|
120
|
+
* @param key 缓存键
|
|
121
|
+
* @returns 缓存值,如果不存在或已过期则返回undefined
|
|
122
|
+
*/
|
|
123
|
+
get(key: string): T | undefined {
|
|
124
|
+
const entry = this.cache.get(key);
|
|
125
|
+
if (!entry) return undefined;
|
|
126
|
+
|
|
127
|
+
if (Date.now() > entry.expiry) {
|
|
128
|
+
this.cache.delete(key);
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return entry.value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 设置缓存值
|
|
137
|
+
* @param key 缓存键
|
|
138
|
+
* @param value 缓存值
|
|
139
|
+
* @param ttl 过期时间(毫秒)
|
|
140
|
+
*/
|
|
141
|
+
set(key: string, value: T, ttl?: number): void {
|
|
142
|
+
this.cache.set(key, {
|
|
143
|
+
value,
|
|
144
|
+
expiry: Date.now() + (ttl || this.defaultTTL)
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 删除缓存值
|
|
150
|
+
* @param key 缓存键
|
|
151
|
+
*/
|
|
152
|
+
delete(key: string): void {
|
|
153
|
+
this.cache.delete(key);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 清空缓存
|
|
158
|
+
*/
|
|
159
|
+
clear(): void {
|
|
160
|
+
this.cache.clear();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 获取缓存大小
|
|
165
|
+
*/
|
|
166
|
+
get size(): number {
|
|
167
|
+
// 清理过期项 (避免在迭代中删除)
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
const expiredKeys: string[] = [];
|
|
170
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
171
|
+
if (now > entry.expiry) {
|
|
172
|
+
expiredKeys.push(key);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
expiredKeys.forEach(key => this.cache.delete(key));
|
|
176
|
+
return this.cache.size;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 异步获取或设置缓存
|
|
181
|
+
* @param key 缓存键
|
|
182
|
+
* @param fn 获取值的函数
|
|
183
|
+
* @param ttl 过期时间
|
|
184
|
+
* @returns 缓存值
|
|
185
|
+
*/
|
|
186
|
+
async getOrSet(
|
|
187
|
+
key: string,
|
|
188
|
+
fn: () => Promise<T>,
|
|
189
|
+
ttl?: number
|
|
190
|
+
): Promise<T> {
|
|
191
|
+
const cached = this.get(key);
|
|
192
|
+
if (cached !== undefined) {
|
|
193
|
+
return cached;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const value = await fn();
|
|
197
|
+
this.set(key, value, ttl);
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 简单信号量
|
|
204
|
+
* @description 用于控制并发数量
|
|
205
|
+
*/
|
|
206
|
+
export class Semaphore {
|
|
207
|
+
private permits: number;
|
|
208
|
+
private queue: Array<() => void> = [];
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @param permits 最大许可数
|
|
212
|
+
*/
|
|
213
|
+
constructor(permits: number) {
|
|
214
|
+
this.permits = permits;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 获取许可
|
|
219
|
+
* @returns Promise
|
|
220
|
+
*/
|
|
221
|
+
async acquire(): Promise<void> {
|
|
222
|
+
if (this.permits > 0) {
|
|
223
|
+
this.permits--;
|
|
224
|
+
return Promise.resolve();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return new Promise<void>(resolve => {
|
|
228
|
+
this.queue.push(resolve);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 释放许可
|
|
234
|
+
*/
|
|
235
|
+
release(): void {
|
|
236
|
+
this.permits++;
|
|
237
|
+
const next = this.queue.shift();
|
|
238
|
+
if (next) {
|
|
239
|
+
this.permits--;
|
|
240
|
+
next();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 尝试获取许可
|
|
246
|
+
* @returns 是否获取成功
|
|
247
|
+
*/
|
|
248
|
+
tryAcquire(): boolean {
|
|
249
|
+
if (this.permits > 0) {
|
|
250
|
+
this.permits--;
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 获取当前可用许可数
|
|
258
|
+
*/
|
|
259
|
+
get availablePermits(): number {
|
|
260
|
+
return this.permits;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 带信号量的异步函数包装器
|
|
266
|
+
* @param fn 异步函数
|
|
267
|
+
* @param semaphore 信号量
|
|
268
|
+
* @returns 包装后的函数
|
|
269
|
+
*/
|
|
270
|
+
export function withSemaphore<T extends (...args: any[]) => Promise<any>>(
|
|
271
|
+
fn: T,
|
|
272
|
+
semaphore: Semaphore
|
|
273
|
+
): T {
|
|
274
|
+
return (async (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
|
275
|
+
await semaphore.acquire();
|
|
276
|
+
try {
|
|
277
|
+
return await fn(...args);
|
|
278
|
+
} finally {
|
|
279
|
+
semaphore.release();
|
|
280
|
+
}
|
|
281
|
+
}) as T;
|
|
282
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 工具函数测试
|
|
3
|
+
* @description 测试通用工具函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
delay,
|
|
8
|
+
throttle,
|
|
9
|
+
debounce,
|
|
10
|
+
formatBytes,
|
|
11
|
+
generateUUID,
|
|
12
|
+
chunk,
|
|
13
|
+
safeParseJSON,
|
|
14
|
+
clamp,
|
|
15
|
+
isValidUrl,
|
|
16
|
+
} from '../utils';
|
|
17
|
+
|
|
18
|
+
describe('Utils', () => {
|
|
19
|
+
describe('delay', () => {
|
|
20
|
+
it('should delay for specified milliseconds', async () => {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
await delay(100);
|
|
23
|
+
const elapsed = Date.now() - start;
|
|
24
|
+
expect(elapsed).toBeGreaterThanOrEqual(90);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('throttle', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
jest.useFakeTimers();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
jest.useRealTimers();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should throttle function calls', () => {
|
|
38
|
+
const fn = jest.fn();
|
|
39
|
+
const throttled = throttle(fn, 100);
|
|
40
|
+
|
|
41
|
+
throttled();
|
|
42
|
+
throttled();
|
|
43
|
+
throttled();
|
|
44
|
+
|
|
45
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should allow calls after throttle period', () => {
|
|
49
|
+
const fn = jest.fn();
|
|
50
|
+
const throttled = throttle(fn, 100);
|
|
51
|
+
|
|
52
|
+
throttled();
|
|
53
|
+
jest.advanceTimersByTime(100);
|
|
54
|
+
throttled();
|
|
55
|
+
|
|
56
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('debounce', () => {
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
jest.useFakeTimers();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
jest.useRealTimers();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should debounce function calls', () => {
|
|
70
|
+
const fn = jest.fn();
|
|
71
|
+
const debounced = debounce(fn, 100);
|
|
72
|
+
|
|
73
|
+
debounced();
|
|
74
|
+
debounced();
|
|
75
|
+
debounced();
|
|
76
|
+
|
|
77
|
+
jest.advanceTimersByTime(100);
|
|
78
|
+
|
|
79
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should execute immediately when immediate is true', () => {
|
|
83
|
+
const fn = jest.fn();
|
|
84
|
+
const debounced = debounce(fn, 100, true);
|
|
85
|
+
|
|
86
|
+
debounced();
|
|
87
|
+
debounced();
|
|
88
|
+
debounced();
|
|
89
|
+
|
|
90
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('formatBytes', () => {
|
|
95
|
+
it('should format bytes correctly', () => {
|
|
96
|
+
expect(formatBytes(0)).toBe('0 Bytes');
|
|
97
|
+
expect(formatBytes(1024)).toBe('1 KB');
|
|
98
|
+
expect(formatBytes(1024 * 1024)).toBe('1 MB');
|
|
99
|
+
expect(formatBytes(1024 * 1024 * 1024)).toBe('1 GB');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should respect decimal places', () => {
|
|
103
|
+
expect(formatBytes(1536, 2)).toBe('1.5 KB');
|
|
104
|
+
expect(formatBytes(1536, 0)).toBe('2 KB');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('generateUUID', () => {
|
|
109
|
+
it('should generate valid UUID format', () => {
|
|
110
|
+
const uuid = generateUUID();
|
|
111
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
112
|
+
expect(uuid).toMatch(uuidRegex);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should generate unique UUIDs', () => {
|
|
116
|
+
const uuids = new Set(Array.from({ length: 100 }, () => generateUUID()));
|
|
117
|
+
expect(uuids.size).toBe(100);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('chunk', () => {
|
|
122
|
+
it('should split array into chunks', () => {
|
|
123
|
+
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
124
|
+
const chunks = chunk(arr, 3);
|
|
125
|
+
expect(chunks).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should handle last chunk smaller than chunk size', () => {
|
|
129
|
+
const arr = [1, 2, 3, 4, 5];
|
|
130
|
+
const chunks = chunk(arr, 2);
|
|
131
|
+
expect(chunks).toEqual([[1, 2], [3, 4], [5]]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should throw error for invalid chunk size', () => {
|
|
135
|
+
expect(() => chunk([1, 2, 3], 0)).toThrow('Chunk size must be greater than 0');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('safeParseJSON', () => {
|
|
140
|
+
it('should parse valid JSON', () => {
|
|
141
|
+
const result = safeParseJSON('{"a": 1}', null);
|
|
142
|
+
expect(result).toEqual({ a: 1 });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return fallback for invalid JSON', () => {
|
|
146
|
+
const result = safeParseJSON('invalid', { fallback: true });
|
|
147
|
+
expect(result).toEqual({ fallback: true });
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('clamp', () => {
|
|
152
|
+
it('should clamp value within range', () => {
|
|
153
|
+
expect(clamp(5, 0, 10)).toBe(5);
|
|
154
|
+
expect(clamp(-5, 0, 10)).toBe(0);
|
|
155
|
+
expect(clamp(15, 0, 10)).toBe(10);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('isValidUrl', () => {
|
|
160
|
+
it('should validate correct URLs', () => {
|
|
161
|
+
expect(isValidUrl('https://example.com')).toBe(true);
|
|
162
|
+
expect(isValidUrl('http://localhost:3000')).toBe(true);
|
|
163
|
+
expect(isValidUrl('ftp://files.example.com')).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should reject invalid URLs', () => {
|
|
167
|
+
expect(isValidUrl('not-a-url')).toBe(false);
|
|
168
|
+
expect(isValidUrl('')).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
package/src/version.ts
CHANGED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file 基础模块
|
|
3
|
-
* @description 提供基础模块实现,作为所有功能模块的基类
|
|
4
|
-
* @module core/base-module
|
|
5
|
-
*/
|
|
6
|
-
import { EventEmitter } from './event-emitter';
|
|
7
|
-
import { Logger } from './logger';
|
|
8
|
-
import { Module } from './module-manager';
|
|
9
|
-
/**
|
|
10
|
-
* 基础模块类
|
|
11
|
-
* 提供模块的基本功能和生命周期管理
|
|
12
|
-
*/
|
|
13
|
-
export declare abstract class BaseModule extends EventEmitter implements Module {
|
|
14
|
-
/** 模块名称 */
|
|
15
|
-
abstract readonly name: string;
|
|
16
|
-
/** 模块版本 */
|
|
17
|
-
readonly version: string;
|
|
18
|
-
/** 模块是否已初始化 */
|
|
19
|
-
protected _isInitialized: boolean;
|
|
20
|
-
/** 日志工具 */
|
|
21
|
-
protected logger: Logger;
|
|
22
|
-
/**
|
|
23
|
-
* 构造函数
|
|
24
|
-
*/
|
|
25
|
-
constructor();
|
|
26
|
-
/**
|
|
27
|
-
* 获取模块是否已初始化
|
|
28
|
-
*/
|
|
29
|
-
get isInitialized(): boolean;
|
|
30
|
-
/**
|
|
31
|
-
* 初始化模块
|
|
32
|
-
* 子类必须实现此方法
|
|
33
|
-
*/
|
|
34
|
-
abstract initialize(): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* 释放模块资源
|
|
37
|
-
* 子类可以覆盖此方法以添加额外的资源释放逻辑
|
|
38
|
-
*/
|
|
39
|
-
dispose(): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* 检查模块是否已初始化,如果未初始化则抛出错误
|
|
42
|
-
*/
|
|
43
|
-
protected ensureInitialized(): void;
|
|
44
|
-
}
|