@mleonard9/vin-scanner 1.2.6 → 1.4.0

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 (70) hide show
  1. package/README.md +287 -16
  2. package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +76 -23
  3. package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt +69 -26
  4. package/ios/VisionCameraBarcodeScanner.m +60 -6
  5. package/ios/VisionCameraTextRecognition.m +67 -13
  6. package/lib/commonjs/ManualVinInput.js +147 -0
  7. package/lib/commonjs/ManualVinInput.js.map +1 -0
  8. package/lib/commonjs/PendingVinBanner.js +120 -0
  9. package/lib/commonjs/PendingVinBanner.js.map +1 -0
  10. package/lib/commonjs/TextVinPrompt.js +132 -0
  11. package/lib/commonjs/TextVinPrompt.js.map +1 -0
  12. package/lib/commonjs/haptics.js +36 -0
  13. package/lib/commonjs/haptics.js.map +1 -0
  14. package/lib/commonjs/index.js +196 -15
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/scanBarcodes.js +14 -3
  17. package/lib/commonjs/scanBarcodes.js.map +1 -1
  18. package/lib/commonjs/scanText.js +14 -3
  19. package/lib/commonjs/scanText.js.map +1 -1
  20. package/lib/commonjs/useVinScanner.js +205 -33
  21. package/lib/commonjs/useVinScanner.js.map +1 -1
  22. package/lib/commonjs/vinUtils.js +165 -32
  23. package/lib/commonjs/vinUtils.js.map +1 -1
  24. package/lib/module/ManualVinInput.js +139 -0
  25. package/lib/module/ManualVinInput.js.map +1 -0
  26. package/lib/module/PendingVinBanner.js +112 -0
  27. package/lib/module/PendingVinBanner.js.map +1 -0
  28. package/lib/module/TextVinPrompt.js +124 -0
  29. package/lib/module/TextVinPrompt.js.map +1 -0
  30. package/lib/module/haptics.js +27 -0
  31. package/lib/module/haptics.js.map +1 -0
  32. package/lib/module/index.js +179 -16
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/scanBarcodes.js +14 -3
  35. package/lib/module/scanBarcodes.js.map +1 -1
  36. package/lib/module/scanText.js +14 -3
  37. package/lib/module/scanText.js.map +1 -1
  38. package/lib/module/useVinScanner.js +206 -34
  39. package/lib/module/useVinScanner.js.map +1 -1
  40. package/lib/module/vinUtils.js +165 -32
  41. package/lib/module/vinUtils.js.map +1 -1
  42. package/lib/typescript/src/ManualVinInput.d.ts +11 -0
  43. package/lib/typescript/src/ManualVinInput.d.ts.map +1 -0
  44. package/lib/typescript/src/PendingVinBanner.d.ts +17 -0
  45. package/lib/typescript/src/PendingVinBanner.d.ts.map +1 -0
  46. package/lib/typescript/src/TextVinPrompt.d.ts +20 -0
  47. package/lib/typescript/src/TextVinPrompt.d.ts.map +1 -0
  48. package/lib/typescript/src/haptics.d.ts +4 -0
  49. package/lib/typescript/src/haptics.d.ts.map +1 -0
  50. package/lib/typescript/src/index.d.ts +4 -1
  51. package/lib/typescript/src/index.d.ts.map +1 -1
  52. package/lib/typescript/src/scanBarcodes.d.ts.map +1 -1
  53. package/lib/typescript/src/scanText.d.ts.map +1 -1
  54. package/lib/typescript/src/types.d.ts +136 -7
  55. package/lib/typescript/src/types.d.ts.map +1 -1
  56. package/lib/typescript/src/useVinScanner.d.ts +3 -1
  57. package/lib/typescript/src/useVinScanner.d.ts.map +1 -1
  58. package/lib/typescript/src/vinUtils.d.ts +12 -3
  59. package/lib/typescript/src/vinUtils.d.ts.map +1 -1
  60. package/package.json +8 -2
  61. package/src/ManualVinInput.tsx +145 -0
  62. package/src/PendingVinBanner.tsx +128 -0
  63. package/src/TextVinPrompt.tsx +139 -0
  64. package/src/haptics.ts +32 -0
  65. package/src/index.tsx +203 -24
  66. package/src/scanBarcodes.ts +16 -4
  67. package/src/scanText.ts +16 -4
  68. package/src/types.ts +140 -11
  69. package/src/useVinScanner.ts +232 -39
  70. package/src/vinUtils.ts +210 -79
@@ -1,13 +1,24 @@
1
- import { useMemo, useRef } from 'react';
1
+ import { useMemo, useRef, useState, useCallback } from 'react';
2
2
  import { useFrameProcessor } from 'react-native-vision-camera';
