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,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for browser-image-compression
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
declare module 'browser-image-compression' {
|
|
6
|
+
export interface Options {
|
|
7
|
+
maxSizeMB?: number;
|
|
8
|
+
maxWidthOrHeight?: number;
|
|
9
|
+
useWebWorker?: boolean;
|
|
10
|
+
maxIteration?: number;
|
|
11
|
+
quality?: number;
|
|
12
|
+
fileType?: string;
|
|
13
|
+
onProgress?: (progress: number) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function imageCompression(file: File, options?: Options): Promise<File>;
|
|
17
|
+
|
|
18
|
+
export default imageCompression;
|
|
19
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for tesseract.js
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
declare module "tesseract.js" {
|
|
6
|
+
// Based on https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts
|
|
7
|
+
// and https://github.com/naptha/tesseract.js/blob/master/docs/api.md
|
|
8
|
+
|
|
9
|
+
export interface Point {
|
|
10
|
+
x: number
|
|
11
|
+
y: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Bbox {
|
|
15
|
+
x0: number
|
|
16
|
+
y0: number
|
|
17
|
+
x1: number
|
|
18
|
+
y1: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Baseline {
|
|
22
|
+
x0: number
|
|
23
|
+
y0: number
|
|
24
|
+
x1: number
|
|
25
|
+
y1: number
|
|
26
|
+
has_descenders: boolean
|
|
27
|
+
has_ascenders: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Word {
|
|
31
|
+
symbols: Symbol[]
|
|
32
|
+
choices: Choice[]
|
|
33
|
+
text: string
|
|
34
|
+
confidence: number
|
|
35
|
+
baseline: Baseline
|
|
36
|
+
bbox: Bbox
|
|
37
|
+
is_numeric: boolean
|
|
38
|
+
in_dictionary: boolean
|
|
39
|
+
direction: string
|
|
40
|
+
language: string
|
|
41
|
+
is_from_dictionary: boolean
|
|
42
|
+
is_fuzzy: boolean
|
|
43
|
+
is_certain: boolean
|
|
44
|
+
is_bold: boolean
|
|
45
|
+
is_italic: boolean
|
|
46
|
+
is_underlined: boolean
|
|
47
|
+
is_monospace: boolean
|
|
48
|
+
is_serif: boolean
|
|
49
|
+
is_smallcaps: boolean
|
|
50
|
+
font_id: number
|
|
51
|
+
font_size: number
|
|
52
|
+
font_name: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface Symbol {
|
|
56
|
+
choices: Choice[]
|
|
57
|
+
image: null | HTMLImageElement // Or string if it's a path/URL
|
|
58
|
+
text: string
|
|
59
|
+
confidence: number
|
|
60
|
+
baseline: Baseline
|
|
61
|
+
bbox: Bbox
|
|
62
|
+
is_superscript: boolean
|
|
63
|
+
is_subscript: boolean
|
|
64
|
+
is_dropcap: boolean
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface Choice {
|
|
68
|
+
text: string
|
|
69
|
+
confidence: number
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Line {
|
|
73
|
+
words: Word[]
|
|
74
|
+
text: string
|
|
75
|
+
confidence: number
|
|
76
|
+
baseline: Baseline
|
|
77
|
+
bbox: Bbox
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface Paragraph {
|
|
81
|
+
lines: Line[]
|
|
82
|
+
text: string
|
|
83
|
+
confidence: number
|
|
84
|
+
baseline: Baseline
|
|
85
|
+
bbox: Bbox
|
|
86
|
+
is_ltr: boolean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface Block {
|
|
90
|
+
paragraphs: Paragraph[]
|
|
91
|
+
lines: Line[]
|
|
92
|
+
words: Word[]
|
|
93
|
+
text: string
|
|
94
|
+
confidence: number
|
|
95
|
+
baseline: Baseline
|
|
96
|
+
bbox: Bbox
|
|
97
|
+
blocktype: string
|
|
98
|
+
polygon: Point[]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface Page {
|
|
102
|
+
blocks: Block[]
|
|
103
|
+
confidence: number
|
|
104
|
+
html: string // HTML representation of the page
|
|
105
|
+
jobId?: string
|
|
106
|
+
text: string
|
|
107
|
+
lines: Line[]
|
|
108
|
+
oem: string
|
|
109
|
+
operator: string
|
|
110
|
+
paragraphs: Paragraph[]
|
|
111
|
+
psm: string
|
|
112
|
+
symbols: Symbol[]
|
|
113
|
+
version: string
|
|
114
|
+
words: Word[]
|
|
115
|
+
hocr?: string // hOCR output
|
|
116
|
+
tsv?: string // TSV output
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface LoggerMessage {
|
|
120
|
+
jobId?: string
|
|
121
|
+
workerId?: string
|
|
122
|
+
status: string
|
|
123
|
+
progress: number
|
|
124
|
+
userfriendlyText?: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface WorkerOptions {
|
|
128
|
+
langPath?: string
|
|
129
|
+
corePath?: string
|
|
130
|
+
workerPath?: string
|
|
131
|
+
logger?: (message: LoggerMessage) => void // More specific type for logger message <mcreference index="1" link="https://github.com/naptha/tesseract.js/blob/master/docs/api.md"></mcreference>
|
|
132
|
+
errorHandler?: (error: Error) => void
|
|
133
|
+
// Add other options based on documentation if needed
|
|
134
|
+
[key: string]: any // For other less common or dynamic options
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface RecognizeResult {
|
|
138
|
+
data: Page // Use the detailed Page interface
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface DetectResult {
|
|
142
|
+
data: {
|
|
143
|
+
tesseract_script_id: number | null
|
|
144
|
+
script: string | null
|
|
145
|
+
script_confidence: number | null
|
|
146
|
+
orientation_degrees: number | null
|
|
147
|
+
orientation_confidence: number | null
|
|
148
|
+
}
|
|
149
|
+
jobId?: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface ConfigResult {
|
|
153
|
+
data: null
|
|
154
|
+
jobId?: string
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type ImageLike =
|
|
158
|
+
| HTMLImageElement
|
|
159
|
+
| HTMLCanvasElement
|
|
160
|
+
| File
|
|
161
|
+
| string
|
|
162
|
+
| Buffer
|
|
163
|
+
| ImageData // Common image types
|
|
164
|
+
|
|
165
|
+
export interface Worker {
|
|
166
|
+
load(jobId?: string): Promise<ConfigResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
167
|
+
loadLanguage(
|
|
168
|
+
langs?: string | string[],
|
|
169
|
+
jobId?: string
|
|
170
|
+
): Promise<ConfigResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
171
|
+
initialize(
|
|
172
|
+
langs?: string | string[],
|
|
173
|
+
oem?: OEM,
|
|
174
|
+
config?: string | Partial<InitOptions>,
|
|
175
|
+
jobId?: string
|
|
176
|
+
): Promise<ConfigResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
177
|
+
setParameters(
|
|
178
|
+
params: Partial<Parameters>,
|
|
179
|
+
jobId?: string
|
|
180
|
+
): Promise<ConfigResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
181
|
+
recognize(
|
|
182
|
+
image: ImageLike,
|
|
183
|
+
options?: Partial<RecognizeOptions>,
|
|
184
|
+
output?: Partial<OutputFormats>,
|
|
185
|
+
jobId?: string
|
|
186
|
+
): Promise<RecognizeResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
187
|
+
detect(
|
|
188
|
+
image: ImageLike,
|
|
189
|
+
options?: Partial<WorkerOptions>,
|
|
190
|
+
jobId?: string
|
|
191
|
+
): Promise<DetectResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
192
|
+
terminate(jobId?: string): Promise<ConfigResult> // <mcreference index="4" link="https://github.com/naptha/tesseract.js/blob/master/src/index.d.ts"></mcreference>
|
|
193
|
+
// Add other worker methods if present in the version you are targeting
|
|
194
|
+
// Example from docs for other methods like FS operations:
|
|
195
|
+
writeText?(
|
|
196
|
+
path: string,
|
|
197
|
+
text: string,
|
|
198
|
+
jobId?: string
|
|
199
|
+
): Promise<ConfigResult>
|
|
200
|
+
readText?(path: string, jobId?: string): Promise<ConfigResult>
|
|
201
|
+
removeFile?(path: string, jobId?: string): Promise<ConfigResult> // Assuming removeFile also returns ConfigResult or similar
|
|
202
|
+
FS?(method: string, args: any[], jobId?: string): Promise<any> // FS is more generic
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Based on Tesseract's OEM and PSM enums
|
|
206
|
+
export enum OEM {
|
|
207
|
+
TESSERACT_ONLY = 0,
|
|
208
|
+
LSTM_ONLY = 1,
|
|
209
|
+
TESSERACT_LSTM_COMBINED = 2,
|
|
210
|
+
DEFAULT = 3,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export enum PSM {
|
|
214
|
+
OSD_ONLY = 0,
|
|
215
|
+
AUTO_OSD = 1,
|
|
216
|
+
AUTO_ONLY = 2,
|
|
217
|
+
AUTO = 3,
|
|
218
|
+
SINGLE_COLUMN = 4,
|
|
219
|
+
SINGLE_BLOCK_VERT_TEXT = 5,
|
|
220
|
+
SINGLE_BLOCK = 6,
|
|
221
|
+
SINGLE_LINE = 7,
|
|
222
|
+
SINGLE_WORD = 8,
|
|
223
|
+
CIRCLE_WORD = 9,
|
|
224
|
+
SINGLE_CHAR = 10,
|
|
225
|
+
SPARSE_TEXT = 11,
|
|
226
|
+
SPARSE_TEXT_OSD = 12,
|
|
227
|
+
RAW_LINE = 13,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface Parameters {
|
|
231
|
+
tessedit_char_whitelist?: string
|
|
232
|
+
tessedit_pageseg_mode?: PSM
|
|
233
|
+
// Add other Tesseract parameters as needed
|
|
234
|
+
[key: string]: any // For flexibility with other parameters
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface RecognizeOptions {
|
|
238
|
+
rectangle?: Bbox // For recognizing a specific region
|
|
239
|
+
rectangles?: Bbox[] // For recognizing multiple regions
|
|
240
|
+
// Add other recognize specific options
|
|
241
|
+
[key: string]: any
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface OutputFormats {
|
|
245
|
+
text?: boolean
|
|
246
|
+
blocks?: boolean
|
|
247
|
+
hocr?: boolean
|
|
248
|
+
tsv?: boolean
|
|
249
|
+
pdf?: boolean // If PDF output is supported
|
|
250
|
+
// Add other output formats
|
|
251
|
+
[key: string]: any
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface InitOptions {
|
|
255
|
+
load_system_dawg?: boolean
|
|
256
|
+
load_freq_dawg?: boolean
|
|
257
|
+
load_punc_dawg?: boolean
|
|
258
|
+
load_number_dawg?: boolean
|
|
259
|
+
load_unambig_dawg?: boolean
|
|
260
|
+
load_bigram_dawg?: boolean
|
|
261
|
+
load_fixed_length_dawgs?: boolean
|
|
262
|
+
// Add other init-only parameters
|
|
263
|
+
[key: string]: any
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function createWorker(options?: Partial<WorkerOptions>): Worker // 修正返回类型为 Worker 而非 Promise<Worker>
|
|
267
|
+
export function setLogging(logging: boolean): void
|
|
268
|
+
export function recognize(
|
|
269
|
+
image: ImageLike,
|
|
270
|
+
langs?: string | string[],
|
|
271
|
+
options?: Partial<RecognizeOptions & WorkerOptions>
|
|
272
|
+
): Promise<RecognizeResult>
|
|
273
|
+
export function detect(
|
|
274
|
+
image: ImageLike,
|
|
275
|
+
options?: Partial<WorkerOptions>
|
|
276
|
+
): Promise<DetectResult>
|
|
277
|
+
|
|
278
|
+
// 正确导出 OEM 和 PSM 枚举
|
|
279
|
+
export { OEM, PSM }
|
|
280
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "id-scanner-lib",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "1.6.2",
|
|
5
4
|
"description": "Browser-based ID card, QR code, and face recognition scanner with liveness detection",
|
|
6
5
|
"main": "dist/id-scanner-lib.js",
|
|
7
6
|
"module": "dist/id-scanner-lib.esm.js",
|
|
@@ -20,7 +19,7 @@
|
|
|
20
19
|
},
|
|
21
20
|
"scripts": {
|
|
22
21
|
"build": "rimraf dist && rollup -c rollup.config.js",
|
|
23
|
-
"dev": "
|
|
22
|
+
"dev": "vite build --watch",
|
|
24
23
|
"test": "jest",
|
|
25
24
|
"lint": "eslint --ext .ts,.js src",
|
|
26
25
|
"format": "prettier --write \"src/**/*.{ts,js}\"",
|
|
@@ -49,9 +48,9 @@
|
|
|
49
48
|
"author": "Your Name",
|
|
50
49
|
"license": "MIT",
|
|
51
50
|
"bugs": {
|
|
52
|
-
"url": "https://github.com/
|
|
51
|
+
"url": "https://github.com/yourusername/id-scanner-lib/issues"
|
|
53
52
|
},
|
|
54
|
-
"homepage": "https://github.com/
|
|
53
|
+
"homepage": "https://github.com/yourusername/id-scanner-lib#readme",
|
|
55
54
|
"dependencies": {
|
|
56
55
|
"@tensorflow/tfjs": "^4.16.0",
|
|
57
56
|
"@vladmandic/face-api": "^1.7.13",
|
|
@@ -61,20 +60,25 @@
|
|
|
61
60
|
"@babel/core": "^7.23.7",
|
|
62
61
|
"@babel/preset-env": "^7.23.7",
|
|
63
62
|
"@babel/preset-typescript": "^7.23.7",
|
|
63
|
+
"@eslint/js": "^10.0.1",
|
|
64
64
|
"@rollup/plugin-babel": "^6.0.4",
|
|
65
65
|
"@rollup/plugin-commonjs": "^25.0.7",
|
|
66
66
|
"@rollup/plugin-json": "^6.0.1",
|
|
67
67
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
68
|
-
"@rollup/plugin-terser": "^0.
|
|
68
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
69
69
|
"@rollup/plugin-typescript": "^11.1.5",
|
|
70
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
70
71
|
"@types/jest": "^29.5.11",
|
|
71
|
-
"@
|
|
72
|
-
"@typescript-eslint/
|
|
72
|
+
"@types/node": "^20.0.0",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
74
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
73
75
|
"dts-bundle-generator": "^9.2.4",
|
|
74
|
-
"eslint": "^
|
|
76
|
+
"eslint": "^9.39.4",
|
|
75
77
|
"eslint-config-prettier": "^9.1.0",
|
|
76
78
|
"eslint-plugin-prettier": "^5.1.3",
|
|
79
|
+
"globals": "^17.4.0",
|
|
77
80
|
"jest": "^29.7.0",
|
|
81
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
78
82
|
"prettier": "^3.2.4",
|
|
79
83
|
"rimraf": "^5.0.5",
|
|
80
84
|
"rollup": "^4.9.6",
|
|
@@ -87,10 +91,16 @@
|
|
|
87
91
|
"ts-jest": "^29.1.1",
|
|
88
92
|
"tslib": "^2.6.2",
|
|
89
93
|
"typedoc": "^0.25.7",
|
|
90
|
-
"typescript": "^5.
|
|
91
|
-
"
|
|
94
|
+
"typescript": "^5.9.3",
|
|
95
|
+
"typescript-eslint": "^8.57.1",
|
|
96
|
+
"vite-plugin-dts": "^4.5.4",
|
|
97
|
+
"vitepress": "^1.6.4"
|
|
92
98
|
},
|
|
93
99
|
"engines": {
|
|
94
100
|
"node": ">=14.0.0"
|
|
101
|
+
},
|
|
102
|
+
"overrides": {
|
|
103
|
+
"esbuild": "^0.27.0",
|
|
104
|
+
"vite": "^6.0.0"
|
|
95
105
|
}
|
|
96
106
|
}
|
|
@@ -671,13 +671,28 @@ export class CameraManager extends EventEmitter {
|
|
|
671
671
|
dispose(): void {
|
|
672
672
|
this.stop();
|
|
673
673
|
|
|
674
|
+
// 停止并释放媒体流
|
|
675
|
+
this.stopMediaStream();
|
|
676
|
+
|
|
674
677
|
if (this.canvas) {
|
|
678
|
+
// 清空 canvas 内容
|
|
679
|
+
const ctx = this.canvas.getContext('2d');
|
|
680
|
+
if (ctx) ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
681
|
+
this.canvas.width = 0;
|
|
682
|
+
this.canvas.height = 0;
|
|
675
683
|
this.canvas = null;
|
|
676
684
|
this.canvasCtx = null;
|
|
677
685
|
}
|
|
678
686
|
|
|
679
|
-
|
|
687
|
+
// 释放 video 元素
|
|
688
|
+
if (this.videoElement) {
|
|
689
|
+
this.videoElement.srcObject = null;
|
|
690
|
+
this.videoElement.load();
|
|
691
|
+
this.videoElement = null;
|
|
692
|
+
}
|
|
693
|
+
|
|
680
694
|
this.status = CameraStatus.NOT_INITIALIZED;
|
|
695
|
+
this.logger.debug('CameraManager', 'Camera resources disposed');
|
|
681
696
|
}
|
|
682
697
|
|
|
683
698
|
/**
|
package/src/core/config.ts
CHANGED
|
@@ -254,6 +254,43 @@ export class ConfigManager {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 检查模块是否启用
|
|
260
|
+
* @param moduleName 模块名称
|
|
261
|
+
*/
|
|
262
|
+
public isModuleEnabled(moduleName: string): boolean {
|
|
263
|
+
const key = `modules.${moduleName}.enabled`;
|
|
264
|
+
return this.get(key) ?? false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 全局配置接口
|
|
270
|
+
*/
|
|
271
|
+
export interface GlobalConfig {
|
|
272
|
+
/** 版本号 */
|
|
273
|
+
version: string;
|
|
274
|
+
/** 调试模式 */
|
|
275
|
+
debug: boolean;
|
|
276
|
+
/** 日志级别 */
|
|
277
|
+
logLevel: string;
|
|
278
|
+
/** 摄像头配置 */
|
|
279
|
+
camera: {
|
|
280
|
+
resolution: { width: number; height: number };
|
|
281
|
+
frameRate: number;
|
|
282
|
+
facingMode: string;
|
|
283
|
+
};
|
|
284
|
+
/** 性能配置 */
|
|
285
|
+
performance: {
|
|
286
|
+
useCache: boolean;
|
|
287
|
+
};
|
|
288
|
+
/** 模块配置 */
|
|
289
|
+
modules: {
|
|
290
|
+
face: { enabled: boolean };
|
|
291
|
+
idcard: { enabled: boolean };
|
|
292
|
+
qrcode: { enabled: boolean };
|
|
293
|
+
};
|
|
257
294
|
}
|
|
258
295
|
|
|
259
296
|
/**
|
package/src/core/errors.ts
CHANGED
|
@@ -31,9 +31,9 @@ export class IDScannerError extends Error {
|
|
|
31
31
|
// 设置错误原因
|
|
32
32
|
this.cause = options?.cause;
|
|
33
33
|
|
|
34
|
-
// 捕获堆栈
|
|
35
|
-
if (Error.captureStackTrace) {
|
|
36
|
-
Error.captureStackTrace(this, this.constructor);
|
|
34
|
+
// 捕获堆栈 (Node.js专有,浏览器环境忽略)
|
|
35
|
+
if (typeof (Error as any).captureStackTrace === 'function') {
|
|
36
|
+
(Error as any).captureStackTrace(this, this.constructor);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file EventEmitter 测试
|
|
3
|
+
* @description 测试事件发射器
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EventEmitter } from './event-emitter';
|
|
7
|
+
|
|
8
|
+
describe('EventEmitter', () => {
|
|
9
|
+
let emitter: EventEmitter;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
emitter = new EventEmitter();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
emitter.removeAllListeners();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should register and emit event', () => {
|
|
20
|
+
let called = false;
|
|
21
|
+
emitter.on('test', () => { called = true; });
|
|
22
|
+
emitter.emit('test');
|
|
23
|
+
expect(called).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should register one-time event', () => {
|
|
27
|
+
let count = 0;
|
|
28
|
+
emitter.once('test', () => { count++; });
|
|
29
|
+
emitter.emit('test');
|
|
30
|
+
emitter.emit('test');
|
|
31
|
+
expect(count).toBe(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should remove listener', () => {
|
|
35
|
+
let called = false;
|
|
36
|
+
const fn = () => { called = true; };
|
|
37
|
+
emitter.on('test', fn);
|
|
38
|
+
emitter.off('test', fn);
|
|
39
|
+
emitter.emit('test');
|
|
40
|
+
expect(called).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 加载状态管理测试
|
|
3
|
+
* @description 测试 LoadingStateManager
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LoadingStateManager, LoadingState } from './loading-state';
|
|
7
|
+
|
|
8
|
+
describe('LoadingStateManager', () => {
|
|
9
|
+
let manager: LoadingStateManager;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
manager = new LoadingStateManager();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
manager.dispose();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should start with idle state', () => {
|
|
20
|
+
expect(manager.getState()).toBe(LoadingState.IDLE);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should start loading', () => {
|
|
24
|
+
manager.startLoading(5);
|
|
25
|
+
|
|
26
|
+
expect(manager.getState()).toBe(LoadingState.LOADING);
|
|
27
|
+
expect(manager.getProgress().progress).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should track model loading', () => {
|
|
31
|
+
manager.startLoading(5);
|
|
32
|
+
manager.startModelLoading('model1');
|
|
33
|
+
|
|
34
|
+
expect(manager.getProgress().loadingModel).toBe('model1');
|
|
35
|
+
|
|
36
|
+
manager.completeModelLoading('model1');
|
|
37
|
+
expect(manager.getProgress().loadedModels).toContain('model1');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should complete loading', () => {
|
|
41
|
+
manager.startLoading(2);
|
|
42
|
+
manager.completeModelLoading('model1');
|
|
43
|
+
manager.completeModelLoading('model2');
|
|
44
|
+
manager.complete();
|
|
45
|
+
|
|
46
|
+
expect(manager.getState()).toBe(LoadingState.READY);
|
|
47
|
+
expect(manager.isReady()).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle failure', () => {
|
|
51
|
+
manager.startLoading(2);
|
|
52
|
+
manager.fail('Network error');
|
|
53
|
+
|
|
54
|
+
expect(manager.getState()).toBe(LoadingState.ERROR);
|
|
55
|
+
expect(manager.getProgress().error).toBe('Network error');
|
|
56
|
+
expect(manager.hasError()).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should dispose correctly', () => {
|
|
60
|
+
manager.startLoading(2);
|
|
61
|
+
manager.complete();
|
|
62
|
+
manager.dispose();
|
|
63
|
+
|
|
64
|
+
expect(manager.getState()).toBe(LoadingState.DISPOSED);
|
|
65
|
+
expect(manager.isReady()).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|