id-scanner-lib 1.3.3 → 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 (80) hide show
  1. package/README.md +324 -410
  2. package/dist/id-scanner-lib.esm.js +4826 -0
  3. package/dist/id-scanner-lib.esm.js.map +1 -0
  4. package/dist/id-scanner-lib.js +4858 -0
  5. package/dist/id-scanner-lib.js.map +1 -0
  6. package/dist/types/browser-image-compression.d.ts +19 -0
  7. package/dist/types/tesseract.d.ts +280 -0
  8. package/package.json +89 -78
  9. package/src/core/base-module.ts +78 -0
  10. package/src/core/camera-manager.ts +813 -0
  11. package/src/core/config.ts +305 -0
  12. package/src/core/errors.ts +174 -0
  13. package/src/core/event-emitter.test.ts +42 -0
  14. package/src/core/event-emitter.ts +110 -0
  15. package/src/core/loading-state.test.ts +67 -0
  16. package/src/core/loading-state.ts +156 -0
  17. package/src/core/logger.test.ts +49 -0
  18. package/src/core/logger.ts +549 -0
  19. package/src/core/module-manager.ts +163 -0
  20. package/src/core/plugin-manager.ts +429 -0
  21. package/src/core/resource-manager.ts +762 -0
  22. package/src/core/result.ts +163 -0
  23. package/src/core/scanner-factory.ts +236 -0
  24. package/src/index.ts +117 -939
  25. package/src/interfaces/external-types.ts +200 -0
  26. package/src/interfaces/face-detection.ts +309 -0
  27. package/src/interfaces/scanner-module.ts +384 -0
  28. package/src/modules/face/face-detector.ts +988 -0
  29. package/src/modules/face/index.ts +208 -0
  30. package/src/modules/face/liveness-detector.ts +908 -0
  31. package/src/modules/face/types.ts +133 -0
  32. package/src/{id-recognition → modules/id-card}/anti-fake-detector.ts +274 -240
  33. package/src/modules/id-card/id-card-detector.ts +474 -0
  34. package/src/modules/id-card/index.ts +425 -0
  35. package/src/{id-recognition → modules/id-card}/ocr-processor.ts +149 -92
  36. package/src/modules/id-card/ocr-worker.ts +259 -0
  37. package/src/modules/id-card/types.ts +178 -0
  38. package/src/modules/qrcode/index.ts +175 -0
  39. package/src/modules/qrcode/qr-code-scanner.ts +231 -0
  40. package/src/modules/qrcode/types.ts +169 -0
  41. package/src/types/common.test.ts +99 -0
  42. package/src/types/common.ts +166 -0
  43. package/src/types/tesseract.d.ts +265 -22
  44. package/src/utils/camera.test.ts +30 -0
  45. package/src/utils/camera.ts +4 -1
  46. package/src/utils/error-handler.test.ts +137 -0
  47. package/src/utils/error-handler.ts +110 -0
  48. package/src/utils/image-processing.ts +68 -49
  49. package/src/utils/index.test.ts +186 -0
  50. package/src/utils/index.ts +429 -0
  51. package/src/utils/performance.ts +168 -131
  52. package/src/utils/resource-manager.ts +65 -146
  53. package/src/utils/retry.test.ts +142 -0
  54. package/src/utils/retry.ts +282 -0
  55. package/src/utils/types.ts +90 -2
  56. package/src/utils/utils.test.ts +171 -0
  57. package/src/utils/worker.ts +123 -84
  58. package/src/version.ts +11 -0
  59. package/tools/scaffold.js +543 -0
  60. package/dist/id-scanner-core.esm.js +0 -11349
  61. package/dist/id-scanner-core.js +0 -11361
  62. package/dist/id-scanner-core.min.js +0 -1
  63. package/dist/id-scanner-ocr.esm.js +0 -2319
  64. package/dist/id-scanner-ocr.js +0 -2328
  65. package/dist/id-scanner-ocr.min.js +0 -1
  66. package/dist/id-scanner-qr.esm.js +0 -1296
  67. package/dist/id-scanner-qr.js +0 -1305
  68. package/dist/id-scanner-qr.min.js +0 -1
  69. package/dist/id-scanner.js +0 -4561
  70. package/dist/id-scanner.min.js +0 -1
  71. package/src/core.ts +0 -138
  72. package/src/demo/demo.ts +0 -204
  73. package/src/id-recognition/data-extractor.ts +0 -262
  74. package/src/id-recognition/id-detector.ts +0 -510
  75. package/src/id-recognition/ocr-worker.ts +0 -156
  76. package/src/index-umd.ts +0 -477
  77. package/src/ocr-module.ts +0 -187
  78. package/src/qr-module.ts +0 -179
  79. package/src/scanner/barcode-scanner.ts +0 -251
  80. package/src/scanner/qr-scanner.ts +0 -167