3
3
  import { useRunOnJS } from 'react-native-worklets-core';
4
4
  import { createBarcodeScannerPlugin } from './scanBarcodes';
5
5
  import { createTextRecognitionPlugin } from './scanText';
6
6
  import { buildVinCandidates, pickFirstCandidate, resolveOptions } from './vinUtils';
7
+ import { triggerSuccessHaptic, triggerSoftHaptic } from './haptics';
7
8
  export function useVinScanner(options) {
8
9
  const resolvedOptions = useMemo(() => resolveOptions(options), [options]);
9
10
  const lastFrameTimestampRef = useRef(0);
10
11
  const frameCounterRef = useRef(0);
12
+ const lastEmittedVin = useRef(null);
13
+ const lastEmitTimestamp = useRef(0);
14
+ const [pendingTextCandidates, setPendingTextCandidates] = useState([]);
15
+ const pendingTextTimestampRef = useRef(null);
16
+ const pendingTextRef = useRef([]);
17
+ const pendingTimerRef = useRef(null);
18
+ const hapticsEnabled = (options === null || options === void 0 ? void 0 : options.haptics) ?? true;
19
+ const sessionSeen = useRef(new Set());
20
+ const noBarcodeFrameCount = useRef(0);
21
+ const useBarcodeFallback = useRef(false);
11
22
  const barcodeScanner = useMemo(() => {
12
23
  if (!resolvedOptions.barcode.enabled) {
13
24
  return null;
@@ -20,9 +31,6 @@ export function useVinScanner(options) {
20
31
  }
21
32
  return createTextRecognitionPlugin({
22
33
  language: resolvedOptions.text.language,
23
- // We can use a broad validation pattern here to help the native side
24
- // filter obvious non-VIN text, improving bridge performance.
25
- // VINs are 17 chars alphanumeric.
26
34
  validationPattern: '[A-HJ-NPR-Z0-9]{10,}'
27
35
  });
28
36
  }, [resolvedOptions.text]);
@@ -31,6 +39,28 @@ export function useVinScanner(options) {
31
39
  options.onResult(result, event);
32
40
  }
33
41
  }, [options === null || options === void 0 ? void 0 : options.onResult]);
