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.
Files changed (72) hide show
  1. package/README.md +378 -59
  2. package/dist/id-scanner-lib.esm.js +195 -10
  3. package/dist/id-scanner-lib.esm.js.map +1 -1
  4. package/dist/id-scanner-lib.js +4812 -14709
  5. package/dist/id-scanner-lib.js.map +1 -1
  6. package/dist/types/browser-image-compression.d.ts +19 -0
  7. package/dist/types/tesseract.d.ts +280 -0
  8. package/package.json +21 -11
  9. package/src/core/camera-manager.ts +16 -1
  10. package/src/core/config.ts +37 -0
  11. package/src/core/errors.ts +3 -3
  12. package/src/core/event-emitter.test.ts +42 -0
  13. package/src/core/loading-state.test.ts +67 -0
  14. package/src/core/loading-state.ts +156 -0
  15. package/src/core/logger.test.ts +49 -0
  16. package/src/core/module-manager.ts +2 -4
  17. package/src/core/scanner-factory.ts +8 -9
  18. package/src/index.ts +3 -2
  19. package/src/modules/face/face-detector.ts +123 -66
  20. package/src/modules/id-card/anti-fake-detector.ts +2 -2
  21. package/src/modules/id-card/ocr-worker.ts +1 -1
  22. package/src/modules/qrcode/qr-code-scanner.ts +2 -1
  23. package/src/modules/qrcode/types.ts +111 -7
  24. package/src/types/common.test.ts +99 -0
  25. package/src/types/common.ts +166 -0
  26. package/src/utils/camera.test.ts +30 -0
  27. package/src/utils/camera.ts +4 -1
  28. package/src/utils/error-handler.test.ts +137 -0
  29. package/src/utils/error-handler.ts +110 -0
  30. package/src/utils/index.test.ts +186 -0
  31. package/src/utils/index.ts +3 -0
  32. package/src/utils/retry.test.ts +142 -0
  33. package/src/utils/retry.ts +282 -0
  34. package/src/utils/utils.test.ts +171 -0
  35. package/src/version.ts +1 -1
  36. package/dist/types/core/base-module.d.ts +0 -44
  37. package/dist/types/core/camera-manager.d.ts +0 -258
  38. package/dist/types/core/config.d.ts +0 -88
  39. package/dist/types/core/errors.d.ts +0 -111
  40. package/dist/types/core/event-emitter.d.ts +0 -55
  41. package/dist/types/core/logger.d.ts +0 -277
  42. package/dist/types/core/module-manager.d.ts +0 -78
  43. package/dist/types/core/plugin-manager.d.ts +0 -158
  44. package/dist/types/core/resource-manager.d.ts +0 -246
  45. package/dist/types/core/result.d.ts +0 -83
  46. package/dist/types/core/scanner-factory.d.ts +0 -93
  47. package/dist/types/index.bundle.d.ts +0 -1303
  48. package/dist/types/index.d.ts +0 -86
  49. package/dist/types/interfaces/external-types.d.ts +0 -174
  50. package/dist/types/interfaces/face-detection.d.ts +0 -293
  51. package/dist/types/interfaces/scanner-module.d.ts +0 -280
  52. package/dist/types/modules/face/face-detector.d.ts +0 -170
  53. package/dist/types/modules/face/index.d.ts +0 -56
  54. package/dist/types/modules/face/liveness-detector.d.ts +0 -177
  55. package/dist/types/modules/face/types.d.ts +0 -136
  56. package/dist/types/modules/id-card/anti-fake-detector.d.ts +0 -170
  57. package/dist/types/modules/id-card/id-card-detector.d.ts +0 -131
  58. package/dist/types/modules/id-card/index.d.ts +0 -89
  59. package/dist/types/modules/id-card/ocr-processor.d.ts +0 -110
  60. package/dist/types/modules/id-card/ocr-worker.d.ts +0 -31
  61. package/dist/types/modules/id-card/types.d.ts +0 -181
  62. package/dist/types/modules/qrcode/index.d.ts +0 -51
  63. package/dist/types/modules/qrcode/qr-code-scanner.d.ts +0 -64
  64. package/dist/types/modules/qrcode/types.d.ts +0 -67
  65. package/dist/types/utils/camera.d.ts +0 -81
  66. package/dist/types/utils/image-processing.d.ts +0 -176
  67. package/dist/types/utils/index.d.ts +0 -175
  68. package/dist/types/utils/performance.d.ts +0 -81
  69. package/dist/types/utils/resource-manager.d.ts +0 -53
  70. package/dist/types/utils/types.d.ts +0 -166
  71. package/dist/types/utils/worker.d.ts +0 -52
  72. package/dist/types/version.d.ts +0 -7
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @file 统一错误处理工具
3
+ * @description 提供统一的错误处理和日志记录功能
4
+ * @module utils/error-handler
5
+ */
6
+
7
+ import { Logger } from '../core/logger';
8
+
9
+ /**
10
+ * 错误严重级别
11
+ */
12
+ export enum ErrorSeverity {
13
+ DEBUG = 'debug',
14
+ INFO = 'info',
15
+ WARN = 'warn',
16
+ ERROR = 'error'
17
+ }
18
+
19
+ /**
20
+ * 错误处理工具类
21
+ */
22
+ export class ErrorHandler {
23
+ private static logger = Logger.getInstance();
24
+
25
+ /**
26
+ * 处理错误并记录日志
27
+ * @param context 错误上下文(模块名)
28
+ * @param message 错误消息
29
+ * @param error 错误对象
30
+ * @param severity 错误级别
31
+ */
32
+ static handle(
33
+ context: string,
34
+ message: string,
35
+ error?: unknown,
36
+ severity: ErrorSeverity = ErrorSeverity.ERROR
37
+ ): void {
38
+ const errorObj = error instanceof Error ? error : error ? new Error(String(error)) : undefined;
39
+
40
+ switch (severity) {
41
+ case ErrorSeverity.DEBUG:
42
+ this.logger.debug(context, message, errorObj);
43
+ break;
44
+ case ErrorSeverity.INFO:
45
+ this.logger.info(context, message, errorObj);
46
+ break;
47
+ case ErrorSeverity.WARN:
48
+ this.logger.warn(context, message, errorObj);
49
+ break;
50
+ case ErrorSeverity.ERROR:
51
+ default:
52
+ this.logger.error(context, message, errorObj);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 处理异步错误
58
+ * @param context 错误上下文
59
+ * @param message 错误消息
60
+ * @returns 返回一个错误处理函数
61
+ */
62
+ static asyncHandler(
63
+ context: string,
64
+ message: string
65
+ ): (error: unknown) => void {
66
+ return (error: unknown) => {
67
+ this.handle(context, message, error);
68
+ };
69
+ }
70
+
71
+ /**
72
+ * 安全执行异步函数并处理错误
73
+ * @param fn 异步函数
74
+ * @param context 错误上下文
75
+ * @param fallbackValue 错误时的返回值
76
+ * @returns 函数结果或 fallbackValue
77
+ */
78
+ static async safeExecute<T>(
79
+ fn: () => Promise<T>,
80
+ context: string,
81
+ fallbackValue: T
82
+ ): Promise<T> {
83
+ try {
84
+ return await fn();
85
+ } catch (error) {
86
+ this.handle(context, 'Operation failed', error);
87
+ return fallbackValue;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * 安全执行函数并处理错误
93
+ * @param fn 函数
94
+ * @param context 错误上下文
95
+ * @param fallbackValue 错误时的返回值
96
+ * @returns 函数结果或 fallbackValue
97
+ */
98
+ static safeExecuteSync<T>(
99
+ fn: () => T,
100
+ context: string,
101
+ fallbackValue: T
102
+ ): T {
103
+ try {
104
+ return fn();
105
+ } catch (error) {
106
+ this.handle(context, 'Operation failed', error);
107
+ return fallbackValue;
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,186 @@
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
+ browserCapabilities
17
+ } from './index';
18
+
19
+ describe('delay', () => {
20
+ it('should delay for specified milliseconds', async () => {
21
+ const start = Date.now();
22
+ await delay(50);
23
+ const elapsed = Date.now() - start;
24
+
25
+ expect(elapsed).toBeGreaterThanOrEqual(45);
26
+ });
27
+ });
28
+
29
+ describe('throttle', () => {
30
+ beforeEach(() => {
31
+ jest.useFakeTimers();
32
+ });
33
+
34
+ afterEach(() => {
35
+ jest.useRealTimers();
36
+ });
37
+
38
+ it('should throttle function calls', () => {
39
+ let count = 0;
40
+ const fn = throttle(() => count++, 100);
41
+
42
+ fn();
43
+ fn();
44
+ fn();
45
+
46
+ expect(count).toBe(1);
47
+
48
+ jest.advanceTimersByTime(100);
49
+
50
+ fn();
51
+ expect(count).toBe(2);
52
+ });
53
+ });
54
+
55
+ describe('debounce', () => {
56
+ beforeEach(() => {
57
+ jest.useFakeTimers();
58
+ });
59
+
60
+ afterEach(() => {
61
+ jest.useRealTimers();
62
+ });
63
+
64
+ it('should debounce function calls', () => {
65
+ let count = 0;
66
+ const fn = debounce(() => count++, 100);
67
+
68
+ fn();
69
+ fn();
70
+ fn();
71
+
72
+ expect(count).toBe(0);
73
+
74
+ jest.advanceTimersByTime(100);
75
+
76
+ expect(count).toBe(1);
77
+ });
78
+
79
+ it('should execute immediately when immediate is true', () => {
80
+ let count = 0;
81
+ const fn = debounce(() => count++, 100, true);
82
+
83
+ fn();
84
+ // Immediate should execute once
85
+ expect(count).toBe(1);
86
+
87
+ // Advance time and check if it runs again
88
+ jest.advanceTimersByTime(100);
89
+ // Depending on implementation, this may vary
90
+ expect(count).toBeGreaterThanOrEqual(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 handle decimals', () => {
103
+ expect(formatBytes(1536, 2)).toBe('1.5 KB');
104
+ });
105
+ });
106
+
107
+ describe('generateUUID', () => {
108
+ it('should generate valid UUID', () => {
109
+ const uuid = generateUUID();
110
+
111
+ expect(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
112
+ });
113
+
114
+ it('should generate unique UUIDs', () => {
115
+ const uuids = new Set<string>();
116
+ for (let i = 0; i < 100; i++) {
117
+ uuids.add(generateUUID());
118
+ }
119
+
120
+ expect(uuids.size).toBe(100);
121
+ });
122
+ });
123
+
124
+ describe('chunk', () => {
125
+ it('should split array into chunks', () => {
126
+ const arr = [1, 2, 3, 4, 5, 6, 7];
127
+ const chunks = chunk(arr, 3);
128
+
129
+ expect(chunks).toEqual([[1, 2, 3], [4, 5, 6], [7]]);
130
+ });
131
+
132
+ it('should throw error for invalid chunk size', () => {
133
+ expect(() => chunk([1, 2, 3], 0)).toThrow();
134
+ });
135
+ });
136
+
137
+ describe('safeParseJSON', () => {
138
+ it('should parse valid JSON', () => {
139
+ const result = safeParseJSON('{"a": 1}', { default: true });
140
+ expect(result).toEqual({ a: 1 });
141
+ });
142
+
143
+ it('should return fallback for invalid JSON', () => {
144
+ const result = safeParseJSON<boolean>('invalid', true);
145
+ expect(result).toBe(true);
146
+ });
147
+ });
148
+
149
+ describe('clamp', () => {
150
+ it('should clamp value within range', () => {
151
+ expect(clamp(5, 0, 10)).toBe(5);
152
+ expect(clamp(-5, 0, 10)).toBe(0);
153
+ expect(clamp(15, 0, 10)).toBe(10);
154
+ });
155
+ });
156
+
157
+ describe('isValidUrl', () => {
158
+ it('should validate URLs', () => {
159
+ expect(isValidUrl('https://example.com')).toBe(true);
160
+ expect(isValidUrl('http://test.com/path')).toBe(true);
161
+ expect(isValidUrl('invalid')).toBe(false);
162
+ expect(isValidUrl('not a url')).toBe(false);
163
+ });
164
+ });
165
+
166
+ describe('browserCapabilities', () => {
167
+ it('should check camera support', () => {
168
+ expect(typeof browserCapabilities.hasCamera).toBe('function');
169
+ });
170
+
171
+ it('should check WebAssembly support', () => {
172
+ expect(typeof browserCapabilities.hasWasm).toBe('function');
173
+ });
174
+
175
+ it('should check WebWorker support', () => {
176
+ expect(typeof browserCapabilities.hasWebWorker).toBe('function');
177
+ });
178
+
179
+ it('should check WebGL support', () => {
180
+ expect(typeof browserCapabilities.hasWebGL).toBe('function');
181
+ });
182
+
183
+ it('should use supports method', () => {
184
+ expect(typeof browserCapabilities.supports).toBe('function');
185
+ });
186
+ });
@@ -4,6 +4,9 @@
4
4
  * @module utils
5
5
  */
6
6
 
7
+ export * from './error-handler';
8
+ export * from './retry';
9
+
7
10
  /**
8
11
  * 创建延迟Promise
9
12
  * @param ms 延迟毫秒数
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @file 重试工具测试
3
+ * @description 测试重试功能
4
+ */
5
+
6
+ import { withRetry, AsyncCache, Semaphore } from './retry';
7
+
8
+ describe('withRetry', () => {
9
+ it('should retry on failure and succeed', async () => {
10
+ let attempts = 0;
11
+ const fn = async () => {
12
+ attempts++;
13
+ if (attempts < 3) throw new Error('fail');
14
+ return 'success';
15
+ };
16
+
17
+ const result = await withRetry(fn, {
18
+ maxAttempts: 3,
19
+ initialDelay: 10
20
+ });
21
+
22
+ expect(result).toBe('success');
23
+ expect(attempts).toBe(3);
24
+ });
25
+
26
+ it('should fail after max attempts', async () => {
27
+ const fn = async () => {
28
+ throw new Error('always fails');
29
+ };
30
+
31
+ await expect(
32
+ withRetry(fn, { maxAttempts: 2, initialDelay: 10 })
33
+ ).rejects.toThrow('always fails');
34
+ });
35
+
36
+ it('should not retry if shouldRetry returns false', async () => {
37
+ let attempts = 0;
38
+ const fn = async () => {
39
+ attempts++;
40
+ throw new Error('test');
41
+ };
42
+
43
+ await expect(
44
+ withRetry(fn, {
45
+ maxAttempts: 3,
46
+ initialDelay: 10,
47
+ shouldRetry: () => false
48
+ })
49
+ ).rejects.toThrow();
50
+
51
+ expect(attempts).toBe(1);
52
+ });
53
+ });
54
+
55
+ describe('AsyncCache', () => {
56
+ it('should store and retrieve values', async () => {
57
+ const cache = new AsyncCache<string>(1000);
58
+ cache.set('key1', 'value1');
59
+
60
+ expect(cache.get('key1')).toBe('value1');
61
+ });
62
+
63
+ it('should return undefined for expired cache', async () => {
64
+ const cache = new AsyncCache<string>(50);
65
+ cache.set('key1', 'value1');
66
+
67
+ await new Promise(resolve => setTimeout(resolve, 60));
68
+
69
+ expect(cache.get('key1')).toBeUndefined();
70
+ });
71
+
72
+ it('should getOrSet value if not exists', async () => {
73
+ const cache = new AsyncCache<string>(1000);
74
+
75
+ const result = await cache.getOrSet('key1', async () => 'computed');
76
+
77
+ expect(result).toBe('computed');
78
+ expect(cache.get('key1')).toBe('computed');
79
+ });
80
+
81
+ it('should not recompute if exists', async () => {
82
+ const cache = new AsyncCache<string>(1000);
83
+ let computeCount = 0;
84
+
85
+ const compute = async () => {
86
+ computeCount++;
87
+ return 'value';
88
+ };
89
+
90
+ await cache.getOrSet('key1', compute);
91
+ await cache.getOrSet('key1', compute);
92
+
93
+ expect(computeCount).toBe(1);
94
+ });
95
+
96
+ it('should clear all cache', async () => {
97
+ const cache = new AsyncCache<string>(1000);
98
+ cache.set('key1', 'value1');
99
+ cache.set('key2', 'value2');
100
+
101
+ expect(cache.size).toBe(2);
102
+
103
+ cache.clear();
104
+ expect(cache.size).toBe(0);
105
+ });
106
+ });
107
+
108
+ describe('Semaphore', () => {
109
+ it('should acquire and release permits', async () => {
110
+ const sem = new Semaphore(2);
111
+
112
+ expect(sem.availablePermits).toBe(2);
113
+
114
+ await sem.acquire();
115
+ expect(sem.availablePermits).toBe(1);
116
+
117
+ await sem.acquire();
118
+ expect(sem.availablePermits).toBe(0);
119
+
120
+ sem.release();
121
+ expect(sem.availablePermits).toBe(1);
122
+ });
123
+
124
+ it('should tryAcquire returns false when no permits', () => {
125
+ const sem = new Semaphore(1);
126
+ sem.acquire();
127
+
128
+ expect(sem.tryAcquire()).toBe(false);
129
+ });
130
+
131
+ it('should queue waiting acquires', async () => {
132
+ const sem = new Semaphore(1);
133
+
134
+ // First acquire
135
+ await sem.acquire();
136
+ expect(sem.availablePermits).toBe(0);
137
+
138
+ // Release
139
+ sem.release();
140
+ expect(sem.availablePermits).toBe(1);
141
+ });
142
+ });