@@ -0,0 +1,429 @@
1
+ /**
2
+ * @file 工具集
3
+ * @description 提供通用工具函数
4
+ * @module utils
5
+ */
6
+
7
+ export * from './error-handler';
8
+ export * from './retry';
9
+
10
+ /**
11
+ * 创建延迟Promise
12
+ * @param ms 延迟毫秒数
13
+ */
14
+ export function delay(ms: number): Promise<void> {
15
+ return new Promise(resolve => setTimeout(resolve, ms));
16
+ }
17
+
18
+ /**
19
+ * 节流函数
20
+ * @param fn 要节流的函数
21
+ * @param wait 等待时间(ms)
22
+ */
23
+ export function throttle<T extends (...args: any[]) => any>(
24
+ fn: T,
25
+ wait: number
26
+ ): (...args: Parameters<T>) => ReturnType<T> | undefined {
27
+ let lastTime = 0;
28
+ let lastResult: ReturnType<T>;
29
+
30
+ return function(this: any, ...args: Parameters<T>): ReturnType<T> | undefined {
31
+ const now = Date.now();
32
+ if (now - lastTime >= wait) {
33
+ lastTime = now;
34
+ lastResult = fn.apply(this, args);
35
+ }
36
+ return lastResult;
37
+ };
38
+ }
39
+
40
+ /**
41
+ * 防抖函数
42
+ * @param fn 要防抖的函数
43
+ * @param wait 等待时间(ms)
44
+ * @param immediate 是否立即执行
45
+ */
46
+ export function debounce<T extends (...args: any[]) => any>(
47
+ fn: T,
48
+ wait: number,
49
+ immediate: boolean = false
50
+ ): (...args: Parameters<T>) => void {
51
+ let timeout: number | null = null;
52
+
53
+ return function(this: any, ...args: Parameters<T>): void {
54
+ const callNow = immediate && !timeout;
55
+
56
+ if (timeout !== null) {
57
+ clearTimeout(timeout);
58
+ }
59
+
60
+ timeout = window.setTimeout(() => {
61
+ timeout = null;
62
+ if (!immediate) {
63
+ fn.apply(this, args);
64
+ }
65
+ }, wait);
66
+
67
+ if (callNow) {
68
+ fn.apply(this, args);
69
+ }
70
+ };
71
+ }
72
+
73
+ /**
74
+ * 格式化字节大小
75
+ * @param bytes 字节数
76
+ * @param decimals 小数位数
77
+ */
78
+ export function formatBytes(bytes: number, decimals: number = 2): string {
79
+ if (bytes === 0) return '0 Bytes';
80
+
81
+ const k = 1024;
82
+ const dm = decimals < 0 ? 0 : decimals;
83
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
84
+
85
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
86
+
87
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
88
+ }
89
+
90
+ /**
91
+ * 生成UUID
92
+ */
93
+ export function generateUUID(): string {
94
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
95
+ const r = Math.random() * 16 | 0;
96
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
97
+ return v.toString(16);
98
+ });
99
+ }
100
+
101
+ /**
102
+ * 检查浏览器支持的功能
103
+ */
104
+ export const browserCapabilities = {
105
+ /**
106
+ * 检查是否支持摄像头
107
+ */
108
+ hasCamera(): boolean {
109
+ return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
110
+ },
111
+
112
+ /**
113
+ * 检查是否支持WebAssembly
114
+ */
115
+ hasWasm(): boolean {
116
+ return typeof WebAssembly === 'object' &&
117
+ typeof WebAssembly.compile === 'function' &&
118
+ typeof WebAssembly.instantiate === 'function';
119
+ },
120
+
121
+ /**
122
+ * 检查是否支持WebWorker
123
+ */
124
+ hasWebWorker(): boolean {
125
+ return typeof Worker === 'function';
126
+ },
127
+
128
+ /**
129
+ * 检查是否支持WebGL
130
+ */
131
+ hasWebGL(): boolean {
132
+ try {
133
+ const canvas = document.createElement('canvas');
134
+ return !!(
135
+ window.WebGLRenderingContext &&
136
+ (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
137
+ );
138
+ } catch (e) {
139
+ return false;
140
+ }
141
+ },
142
+
143
+ /**
144
+ * 检查是否支持SharedArrayBuffer
145
+ */
146
+ hasSharedArrayBuffer(): boolean {
147
+ return typeof SharedArrayBuffer === 'function';
148
+ },
149
+
150
+ /**
151
+ * 检查是否支持特定的功能
152
+ * @param feature 功能名称
153
+ */
154
+ supports(feature: string): boolean {
155
+ switch (feature.toLowerCase()) {
156
+ case 'camera': return this.hasCamera();
157
+ case 'wasm': return this.hasWasm();
158
+ case 'webworker': case 'worker': return this.hasWebWorker();
159
+ case 'webgl': case 'gl': return this.hasWebGL();
160
+ case 'sharedarraybuffer': case 'sab': return this.hasSharedArrayBuffer();
161
+ default: return false;
162
+ }
163
+ }
164
+ };
165
+
166
+ /**
167
+ * 数组分块
168
+ * @param array 要分块的数组
169
+ * @param chunkSize 每块大小
170
+ */
171
+ export function chunk<T>(array: T[], chunkSize: number): T[][] {
172
+ if (chunkSize < 1) throw new Error('Chunk size must be greater than 0');
173
+
174
+ const result: T[][] = [];
175
+ for (let i = 0; i < array.length; i += chunkSize) {
176
+ result.push(array.slice(i, i + chunkSize));
177
+ }
178
+ return result;
179
+ }
180
+
181
+ /**
182
+ * 安全解析JSON
183
+ * @param text JSON字符串
184
+ * @param fallback 解析失败时的默认值
185
+ */
186
+ export function safeParseJSON<T = any>(text: string, fallback: T): T {
187
+ try {
188
+ return JSON.parse(text) as T;
189
+ } catch (e) {
190
+ return fallback;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * 限制值在指定范围内
196
+ * @param value 要限制的值
197
+ * @param min 最小值
198
+ * @param max 最大值
199
+ */
200
+ export function clamp(value: number, min: number, max: number): number {
201
+ return Math.min(Math.max(value, min), max);
202
+ }
203
+
204
+ /**
205
+ * 等待加载图片
206
+ * @param url 图片URL
207
+ */
208
+ export function loadImage(url: string): Promise<HTMLImageElement> {
209
+ return new Promise((resolve, reject) => {
210
+ const img = new Image();
211
+ img.onload = () => resolve(img);
212
+ img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
213
+ img.src = url;
214
+ });
215
+ }
216
+
217
+ /**
218
+ * 将Blob转换为Base64
219
+ * @param blob Blob对象
220
+ */
221
+ export function blobToBase64(blob: Blob): Promise<string> {
222
+ return new Promise((resolve, reject) => {
223
+ const reader = new FileReader();
224
+ reader.onload = () => resolve(reader.result as string);
225
+ reader.onerror = () => reject(reader.error);
226
+ reader.readAsDataURL(blob);
227
+ });
228
+ }
229
+
230
+ /**
231
+ * 将Base64转换为Blob
232
+ * @param base64 Base64字符串
233
+ * @param contentType 内容类型
234
+ */
235
+ export function base64ToBlob(base64: string, contentType: string = ''): Blob {
236
+ const byteString = atob(base64.split(',')[1]);
237
+ const ab = new ArrayBuffer(byteString.length);
238
+ const ia = new Uint8Array(ab);
239
+
240
+ for (let i = 0; i < byteString.length; i++) {
241
+ ia[i] = byteString.charCodeAt(i);
242
+ }
243
+
244
+ return new Blob([ab], { type: contentType });
245
+ }
246
+
247
+ /**
248
+ * 获取媒体约束
249
+ * @param width 宽度
250
+ * @param height 高度
251
+ * @param facingMode 前后置摄像头
252
+ * @param frameRate 帧率
253
+ */
254
+ export function getMediaConstraints(
255
+ width: number = 1280,
256
+ height: number = 720,
257
+ facingMode: 'user' | 'environment' = 'environment',
258
+ frameRate: number = 30
259
+ ): MediaStreamConstraints {
260
+ return {
261
+ video: {
262
+ width: { ideal: width },
263
+ height: { ideal: height },
264
+ facingMode: { ideal: facingMode },
265
+ frameRate: { ideal: frameRate }
266
+ },
267
+ audio: false
268
+ };
269
+ }
270
+
271
+ /**
272
+ * 检查URL是否有效
273
+ * @param url 要检查的URL
274
+ */
275
+ export function isValidUrl(url: string): boolean {
276
+ try {
277
+ new URL(url);
278
+ return true;
279
+ } catch (e) {
280
+ return false;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * DOM帮助函数
286
+ */
287
+ export const dom = {
288
+ /**
289
+ * 创建元素
290
+ * @param tag 标签名
291
+ * @param attributes 属性
292
+ * @param children 子元素
293
+ */
294
+ createElement<K extends keyof HTMLElementTagNameMap>(
295
+ tag: K,
296
+ attributes: Record<string, any> = {},
297
+ children: (string | Node)[] = []
298
+ ): HTMLElementTagNameMap[K] {
299
+ const element = document.createElement(tag);
300
+
301
+ // 设置属性
302
+ Object.entries(attributes).forEach(([key, value]) => {
303
+ if (key === 'style' && typeof value === 'object') {
304
+ Object.assign(element.style, value);
305
+ } else if (key.startsWith('on') && typeof value === 'function') {
306
+ element.addEventListener(key.substring(2).toLowerCase(), value);
307
+ } else if (key === 'className') {
308
+ element.className = value;
309
+ } else {
310
+ element.setAttribute(key, value);
311
+ }
312
+ });
313
+
314
+ // 添加子元素
315
+ children.forEach(child => {
316
+ if (typeof child === 'string') {
317
+ element.appendChild(document.createTextNode(child));
318
+ } else {
319
+ element.appendChild(child);
320
+ }
321
+ });
322
+
323
+ return element;
324
+ },
325
+
326
+ /**
327
+ * 查找元素
328
+ * @param selector 选择器
329
+ * @param parent 父元素
330
+ */
331
+ find<E extends Element = Element>(
332
+ selector: string,
333
+ parent: Document | Element = document
334
+ ): E | null {
335
+ return parent.querySelector<E>(selector);
336
+ },
337
+
338
+ /**
339
+ * 查找所有元素
340
+ * @param selector 选择器
341
+ * @param parent 父元素
342
+ */
343
+ findAll<E extends Element = Element>(
344
+ selector: string,
345
+ parent: Document | Element = document
346
+ ): E[] {
347
+ return Array.from(parent.querySelectorAll<E>(selector));
348
+ },
349
+
350
+ /**
351
+ * 添加事件监听器
352
+ * @param element 元素
353
+ * @param event 事件名称
354
+ * @param handler 处理函数
355
+ * @param options 选项
356
+ */
357
+ on<K extends keyof HTMLElementEventMap>(
358
+ element: HTMLElement,
359
+ event: K,
360
+ handler: (event: HTMLElementEventMap[K]) => any,
361
+ options?: AddEventListenerOptions
362
+ ): void {
363
+ element.addEventListener(event, handler as EventListener, options);
364
+ },
365
+
366
+ /**
367
+ * 移除事件监听器
368
+ * @param element 元素
369
+ * @param event 事件名称
370
+ * @param handler 处理函数
371
+ * @param options 选项
372
+ */
373
+ off<K extends keyof HTMLElementEventMap>(
374
+ element: HTMLElement,
375
+ event: K,
376
+ handler: (event: HTMLElementEventMap[K]) => any,
377
+ options?: EventListenerOptions
378
+ ): void {
379
+ element.removeEventListener(event, handler as EventListener, options);
380
+ },
381
+
382
+ /**
383
+ * 设置样式
384
+ * @param element 元素
385
+ * @param styles 样式对象
386
+ */
387
+ setStyles(
388
+ element: HTMLElement,
389
+ styles: Partial<CSSStyleDeclaration>
390
+ ): void {
391
+ Object.assign(element.style, styles);
392
+ },
393
+
394
+ /**
395
+ * 添加类名
396
+ * @param element 元素
397
+ * @param classNames 类名
398
+ */
399
+ addClass(
400
+ element: HTMLElement,
401
+ ...classNames: string[]
402
+ ): void {
403
+ element.classList.add(...classNames);
404
+ },
405
+
406
+ /**
407
+ * 移除类名
408
+ * @param element 元素
409
+ * @param classNames 类名
410
+ */
411
+ removeClass(
412
+ element: HTMLElement,
413
+ ...classNames: string[]
414
+ ): void {
415
+ element.classList.remove(...classNames);
416
+ },
417
+
418
+ /**
419
+ * 判断是否包含类名
420
+ * @param element 元素
421
+ * @param className 类名
422
+ */
423
+ hasClass(
424
+ element: HTMLElement,
425
+ className: string
426
+ ): boolean {
427
+ return element.classList.contains(className);
428
+ }
429
+ };