42
+ const emitTextPending = useRunOnJS((pending, ts) => {
43
+ pendingTextTimestampRef.current = ts;
44
+ pendingTextRef.current = pending;
45
+ setPendingTextCandidates(pending);
46
+ if (pendingTimerRef.current) {
47
+ clearTimeout(pendingTimerRef.current);
48
+ }
49
+ const ttl = resolvedOptions.text.pendingTtlMs;
50
+ if (ttl > 0) {
51
+ pendingTimerRef.current = setTimeout(() => {
52
+ pendingTextTimestampRef.current = null;
53
+ pendingTextRef.current = [];
54
+ setPendingTextCandidates([]);
55
+ }, ttl);
56
+ }
57
+ if (typeof (options === null || options === void 0 ? void 0 : options.onTextPending) === 'function') {
58
+ options.onTextPending(pending);
59
+ }
60
+ if (hapticsEnabled) {
61
+ triggerSoftHaptic();
62
+ }
63
+ }, [options === null || options === void 0 ? void 0 : options.onTextPending, resolvedOptions.text.pendingTtlMs, hapticsEnabled]);
34
64
  const frameProcessor = useFrameProcessor(frame => {
35
65
  'worklet';
36
66
 
@@ -44,58 +74,200 @@ export function useVinScanner(options) {
44
74
  lastFrameTimestampRef.current = now;
45
75
  }
46
76
  const orientationOverride = resolvedOptions.detection.forceOrientation ?? null;
47
- const barcodeArgs = orientationOverride ? {
48
- orientation: orientationOverride
49
- } : undefined;
50
- const barcodes = barcodeScanner ? barcodeScanner.scanBarcodes(frame, barcodeArgs) : [];
77
+ const regionOverride = resolvedOptions.detection.scanRegion ?? null;
51
78
 
52
- // Performance Optimization:
53
- // If we found a valid VIN in the barcode, and we only need the "first" result,
54
- // skip the expensive text recognition step entirely.
55
- let hasBarcodeVin = false;
56
- if (barcodes && barcodes.length > 0) {
57
- const tempCandidates = buildVinCandidates({
58
- barcodes,
59
- textBlocks: [],
60
- timestamp: now
61
- }, resolvedOptions);
62
- if (tempCandidates.length > 0) {
63
- hasBarcodeVin = true;
79
+ // Frame quality gate (luma + sharpness)
80
+ const minLuma = resolvedOptions.detection.minLuma;
81
+ const minSharpness = resolvedOptions.detection.minSharpness;
82
+ if (minLuma > 0 || minSharpness > 0) {
83
+ const planes = frame === null || frame === void 0 ? void 0 : frame.planes;
84
+ if ((planes === null || planes === void 0 ? void 0 : planes.length) > 0) {
85
+ const yPlane = planes[0];
86
+ const bytes = yPlane === null || yPlane === void 0 ? void 0 : yPlane.bytes;
87
+ const width = yPlane === null || yPlane === void 0 ? void 0 : yPlane.width;
88
+ const height = yPlane === null || yPlane === void 0 ? void 0 : yPlane.height;
89
+ const stride = yPlane === null || yPlane === void 0 ? void 0 : yPlane.bytesPerRow;
90
+ if (bytes && width && height && stride) {
91
+ let lumaSum = 0;
92
+ const sampleStep = 8;
93
+ const sampleCount = Math.floor(bytes.length / sampleStep);
94
+ for (let i = 0; i < bytes.length; i += sampleStep) {
95
+ lumaSum += bytes[i];
96
+ }
97
+ const meanLuma = lumaSum / Math.max(1, sampleCount);
98
+ if (minLuma > 0 && meanLuma < minLuma) {
99
+ return;
100
+ }
101
+ if (minSharpness > 0) {
102
+ const step = Math.max(2, Math.floor(width / 96));
103
+ let sharpAccum = 0;
104
+ let sharpCount = 0;
105
+ for (let y = step; y < height - step; y += step) {
106
+ const row = y * stride;
107
+ for (let x = step; x < width - step; x += step) {
108
+ const idx = row + x;
109
+ const gx = bytes[idx + 1] - bytes[idx - 1];
110
+ const gy = bytes[idx + stride] - bytes[idx - stride];
111
+ sharpAccum += Math.abs(gx) + Math.abs(gy);
112
+ sharpCount += 1;
113
+ }
114
+ }
115
+ const sharpness = sharpAccum / Math.max(1, sharpCount);
116
+ if (sharpness < minSharpness) {
117
+ return;
118
+ }
119
+ }
120
+ }
64
121
  }
65
122
  }
66
- const textArgs = orientationOverride ? {
67
- orientation: orientationOverride
123
+ const barcodeArgs = orientationOverride || regionOverride ? {
124
+ ...(orientationOverride && {
125
+ orientation: orientationOverride
126
+ }),
127
+ ...(regionOverride && {
128
+ scanRegion: regionOverride
129
+ })
130
+ } : undefined;
131
+
132
+ // Start performance tracking
133
+ const t0 = Date.now();
134
+ const barcodes = barcodeScanner ? barcodeScanner.scanBarcodes(frame, useBarcodeFallback.current ? {
135
+ all: true
136
+ } : barcodeArgs) ?? [] : [];
137
+ const t1 = Date.now();
138
+ const textArgs = orientationOverride || regionOverride ? {
139
+ ...(orientationOverride && {
140
+ orientation: orientationOverride
141
+ }),
142
+ ...(regionOverride && {
143
+ scanRegion: regionOverride
144
+ })
68
145
  } : undefined;
69
146
  frameCounterRef.current += 1;
70
147
  const frameIndex = frameCounterRef.current;
71
148
  const textScanInterval = resolvedOptions.detection.textScanInterval;
72
- const shouldRunText = !hasBarcodeVin &&
73
- // Skip if we already have a VIN from barcode
74
- textScanner && (textScanInterval <= 1 || frameIndex % textScanInterval === 0);
75
- const textBlocks = shouldRunText && textScanner ? textScanner.scanText(frame, textArgs) : [];
149
+ const shouldRunText = textScanner && (textScanInterval <= 1 || frameIndex % textScanInterval === 0);
150
+ let textBlocks = shouldRunText && textScanner ? textScanner.scanText(frame, textArgs) ?? [] : [];
151
+
152
+ // Two-pass OCR: if we skipped this frame or got nothing and barcodes are empty, run one immediate OCR pass.
153
+ if (textScanner && textBlocks.length === 0 && barcodes.length === 0 && !shouldRunText) {
154
+ textBlocks = textScanner.scanText(frame, textArgs) ?? [];
155
+ }
156
+ const t2 = Date.now();
76
157
  const payload = {
77
158
  barcodes: barcodes ?? [],
78
159
  textBlocks: textBlocks ?? [],
79
160
  timestamp: now
80
161
  };
81
162
  const candidates = buildVinCandidates(payload, resolvedOptions);
82
- const best = pickFirstCandidate(candidates, resolvedOptions);
83
- const mode = resolvedOptions.detection.resultMode;
84
- const normalizedResult = mode === 'all' ? candidates.length > 0 ? candidates : null : best ?? null;
163
+ const filtered = candidates.filter(c => c.confidence >= resolvedOptions.detection.minConfidence);
164
+ const barcodeCandidates = filtered.filter(c => c.source === 'barcode');
165
+ const textCandidates = filtered.filter(c => c.source === 'text');
166
+
167
+ // Adaptive barcode fallback: if no barcode hits for N frames, scan all formats.
168
+ if (barcodeCandidates.length === 0) {
169
+ noBarcodeFrameCount.current += 1;
170
+ if (noBarcodeFrameCount.current >= resolvedOptions.detection.barcodeFallbackAfter) {
171
+ useBarcodeFallback.current = true;
172
+ }
173
+ } else {
174
+ noBarcodeFrameCount.current = 0;
175
+ useBarcodeFallback.current = false;
176
+ }
177
+ const firstCandidate = pickFirstCandidate(candidates, resolvedOptions);
178
+ const t3 = Date.now();
179
+ const duration = Date.now() - now;
85
180
  const event = {
86
- mode,
87
181
  timestamp: payload.timestamp,
182
+ duration,
88
183
  candidates,
89
- best,
184
+ firstCandidate,
90
185
  raw: {
91
186
  barcodes: payload.barcodes,
92
187
  textBlocks: payload.textBlocks
188
+ },
189
+ performance: {
190
+ barcodeMs: t1 - t0,
191
+ textMs: t2 - t1,
192
+ validationMs: t3 - t2,
193
+ totalMs: t3 - t0
194
+ }
195
+ };
196
+
197
+ // If a barcode is present, emit immediately (highest confidence path)
198
+ if (barcodeCandidates.length > 0) {
199
+ const barcodeFirst = pickFirstCandidate(barcodeCandidates, resolvedOptions);
200
+ if (barcodeFirst) {
201
+ const debounceMs = resolvedOptions.duplicateDebounceMs ?? 1500;
202
+ const isDuplicate = barcodeFirst.value === lastEmittedVin.current && now - lastEmitTimestamp.current < debounceMs;
203
+ if (!isDuplicate) {
204
+ lastEmittedVin.current = barcodeFirst.value;
205
+ lastEmitTimestamp.current = now;
206
+ sessionSeen.current.add(barcodeFirst.value);
207
+ emitResult(barcodeCandidates, {
208
+ ...event,
209
+ candidates: barcodeCandidates,
210
+ firstCandidate: barcodeFirst
211
+ });
212
+ if (hapticsEnabled) {
213
+ triggerSuccessHaptic();
214
+ }
215
+ }
216
+ }
217
+ return;
218
+ }
219
+
220
+ // For text-only detections, optionally require user confirmation
221
+ if (resolvedOptions.text.requireConfirmation && textCandidates.length > 0) {
222
+ emitTextPending(textCandidates, payload.timestamp);
223
+ return;
224
+ }
225
+
226
+ // Default behavior: emit first candidate (text)
227
+ if (firstCandidate) {
228
+ const debounceMs = resolvedOptions.duplicateDebounceMs ?? 1500;
229
+ const isDuplicate = firstCandidate.value === lastEmittedVin.current && now - lastEmitTimestamp.current < debounceMs;
230
+ const seen = sessionSeen.current.has(firstCandidate.value);
231
+ if (!isDuplicate && !seen) {
232
+ lastEmittedVin.current = firstCandidate.value;
233
+ lastEmitTimestamp.current = now;
234
+ sessionSeen.current.add(firstCandidate.value);
235
+ emitResult(candidates, event);
236
+ }
237
+ }
238
+ }, [barcodeScanner, textScanner, emitResult, emitTextPending, resolvedOptions]);
239
+ const confirmTextCandidate = useCallback(vinValue => {
240
+ const pending = pendingTextRef.current;
241
+ if (!pending.length) return;
242
+ const selected = vinValue && pending.find(c => c.value === vinValue) || pending[0];
243
+ const ts = pendingTextTimestampRef.current ?? Date.now();
244
+
245
+ // Clear pending state
246
+ if (pendingTimerRef.current) {
247
+ clearTimeout(pendingTimerRef.current);
248
+ pendingTimerRef.current = null;
249
+ }
250
+ pendingTextTimestampRef.current = null;
251
+ pendingTextRef.current = [];
252
+ setPendingTextCandidates([]);
253
+ const event = {
254
+ timestamp: ts,
255
+ duration: 0,
256
+ candidates: pending,
257
+ firstCandidate: selected,
258
+ raw: {
259
+ barcodes: [],
260
+ textBlocks: []
93
261
  }
94
262
  };
95
- emitResult(normalizedResult, event);
96
- }, [barcodeScanner, textScanner, emitResult, resolvedOptions]);
263
+ emitResult(pending, event);
264
+ lastEmittedVin.current = (selected === null || selected === void 0 ? void 0 : selected.value) ?? null;
265
+ lastEmitTimestamp.current = Date.now();
266
+ }, [emitResult]);
97
267
  return {
98
- frameProcessor
268
+ frameProcessor,
269
+ pendingTextCandidates,
270
+ confirmTextCandidate
99
271
  };
100
272
  }
101
273
  //# sourceMappingURL=useVinScanner.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useMemo","useRef","useFrameProcessor","useRunOnJS","createBarcodeScannerPlugin","createTextRecognitionPlugin","buildVinCandidates","pickFirstCandidate","resolveOptions","useVinScanner","options","resolvedOptions","lastFrameTimestampRef","frameCounterRef","barcodeScanner","barcode","enabled","formats","textScanner","text","language","validationPattern","emitResult","result","event","onResult","frameProcessor","frame","now","timestamp","Date","maxFps","detection","maxFrameRate","minInterval","current","orientationOverride","forceOrientation","barcodeArgs","orientation","undefined","barcodes","scanBarcodes","hasBarcodeVin","length","tempCandidates","textBlocks","textArgs","frameIndex","textScanInterval","shouldRunText","scanText","payload","candidates","best","mode","resultMode","normalizedResult","raw"],"sourceRoot":"../../src","sources":["useVinScanner.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useMemo","useRef","useState","useCallback","useFrameProcessor","useRunOnJS","createBarcodeScannerPlugin","createTextRecognitionPlugin","buildVinCandidates","pickFirstCandidate","resolveOptions","triggerSuccessHaptic","triggerSoftHaptic","useVinScanner","options","resolvedOptions","lastFrameTimestampRef","frameCounterRef","lastEmittedVin","lastEmitTimestamp","pendingTextCandidates","setPendingTextCandidates","pendingTextTimestampRef","pendingTextRef","pendingTimerRef","hapticsEnabled","haptics","sessionSeen","Set","noBarcodeFrameCount","useBarcodeFallback","barcodeScanner","barcode","enabled","formats","textScanner","text","language","validationPattern","emitResult","result","event","onResult","emitTextPending","pending","ts","current","clearTimeout","ttl","pendingTtlMs","setTimeout","onTextPending","frameProcessor","frame","now","timestamp","Date","maxFps","detection","maxFrameRate","minInterval","orientationOverride","forceOrientation","regionOverride","scanRegion","minLuma","minSharpness","planes","length","yPlane","bytes","width","height","stride","bytesPerRow","lumaSum","sampleStep","sampleCount","Math","floor","i","meanLuma","max","step","sharpAccum","sharpCount","y","row","x","idx","gx","gy","abs","sharpness","barcodeArgs","orientation","undefined","t0","barcodes","scanBarcodes","all","t1","textArgs","frameIndex","textScanInterval","shouldRunText","textBlocks","scanText","t2","payload","candidates","filtered","filter","c","confidence","minConfidence","barcodeCandidates","source","textCandidates","barcodeFallbackAfter","firstCandidate","t3","duration","raw","performance","barcodeMs","textMs","validationMs","totalMs","barcodeFirst","debounceMs","duplicateDebounceMs","isDuplicate","value","add","requireConfirmation","seen","has","confirmTextCandidate","vinValue","selected","find"],"sourceRoot":"../../src","sources":["useVinScanner.ts"],"mappings":"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","ignoreList":[]}
@@ -18,25 +18,42 @@
18
18
  const DEFAULT_RESOLVED_OPTIONS = {
19
19
  barcode: {
20
20
  enabled: true,
21
- formats: ['all']
21
+ formats: ['code-39', 'code-128', 'pdf-417']
22
22
  },
23
23
  text: {
24
24
  enabled: true,
25
- language: 'latin'
25
+ language: 'latin',
26
+ requireConfirmation: false,
27
+ pendingTtlMs: 5000
26
28
  },
27
29
  detection: {
28
- resultMode: 'first',
29
- textScanInterval: 1,
30
+ textScanInterval: 3,
30
31
  maxFrameRate: 30,
31
- forceOrientation: null
32
- }
32
+ forceOrientation: null,
33
+ scanRegion: {
34
+ x: 0.15,
35
+ y: 0.15,
36
+ width: 0.7,
37
+ height: 0.7
38
+ },
39
+ minLuma: 30,
40
+ minSharpness: 12,
41
+ minConfidence: 0.6,
42
+ barcodeFallbackAfter: 45
43
+ },
44
+ showOverlay: false,
45
+ overlayColors: {
46
+ high: "#00FF00",
47
+ medium: "#FFFF00",
48
+ low: "#FF0000"
49
+ },
50
+ duplicateDebounceMs: 1500
33
51
  };
34
52
  export const resolveOptions = options => {
35
53
  'worklet';
36
54
 
37
- var _options$detection, _options$detection2, _options$barcode, _options$barcode2, _options$text, _options$text2, _options$detection3, _options$detection4;
38
- const resultMode = (options === null || options === void 0 || (_options$detection = options.detection) === null || _options$detection === void 0 ? void 0 : _options$detection.resultMode) ?? DEFAULT_RESOLVED_OPTIONS.detection.resultMode;
39
- const textScanInterval = Math.max(1, (options === null || options === void 0 || (_options$detection2 = options.detection) === null || _options$detection2 === void 0 ? void 0 : _options$detection2.textScanInterval) ?? DEFAULT_RESOLVED_OPTIONS.detection.textScanInterval);
55
+ var _options$detection, _options$barcode, _options$barcode2, _options$text, _options$text2, _options$text3, _options$text4, _options$detection2, _options$detection3, _options$detection4, _options$detection5, _options$detection6, _options$detection7, _options$detection8;
56
+ const textScanInterval = Math.max(1, (options === null || options === void 0 || (_options$detection = options.detection) === null || _options$detection === void 0 ? void 0 : _options$detection.textScanInterval) ?? DEFAULT_RESOLVED_OPTIONS.detection.textScanInterval);
40
57
  return {
41
58
  barcode: {
42
59
  enabled: (options === null || options === void 0 || (_options$barcode = options.barcode) === null || _options$barcode === void 0 ? void 0 : _options$barcode.enabled) ?? DEFAULT_RESOLVED_OPTIONS.barcode.enabled,
@@ -44,14 +61,26 @@ export const resolveOptions = options => {
44
61
  },
45
62
  text: {
46
63
  enabled: (options === null || options === void 0 || (_options$text = options.text) === null || _options$text === void 0 ? void 0 : _options$text.enabled) ?? DEFAULT_RESOLVED_OPTIONS.text.enabled,
47
- language: (options === null || options === void 0 || (_options$text2 = options.text) === null || _options$text2 === void 0 ? void 0 : _options$text2.language) ?? DEFAULT_RESOLVED_OPTIONS.text.language
64
+ language: (options === null || options === void 0 || (_options$text2 = options.text) === null || _options$text2 === void 0 ? void 0 : _options$text2.language) ?? DEFAULT_RESOLVED_OPTIONS.text.language,
65
+ requireConfirmation: (options === null || options === void 0 || (_options$text3 = options.text) === null || _options$text3 === void 0 ? void 0 : _options$text3.requireConfirmation) ?? DEFAULT_RESOLVED_OPTIONS.text.requireConfirmation,
66
+ pendingTtlMs: (options === null || options === void 0 || (_options$text4 = options.text) === null || _options$text4 === void 0 ? void 0 : _options$text4.pendingTtlMs) ?? DEFAULT_RESOLVED_OPTIONS.text.pendingTtlMs
48
67
  },
49
68
  detection: {
50
- resultMode,
51
69
  textScanInterval,
52
- maxFrameRate: (options === null || options === void 0 || (_options$detection3 = options.detection) === null || _options$detection3 === void 0 ? void 0 : _options$detection3.maxFrameRate) ?? DEFAULT_RESOLVED_OPTIONS.detection.maxFrameRate,
53
- forceOrientation: (options === null || options === void 0 || (_options$detection4 = options.detection) === null || _options$detection4 === void 0 ? void 0 : _options$detection4.forceOrientation) ?? DEFAULT_RESOLVED_OPTIONS.detection.forceOrientation
54
- }
70
+ maxFrameRate: (options === null || options === void 0 || (_options$detection2 = options.detection) === null || _options$detection2 === void 0 ? void 0 : _options$detection2.maxFrameRate) ?? DEFAULT_RESOLVED_OPTIONS.detection.maxFrameRate,
71
+ forceOrientation: (options === null || options === void 0 || (_options$detection3 = options.detection) === null || _options$detection3 === void 0 ? void 0 : _options$detection3.forceOrientation) ?? DEFAULT_RESOLVED_OPTIONS.detection.forceOrientation,
72
+ scanRegion: (options === null || options === void 0 || (_options$detection4 = options.detection) === null || _options$detection4 === void 0 ? void 0 : _options$detection4.scanRegion) ?? DEFAULT_RESOLVED_OPTIONS.detection.scanRegion,
73
+ minLuma: (options === null || options === void 0 || (_options$detection5 = options.detection) === null || _options$detection5 === void 0 ? void 0 : _options$detection5.minLuma) ?? DEFAULT_RESOLVED_OPTIONS.detection.minLuma,
74
+ minSharpness: (options === null || options === void 0 || (_options$detection6 = options.detection) === null || _options$detection6 === void 0 ? void 0 : _options$detection6.minSharpness) ?? DEFAULT_RESOLVED_OPTIONS.detection.minSharpness,
75
+ minConfidence: (options === null || options === void 0 || (_options$detection7 = options.detection) === null || _options$detection7 === void 0 ? void 0 : _options$detection7.minConfidence) ?? DEFAULT_RESOLVED_OPTIONS.detection.minConfidence,
76
+ barcodeFallbackAfter: (options === null || options === void 0 || (_options$detection8 = options.detection) === null || _options$detection8 === void 0 ? void 0 : _options$detection8.barcodeFallbackAfter) ?? DEFAULT_RESOLVED_OPTIONS.detection.barcodeFallbackAfter
77
+ },
78
+ showOverlay: (options === null || options === void 0 ? void 0 : options.showOverlay) ?? false,
79
+ overlayColors: {
80
+ ...DEFAULT_RESOLVED_OPTIONS.overlayColors,
81
+ ...(options === null || options === void 0 ? void 0 : options.overlayColors)
82
+ },
83
+ duplicateDebounceMs: (options === null || options === void 0 ? void 0 : options.duplicateDebounceMs) ?? 1500
55
84
  };
56
85
  };
57
86
  const transliteration = {
@@ -187,6 +216,107 @@ const extractWithContext = text => {
187
216
  return Array.from(matches);
188
217
  };
189
218
 
219
+ /**
220
+ * Calculate confidence score for a VIN candidate based on multiple factors.
221
+ * Returns a score from 0.0 (lowest confidence) to 1.0 (highest confidence).
222
+ */
223
+ const calculateConfidence = (value, source, origin, rawPayload) => {
224
+ 'worklet';
225
+
226
+ let score = 0.5; // Base confidence
227
+
228
+ // Analyze VIN value characteristics
229
+ // Known manufacturer WMI prefixes (first 3 characters) increase confidence
230
+ const wmi = value.slice(0, 3);
231
+ const knownManufacturers = ['1G1', '1G2', '1G3', '1G4', '1G6',
232
+ // GM
233
+ '1FA', '1FB', '1FC', '1FD', '1FM', '1FT',
234
+ // Ford
235
+ '1HG', '1HC',
236
+ // Honda
237
+ '1N4', '1N6',
238
+ // Nissan
239
+ '2C3', '2C4',
240
+ // Chrysler
241
+ '2HG', '2HM',
242
+ // Honda
243
+ '2T1', '2T2', '2T3',
244
+ // Toyota
245
+ '3FA', '3FE',
246
+ // Ford Mexico
247
+ '4T1', '4T3',
248
+ // Toyota
249
+ '5FN', '5FY',
250
+ // Honda
251
+ '5TD', '5TF', '5TJ',
252
+ // Toyota
253
+ 'JHM', 'JHL',
254
+ // Honda Japan
255
+ 'JT2', 'JT3', 'JTD',
256
+ // Toyota Japan
257
+ 'KM8', 'KNA', 'KND',
258
+ // Hyundai/Kia
259
+ 'WBA', 'WBS', 'WBY',
260
+ // BMW
261
+ 'WDB', 'WDC', 'WDD',
262
+ // Mercedes
263
+ 'WAU', 'WA1',
264
+ // Audi
265
+ 'WVW', 'WV1', 'WV2' // VW
266
+ ];
267
+ if (knownManufacturers.includes(wmi)) {
268
+ score += 0.15;
269
+ }
270
+
271
+ // Check character distribution - VINs with too many repeated characters are suspicious
272
+ const charCounts = new Map();
273
+ for (let i = 0; i < value.length; i += 1) {
274
+ const char = value[i];
275
+ if (char) {
276
+ charCounts.set(char, (charCounts.get(char) ?? 0) + 1);
277
+ }
278
+ }
279
+ const maxRepeat = Math.max(...Array.from(charCounts.values()));
280
+ if (maxRepeat > 5) {
281
+ // Too many repeated characters (e.g., "11111111111111111")
282
+ score -= 0.2;
283
+ } else if (maxRepeat <= 2) {
284
+ // Good character diversity
285
+ score += 0.1;
286
+ }
287
+
288
+ // Barcodes are generally more reliable than OCR
289
+ if (source === 'barcode') {
290
+ score += 0.3;
291
+ }
292
+
293
+ // Text from element-level is more precise than block
294
+ if (origin === 'element') {
295
+ score += 0.2;
296
+ } else if (origin === 'line') {
297
+ score += 0.1;
298
+ } else if (origin === 'rawValue' || origin === 'displayValue') {
299
+ // Barcode origins get slight boost
300
+ score += 0.15;
301
+ }
302
+
303
+ // Context-aware detection (VIN prefix found) increases confidence
304
+ const resultText = rawPayload === null || rawPayload === void 0 ? void 0 : rawPayload.resultText;
305
+ if (resultText && typeof resultText === 'string') {
306
+ const lowerText = resultText.toLowerCase();
307
+ if (lowerText.includes('vin') || lowerText.includes('serial') || lowerText.includes('chassis')) {
308
+ score += 0.2;
309
+ }
310
+ }
311
+
312
+ // Valid checksum confirmed (already validated by isValidVin)
313
+ // All candidates here have passed validation, so add bonus
314
+ score += 0.2;
315
+
316
+ // Clamp to valid range
317
+ return Math.min(1.0, Math.max(0.0, score));
318
+ };
319
+
190
320
  /**
191
321
  * Tokenize and extract VIN candidates from raw text
192
322
  * Includes OCR error correction and context-aware extraction
@@ -253,9 +383,13 @@ const isValidWmi = value => {
253
383
  return false;
254
384
  }
255
385
  const firstChar = value[0];
256
- // North America: 1, 2 (Canada), 3, 4, 5 (United States)
257
- // Japan : 'J' Germany : 'W'
258
- return firstChar === '1' || firstChar === '2' || firstChar === '3' || firstChar === '4' || firstChar === '5' || firstChar === 'J' || firstChar === 'W';
386
+ if (!firstChar) {
387
+ return false;
388
+ }
389
+
390
+ // Allow any WMI that starts with a VIN-valid character (A–Z minus I/O/Q, or 1–9).
391
+ // This keeps validation global while still rejecting impossible leading chars.
392
+ return /^[A-HJ-NPR-Z1-9]$/.test(firstChar);
259
393
  };
260
394
 
261
395
  /**
@@ -353,13 +487,17 @@ const candidateFromBarcode = detection => {
353
487
  tokenizeCandidate(detection.rawValue ?? '').forEach(value => values.add(value));
354
488
  tokenizeCandidate(detection.displayValue ?? '').forEach(value => values.add(value));
355
489
  const boundingBox = toBoundingBox(detection, 'barcode');
356
- return Array.from(values).filter(value => isValidVin(value)).map(value => ({
357
- value,
358
- source: 'barcode',
359
- boundingBox,
360
- origin: detection.displayValue ? 'displayValue' : 'rawValue',
361
- rawPayload: detection
362
- }));
490
+ return Array.from(values).filter(value => isValidVin(value)).map(value => {
491
+ const origin = detection.displayValue ? 'displayValue' : 'rawValue';
492
+ return {
493
+ value,
494
+ source: 'barcode',
495
+ confidence: calculateConfidence(value, 'barcode', origin, detection),
496
+ boundingBox,
497
+ origin,
498
+ rawPayload: detection
499
+ };
500
+ });
363
501
  };
364
502
  const candidateFromText = detection => {
365
503
  'worklet';
@@ -402,6 +540,7 @@ const candidateFromText = detection => {
402
540
  return {
403
541
  value,
404
542
  source: 'text',
543
+ confidence: calculateConfidence(value, 'text', origin, detection),
405
544
  boundingBox,
406
545
  origin,
407
546
  rawPayload: detection
@@ -425,7 +564,6 @@ export const buildVinCandidates = (payload, options) => {
425
564
  'worklet';
426
565
 
427
566
  const list = [];
428
- const prefersBarcodeFirst = options.detection.resultMode === 'first';
429
567
  const addBarcodeCandidates = () => {
430
568
  if (!options.barcode.enabled) {
431
569
  return;
@@ -442,13 +580,8 @@ export const buildVinCandidates = (payload, options) => {
442
580
  list.push(...candidateFromText(textBlock));
443
581
  });
444
582
  };
445
- if (prefersBarcodeFirst) {
446
- addBarcodeCandidates();
447
- addTextCandidates();
448
- } else {
449
- addTextCandidates();
450
- addBarcodeCandidates();
451
- }
583
+ addBarcodeCandidates();
584
+ addTextCandidates();
452
585
  return dedupeCandidates(list);
453
586
  };
454
587
  export const pickFirstCandidate = (candidates, options) => {