@agicash/qr-scanner 0.1.1 → 0.1.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 +10 -0
- package/dist/index.cjs +93 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -70
- package/dist/index.js.map +1 -1
- package/dist/worker.js +6 -4
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,6 +34,12 @@ function setCachedDeviceId(facingMode, deviceId) {
|
|
|
34
34
|
} catch {
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
function clearCachedDeviceId(facingMode) {
|
|
38
|
+
try {
|
|
39
|
+
localStorage.removeItem(`${CACHE_KEY_PREFIX}${facingMode}`);
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
37
43
|
var CameraManager = class {
|
|
38
44
|
constructor(config = {}) {
|
|
39
45
|
this.stream = null;
|
|
@@ -147,14 +153,20 @@ var CameraManager = class {
|
|
|
147
153
|
debug(
|
|
148
154
|
`[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`
|
|
149
155
|
);
|
|
150
|
-
} catch {
|
|
156
|
+
} catch (err) {
|
|
157
|
+
debug(
|
|
158
|
+
`[QrScanner] ensureBestCamera: skipped (getCapabilities failed: ${err instanceof Error ? err.message : err})`
|
|
159
|
+
);
|
|
151
160
|
return;
|
|
152
161
|
}
|
|
153
162
|
const currentDeviceId = track.getSettings().deviceId;
|
|
154
163
|
let devices;
|
|
155
164
|
try {
|
|
156
165
|
devices = await navigator.mediaDevices.enumerateDevices();
|
|
157
|
-
} catch {
|
|
166
|
+
} catch (err) {
|
|
167
|
+
debug(
|
|
168
|
+
`[QrScanner] ensureBestCamera: skipped (enumerateDevices failed: ${err instanceof Error ? err.message : err})`
|
|
169
|
+
);
|
|
158
170
|
return;
|
|
159
171
|
}
|
|
160
172
|
if (!Array.isArray(devices)) return;
|
|
@@ -181,10 +193,13 @@ var CameraManager = class {
|
|
|
181
193
|
debug(
|
|
182
194
|
`[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`
|
|
183
195
|
);
|
|
184
|
-
} catch {
|
|
196
|
+
} catch (err) {
|
|
185
197
|
debug(
|
|
186
|
-
`[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`
|
|
198
|
+
`[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms \u2014 ${err instanceof Error ? err.name : err}`
|
|
187
199
|
);
|
|
200
|
+
if (err instanceof DOMException && err.name === "NotAllowedError") {
|
|
201
|
+
throw new CameraPermissionError();
|
|
202
|
+
}
|
|
188
203
|
continue;
|
|
189
204
|
}
|
|
190
205
|
const candidateTrack = candidateStream.getVideoTracks()[0];
|
|
@@ -203,27 +218,52 @@ var CameraManager = class {
|
|
|
203
218
|
this.stream = candidateStream;
|
|
204
219
|
return;
|
|
205
220
|
}
|
|
206
|
-
} catch {
|
|
221
|
+
} catch (err) {
|
|
222
|
+
debug(
|
|
223
|
+
`[QrScanner] ensureBestCamera: candidate getCapabilities failed \u2014 ${err instanceof Error ? err.message : err}`
|
|
224
|
+
);
|
|
207
225
|
}
|
|
208
226
|
for (const t2 of candidateStream.getTracks()) t2.stop();
|
|
209
227
|
}
|
|
210
|
-
|
|
211
|
-
|
|
228
|
+
const recoveryAttempts = [
|
|
229
|
+
// Original deviceId + resolution
|
|
230
|
+
{
|
|
212
231
|
video: {
|
|
213
232
|
deviceId: currentDeviceId ? { exact: currentDeviceId } : void 0,
|
|
214
233
|
width: this.resolution?.width ?? { ideal: 1920 },
|
|
215
234
|
height: this.resolution?.height ?? { ideal: 1080 }
|
|
216
235
|
},
|
|
217
236
|
audio: false
|
|
218
|
-
}
|
|
219
|
-
|
|
237
|
+
},
|
|
238
|
+
// facingMode + resolution
|
|
239
|
+
this.buildConstraints(),
|
|
240
|
+
// facingMode only
|
|
241
|
+
this.buildConstraints(false),
|
|
242
|
+
// Bare minimum — should always succeed if a camera exists
|
|
243
|
+
{ video: true, audio: false }
|
|
244
|
+
];
|
|
245
|
+
let lastRecoveryError;
|
|
246
|
+
for (const constraints of recoveryAttempts) {
|
|
220
247
|
try {
|
|
221
|
-
this.stream = await navigator.mediaDevices.getUserMedia(
|
|
222
|
-
|
|
248
|
+
this.stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
249
|
+
return;
|
|
250
|
+
} catch (err) {
|
|
251
|
+
if (err instanceof DOMException && err.name === "NotAllowedError") {
|
|
252
|
+
throw new CameraPermissionError();
|
|
253
|
+
}
|
|
254
|
+
if (err instanceof DOMException && err.name === "NotFoundError") {
|
|
255
|
+
throw new CameraNotFoundError();
|
|
256
|
+
}
|
|
257
|
+
lastRecoveryError = err;
|
|
258
|
+
debug(
|
|
259
|
+
`[QrScanner] ensureBestCamera recovery failed: ${err instanceof Error ? err.name : err}`
|
|
223
260
|
);
|
|
224
|
-
|
|
261
|
+
continue;
|
|
225
262
|
}
|
|
226
263
|
}
|
|
264
|
+
debug(
|
|
265
|
+
`[QrScanner] ensureBestCamera: all recovery attempts failed, last error: ${lastRecoveryError instanceof Error ? lastRecoveryError.message : lastRecoveryError}`
|
|
266
|
+
);
|
|
227
267
|
}
|
|
228
268
|
getVideoTrack() {
|
|
229
269
|
if (!this.stream) return null;
|
|
@@ -238,12 +278,10 @@ var CameraManager = class {
|
|
|
238
278
|
* fewer constraints lets us still open the camera on those browsers.
|
|
239
279
|
*/
|
|
240
280
|
async acquireStream() {
|
|
241
|
-
const labels = [];
|
|
242
281
|
const attempts = [];
|
|
243
282
|
if (this.facingMode === "environment" || this.facingMode === "user") {
|
|
244
283
|
const cachedId = getCachedDeviceId(this.facingMode);
|
|
245
284
|
if (cachedId) {
|
|
246
|
-
labels.push("cached deviceId");
|
|
247
285
|
const video = {
|
|
248
286
|
deviceId: { exact: cachedId }
|
|
249
287
|
};
|
|
@@ -251,42 +289,50 @@ var CameraManager = class {
|
|
|
251
289
|
else video.width = { ideal: 1920 };
|
|
252
290
|
if (this.resolution?.height) video.height = this.resolution.height;
|
|
253
291
|
else video.height = { ideal: 1080 };
|
|
254
|
-
attempts.push({
|
|
292
|
+
attempts.push({
|
|
293
|
+
label: "cached deviceId",
|
|
294
|
+
constraints: { video, audio: false },
|
|
295
|
+
isCachedDeviceId: true
|
|
296
|
+
});
|
|
255
297
|
}
|
|
256
298
|
}
|
|
257
|
-
labels.push("full constraints", "no resolution", "bare minimum");
|
|
258
299
|
attempts.push(
|
|
259
|
-
|
|
260
|
-
this.buildConstraints(),
|
|
261
|
-
|
|
262
|
-
this.buildConstraints(false),
|
|
263
|
-
// Bare minimum
|
|
264
|
-
{ video: true, audio: false }
|
|
300
|
+
{ label: "full constraints", constraints: this.buildConstraints() },
|
|
301
|
+
{ label: "no resolution", constraints: this.buildConstraints(false) },
|
|
302
|
+
{ label: "bare minimum", constraints: { video: true, audio: false } }
|
|
265
303
|
);
|
|
266
304
|
let lastError;
|
|
267
|
-
for (
|
|
305
|
+
for (const attempt of attempts) {
|
|
268
306
|
const t = performance.now();
|
|
269
307
|
try {
|
|
270
|
-
const stream = await navigator.mediaDevices.getUserMedia(
|
|
308
|
+
const stream = await navigator.mediaDevices.getUserMedia(
|
|
309
|
+
attempt.constraints
|
|
310
|
+
);
|
|
271
311
|
debug(
|
|
272
|
-
`[QrScanner] getUserMedia(${
|
|
312
|
+
`[QrScanner] getUserMedia(${attempt.label}): ${(performance.now() - t).toFixed(0)}ms \u2713`
|
|
273
313
|
);
|
|
274
314
|
return stream;
|
|
275
315
|
} catch (err) {
|
|
276
316
|
debug(
|
|
277
|
-
`[QrScanner] getUserMedia(${
|
|
317
|
+
`[QrScanner] getUserMedia(${attempt.label}): ${(performance.now() - t).toFixed(0)}ms \u2717 ${err instanceof Error ? err.name : err}`
|
|
278
318
|
);
|
|
279
|
-
if (err instanceof DOMException) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
throw new CameraNotFoundError();
|
|
285
|
-
}
|
|
286
|
-
lastError = err;
|
|
287
|
-
continue;
|
|
319
|
+
if (err instanceof DOMException && err.name === "NotAllowedError") {
|
|
320
|
+
throw new CameraPermissionError();
|
|
321
|
+
}
|
|
322
|
+
if (err instanceof DOMException && err.name === "NotFoundError") {
|
|
323
|
+
throw new CameraNotFoundError();
|
|
288
324
|
}
|
|
289
|
-
|
|
325
|
+
if (!(err instanceof DOMException) && !(err instanceof OverconstrainedError)) {
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
if (attempt.isCachedDeviceId) {
|
|
329
|
+
clearCachedDeviceId(this.facingMode);
|
|
330
|
+
debug(
|
|
331
|
+
`[QrScanner] cleared stale cached deviceId for "${this.facingMode}"`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
lastError = err;
|
|
335
|
+
continue;
|
|
290
336
|
}
|
|
291
337
|
}
|
|
292
338
|
throw lastError;
|
|
@@ -396,9 +442,6 @@ var FrameExtractor = class {
|
|
|
396
442
|
markWorkerIdle() {
|
|
397
443
|
this.workerBusy = false;
|
|
398
444
|
}
|
|
399
|
-
markWorkerBusy() {
|
|
400
|
-
this.workerBusy = true;
|
|
401
|
-
}
|
|
402
445
|
};
|
|
403
446
|
|
|
404
447
|
// src/overlay.ts
|
|
@@ -408,22 +451,9 @@ function getRenderedVideoRect(video) {
|
|
|
408
451
|
const videoWidth = video.videoWidth || 1;
|
|
409
452
|
const videoHeight = video.videoHeight || 1;
|
|
410
453
|
const objectFit = getComputedStyle(video).objectFit;
|
|
411
|
-
if (objectFit === "cover") {
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
elementHeight / videoHeight
|
|
415
|
-
);
|
|
416
|
-
const renderedWidth = videoWidth * scale;
|
|
417
|
-
const renderedHeight = videoHeight * scale;
|
|
418
|
-
return {
|
|
419
|
-
offsetX: (elementWidth - renderedWidth) / 2,
|
|
420
|
-
offsetY: (elementHeight - renderedHeight) / 2,
|
|
421
|
-
width: renderedWidth,
|
|
422
|
-
height: renderedHeight
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
if (objectFit === "contain") {
|
|
426
|
-
const scale = Math.min(
|
|
454
|
+
if (objectFit === "cover" || objectFit === "contain") {
|
|
455
|
+
const scaleFn = objectFit === "cover" ? Math.max : Math.min;
|
|
456
|
+
const scale = scaleFn(
|
|
427
457
|
elementWidth / videoWidth,
|
|
428
458
|
elementHeight / videoHeight
|
|
429
459
|
);
|
|
@@ -744,18 +774,9 @@ var Scanner = class {
|
|
|
744
774
|
}
|
|
745
775
|
setInversionMode(mode) {
|
|
746
776
|
if (!this.worker) return;
|
|
747
|
-
const options = {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
options.tryInvert = false;
|
|
751
|
-
break;
|
|
752
|
-
case "invert":
|
|
753
|
-
options.tryInvert = true;
|
|
754
|
-
break;
|
|
755
|
-
case "both":
|
|
756
|
-
options.tryInvert = true;
|
|
757
|
-
break;
|
|
758
|
-
}
|
|
777
|
+
const options = {
|
|
778
|
+
tryInvert: mode !== "original"
|
|
779
|
+
};
|
|
759
780
|
const msg = { type: "configure", options };
|
|
760
781
|
this.worker.postMessage(msg);
|
|
761
782
|
}
|
|
@@ -888,8 +909,8 @@ async function createImageBitmapFromBlob(blob) {
|
|
|
888
909
|
}
|
|
889
910
|
}
|
|
890
911
|
|
|
891
|
-
// src/
|
|
892
|
-
var
|
|
912
|
+
// src/decoder-utils.ts
|
|
913
|
+
var DEFAULT_READER_OPTIONS = {
|
|
893
914
|
formats: ["QRCode"],
|
|
894
915
|
tryHarder: true,
|
|
895
916
|
tryInvert: true,
|
|
@@ -906,12 +927,14 @@ function mapPosition(position) {
|
|
|
906
927
|
position.bottomLeft
|
|
907
928
|
];
|
|
908
929
|
}
|
|
930
|
+
|
|
931
|
+
// src/scan-image.ts
|
|
909
932
|
function isDirectInput(source) {
|
|
910
933
|
return source instanceof Blob || source instanceof ArrayBuffer || source instanceof Uint8Array || typeof ImageData !== "undefined" && source instanceof ImageData;
|
|
911
934
|
}
|
|
912
935
|
async function scanImage(source, options) {
|
|
913
936
|
const readerOptions = {
|
|
914
|
-
...
|
|
937
|
+
...DEFAULT_READER_OPTIONS,
|
|
915
938
|
...options?.decoderOptions,
|
|
916
939
|
formats: ["QRCode"]
|
|
917
940
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/debug.ts","../src/camera.ts","../src/frame-extractor.ts","../src/overlay.ts","../src/scan-region.ts","../src/scanner.ts","../src/scan-image.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["let enabled = false;\n\nexport function setDebug(on: boolean): void {\n enabled = on;\n}\n\nexport function debug(...args: unknown[]): void {\n if (enabled) console.debug(...args);\n}\n","import { debug } from './debug.js';\nimport type { FacingMode, DeviceId, Camera } from './types.js';\n\nexport class CameraPermissionError extends Error {\n constructor(\n message = 'Camera access denied. Please grant camera permission and try again.',\n ) {\n super(message);\n this.name = 'CameraPermissionError';\n }\n}\n\nexport class CameraNotFoundError extends Error {\n constructor(\n message = 'No camera found. Please connect a camera and try again.',\n ) {\n super(message);\n this.name = 'CameraNotFoundError';\n }\n}\n\nconst CACHE_KEY_PREFIX = '@agicash/qr-scanner:camera:';\n\nfunction getCachedDeviceId(facingMode: string): string | null {\n try {\n return localStorage.getItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n return null;\n }\n}\n\nfunction setCachedDeviceId(facingMode: string, deviceId: string): void {\n try {\n localStorage.setItem(`${CACHE_KEY_PREFIX}${facingMode}`, deviceId);\n } catch {\n // localStorage unavailable or full — ignore\n }\n}\n\nexport interface CameraConfig {\n preferredCamera?: FacingMode | DeviceId;\n cameraResolution?: {\n width?: MediaTrackConstraintSet['width'];\n height?: MediaTrackConstraintSet['height'];\n };\n}\n\nexport class CameraManager {\n private stream: MediaStream | null = null;\n private facingMode: FacingMode | DeviceId;\n private resolution: CameraConfig['cameraResolution'];\n\n constructor(config: CameraConfig = {}) {\n this.facingMode = config.preferredCamera ?? 'environment';\n this.resolution = config.cameraResolution;\n }\n\n async start(video: HTMLVideoElement): Promise<MediaStream> {\n if (this.stream) {\n return this.stream;\n }\n\n const t0 = performance.now();\n\n this.stream = await this.acquireStream();\n const t1 = performance.now();\n debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);\n\n // On some devices (e.g. Samsung S24 + Brave), facingMode: 'environment'\n // picks an ultrawide camera that lacks autofocus. Check and switch to a\n // better camera BEFORE showing on screen to avoid visible flicker.\n await this.ensureBestCamera();\n const t2 = performance.now();\n debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);\n\n video.srcObject = this.stream;\n video.setAttribute('playsinline', 'true');\n await video.play();\n const t3 = performance.now();\n debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);\n debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);\n\n // Cache the final camera so subsequent starts skip ensureBestCamera\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;\n if (finalDeviceId) {\n setCachedDeviceId(this.facingMode, finalDeviceId);\n }\n }\n\n return this.stream;\n }\n\n stop(): void {\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n }\n\n async setCamera(\n facingModeOrDeviceId: FacingMode | DeviceId,\n video: HTMLVideoElement,\n ): Promise<void> {\n this.stop();\n this.facingMode = facingModeOrDeviceId;\n await this.start(video);\n }\n\n getStream(): MediaStream | null {\n return this.stream;\n }\n\n async hasFlash(): Promise<boolean> {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n torch?: boolean;\n };\n return capabilities.torch === true;\n } catch {\n return false;\n }\n }\n\n isFlashOn(): boolean {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n const settings = track.getSettings() as MediaTrackSettings & {\n torch?: boolean;\n };\n return settings.torch === true;\n }\n\n async toggleFlash(): Promise<void> {\n if (this.isFlashOn()) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise<void> {\n await this.setTorch(true);\n }\n\n async turnFlashOff(): Promise<void> {\n await this.setTorch(false);\n }\n\n private async setTorch(on: boolean): Promise<void> {\n const track = this.getVideoTrack();\n if (!track) {\n throw new Error('No active camera stream');\n }\n\n try {\n await track.applyConstraints({\n advanced: [{ torch: on } as MediaTrackConstraintSet],\n });\n } catch {\n throw new Error('Flash/torch is not supported on this device');\n }\n }\n\n /**\n * If the current camera lacks continuous autofocus (e.g. an ultrawide sensor\n * picked by facingMode: 'environment'), find a better camera with the same\n * facing mode and replace this.stream. Called before assigning to the video\n * element so the user never sees the wrong camera.\n */\n private async ensureBestCamera(): Promise<void> {\n if (this.facingMode !== 'environment' && this.facingMode !== 'user') {\n debug('[QrScanner] ensureBestCamera: skipped (specific deviceId)');\n return;\n }\n\n const track = this.getVideoTrack();\n if (!track) return;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (\n !capabilities.focusMode ||\n capabilities.focusMode.includes('continuous')\n ) {\n // focusMode not reported (e.g. Safari/iOS) or has autofocus — skip.\n // Only enter the candidate loop when the browser explicitly reports\n // focusMode without 'continuous' (e.g. S24 + Brave ultrawide).\n debug(\n `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n return;\n }\n debug(\n `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n } catch {\n return;\n }\n\n // Current camera lacks continuous autofocus.\n // Enumerate devices while stream is active (ensures deviceIds are available).\n const currentDeviceId = track.getSettings().deviceId;\n\n let devices: MediaDeviceInfo[];\n try {\n devices = await navigator.mediaDevices.enumerateDevices();\n } catch {\n return;\n }\n if (!Array.isArray(devices)) return;\n\n const candidates = devices.filter(\n (d) => d.kind === 'videoinput' && d.deviceId !== currentDeviceId,\n );\n debug(\n `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`,\n );\n if (candidates.length === 0) return;\n\n // Stop current stream — mobile devices only allow one active camera\n this.stop();\n\n for (const candidate of candidates) {\n const t = performance.now();\n let candidateStream: MediaStream;\n try {\n candidateStream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: candidate.deviceId },\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`,\n );\n } catch {\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`,\n );\n continue;\n }\n\n const candidateTrack = candidateStream.getVideoTracks()[0];\n if (!candidateTrack) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Must match the desired facing mode\n const candidateSettings =\n candidateTrack.getSettings() as MediaTrackSettings & {\n facingMode?: string;\n };\n if (\n candidateSettings.facingMode &&\n candidateSettings.facingMode !== this.facingMode\n ) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Check if this camera supports continuous autofocus\n try {\n const candidateCaps =\n candidateTrack.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (candidateCaps.focusMode?.includes('continuous')) {\n this.stream = candidateStream;\n return;\n }\n } catch {\n // Can't check capabilities, skip\n }\n\n for (const t of candidateStream.getTracks()) t.stop();\n }\n\n // No better camera found — re-open the original\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined,\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n } catch {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia(\n this.buildConstraints(),\n );\n } catch {\n // Could not recover camera\n }\n }\n }\n\n private getVideoTrack(): MediaStreamTrack | null {\n if (!this.stream) return null;\n const tracks = this.stream.getVideoTracks();\n return tracks[0] ?? null;\n }\n\n /**\n * Try getUserMedia with progressively simpler constraints.\n *\n * Some browsers (e.g. Brave on Samsung Galaxy S24) throw NotReadableError\n * when facingMode and resolution constraints are combined. Falling back to\n * fewer constraints lets us still open the camera on those browsers.\n */\n private async acquireStream(): Promise<MediaStream> {\n const labels: string[] = [];\n const attempts: MediaStreamConstraints[] = [];\n\n // If we've previously found a good camera for this facingMode, try it first\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const cachedId = getCachedDeviceId(this.facingMode);\n if (cachedId) {\n labels.push('cached deviceId');\n const video: MediaTrackConstraints = {\n deviceId: { exact: cachedId },\n };\n if (this.resolution?.width) video.width = this.resolution.width;\n else video.width = { ideal: 1920 };\n if (this.resolution?.height) video.height = this.resolution.height;\n else video.height = { ideal: 1080 };\n attempts.push({ video, audio: false });\n }\n }\n\n // Standard fallback chain\n labels.push('full constraints', 'no resolution', 'bare minimum');\n attempts.push(\n // facingMode/deviceId + resolution\n this.buildConstraints(),\n // facingMode/deviceId only, no resolution\n this.buildConstraints(false),\n // Bare minimum\n { video: true, audio: false },\n );\n\n let lastError: unknown;\n for (let i = 0; i < attempts.length; i++) {\n const t = performance.now();\n try {\n const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);\n debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✓`,\n );\n return stream;\n } catch (err) {\n debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✗ ${err instanceof DOMException ? err.name : err}`,\n );\n if (err instanceof DOMException) {\n if (err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n // NotReadableError or OverconstrainedError — try next fallback\n lastError = err;\n continue;\n }\n throw err;\n }\n }\n\n throw lastError;\n }\n\n private buildConstraints(includeResolution = true): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n\n if (includeResolution) {\n video.width = this.resolution?.width ?? { ideal: 1920 };\n video.height = this.resolution?.height ?? { ideal: 1080 };\n }\n\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n video.facingMode = this.facingMode;\n } else {\n video.deviceId = { exact: this.facingMode };\n }\n\n return { video, audio: false };\n }\n\n static async hasCamera(): Promise<boolean> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.some((d) => d.kind === 'videoinput');\n } catch {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise<Camera[]> {\n if (requestLabels) {\n // Requesting labels requires a temporary stream to trigger the permission prompt\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n for (const track of stream.getTracks()) {\n track.stop();\n }\n } catch {\n // Permission denied — fall through with empty labels\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === 'videoinput')\n .map((d) => ({\n id: d.deviceId,\n label: d.label || `Camera ${d.deviceId.slice(0, 8)}`,\n }));\n }\n}\n","import type { ScanRegion } from './types.js';\n\nexport interface FrameExtractorConfig {\n maxScansPerSecond: number;\n getScanRegion: () => ScanRegion;\n}\n\nexport class FrameExtractor {\n private video: HTMLVideoElement;\n private canvas: HTMLCanvasElement | OffscreenCanvas;\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private rafId: number | null = null;\n private running = false;\n private workerBusy = false;\n private lastScanTime = -Infinity;\n private minInterval: number;\n private getScanRegion: () => ScanRegion;\n private onFrame: ((imageData: ImageData) => void) | null = null;\n\n constructor(video: HTMLVideoElement, config: FrameExtractorConfig) {\n this.video = video;\n this.minInterval = 1000 / config.maxScansPerSecond;\n this.getScanRegion = config.getScanRegion;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n this.canvas = new OffscreenCanvas(1, 1);\n this.ctx = this.canvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'none';\n this.ctx = this.canvas.getContext('2d')!;\n }\n }\n\n start(onFrame: (imageData: ImageData) => void): void {\n if (this.running) return;\n this.running = true;\n this.onFrame = onFrame;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n this.running = false;\n this.onFrame = null;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n destroy(): void {\n this.stop();\n if (this.canvas instanceof HTMLCanvasElement && this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n }\n\n markWorkerIdle(): void {\n this.workerBusy = false;\n }\n\n markWorkerBusy(): void {\n this.workerBusy = true;\n }\n\n private tick = (): void => {\n if (!this.running) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n\n // Skip if worker is still processing previous frame\n if (this.workerBusy) return;\n\n // Rate limiting\n const now = performance.now();\n if (now - this.lastScanTime < this.minInterval) return;\n\n // Skip if video isn't ready\n if (this.video.readyState < 2) return;\n\n this.lastScanTime = now;\n\n const region = this.getScanRegion();\n const sx = region.x ?? 0;\n const sy = region.y ?? 0;\n const sw = region.width ?? this.video.videoWidth;\n const sh = region.height ?? this.video.videoHeight;\n\n if (sw <= 0 || sh <= 0) return;\n\n this.canvas.width = sw;\n this.canvas.height = sh;\n\n this.ctx.drawImage(this.video, sx, sy, sw, sh, 0, 0, sw, sh);\n const imageData = this.ctx.getImageData(0, 0, sw, sh);\n\n this.workerBusy = true;\n this.onFrame?.(imageData);\n };\n}\n","import type { ScanRegion, Point } from './types.js';\n\n/**\n * Compute the actual rendered position and size of the video content\n * within the element, accounting for object-fit: cover.\n *\n * With object-fit: cover the video is scaled up to fill the element and\n * cropped. The rendered content is larger than the element, centered,\n * with negative offsets for the cropped portions.\n *\n * When object-fit is the default (fill), the rendered size equals the\n * element size and offsets are zero — so this is backwards-compatible.\n */\nfunction getRenderedVideoRect(video: HTMLVideoElement): {\n offsetX: number;\n offsetY: number;\n width: number;\n height: number;\n} {\n const elementWidth = video.clientWidth;\n const elementHeight = video.clientHeight;\n const videoWidth = video.videoWidth || 1;\n const videoHeight = video.videoHeight || 1;\n\n const objectFit = getComputedStyle(video).objectFit;\n\n if (objectFit === 'cover') {\n const scale = Math.max(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n if (objectFit === 'contain') {\n const scale = Math.min(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n // Default (fill / none / scale-down with no scaling needed): element dimensions\n return { offsetX: 0, offsetY: 0, width: elementWidth, height: elementHeight };\n}\n\nexport interface OverlayConfig {\n highlightScanRegion: boolean;\n highlightCodeOutline: boolean;\n customOverlay?: HTMLDivElement;\n}\n\nexport class ScanOverlay {\n private container: HTMLElement;\n private overlayEl: HTMLDivElement | null = null;\n private codeOutlineEl: SVGElement | null = null;\n private config: OverlayConfig;\n private video: HTMLVideoElement;\n\n constructor(video: HTMLVideoElement, config: OverlayConfig) {\n this.video = video;\n this.config = config;\n\n const parent = video.parentElement;\n if (!parent) {\n throw new Error(\n 'QrScanner: video element must have a parent element. ' +\n 'The parent should have position: relative.',\n );\n }\n this.container = parent;\n }\n\n setup(): void {\n if (this.config.customOverlay) {\n this.overlayEl = this.config.customOverlay;\n this.positionOverlay();\n return;\n }\n\n if (this.config.highlightScanRegion) {\n this.createScanRegionOverlay();\n }\n\n if (this.config.highlightCodeOutline) {\n this.createCodeOutline();\n }\n }\n\n updateScanRegion(region: ScanRegion): void {\n if (!this.overlayEl || this.config.customOverlay) return;\n this.positionOverlayToRegion(region);\n }\n\n updateCodeOutline(\n cornerPoints: Point[] | null,\n scanRegion?: ScanRegion,\n ): void {\n if (!this.codeOutlineEl) return;\n\n if (!cornerPoints || cornerPoints.length < 4) {\n this.codeOutlineEl.style.display = 'none';\n return;\n }\n\n this.codeOutlineEl.style.display = 'block';\n\n const polygon = this.codeOutlineEl.querySelector('polygon');\n if (!polygon) return;\n\n // Corner points are relative to the cropped scan region.\n // Add the scan region offset to get full video coordinates,\n // then scale to display coordinates (accounting for object-fit).\n const regionX = scanRegion?.x ?? 0;\n const regionY = scanRegion?.y ?? 0;\n const rendered = getRenderedVideoRect(this.video);\n const scaleX = rendered.width / this.video.videoWidth;\n const scaleY = rendered.height / this.video.videoHeight;\n\n const points = cornerPoints\n .map(\n (p) =>\n `${(p.x + regionX) * scaleX + rendered.offsetX},${(p.y + regionY) * scaleY + rendered.offsetY}`,\n )\n .join(' ');\n\n polygon.setAttribute('points', points);\n }\n\n destroy(): void {\n if (this.overlayEl && !this.config.customOverlay) {\n this.overlayEl.remove();\n }\n if (this.codeOutlineEl) {\n this.codeOutlineEl.remove();\n }\n this.overlayEl = null;\n this.codeOutlineEl = null;\n }\n\n private createScanRegionOverlay(): void {\n this.overlayEl = document.createElement('div');\n this.overlayEl.className = 'qr-scanner-region';\n\n // Overlay size: 3/4 of the smaller container dimension, centered.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: `${size}px`,\n height: `${size}px`,\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '8px',\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',\n pointerEvents: 'none',\n zIndex: '10',\n });\n\n // Corner markers\n const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\n for (const corner of corners) {\n const marker = document.createElement('div');\n marker.className = `qr-scanner-corner qr-scanner-corner-${corner}`;\n\n const [vertical, horizontal] = corner.split('-');\n\n Object.assign(marker.style, {\n position: 'absolute',\n width: '24px',\n height: '24px',\n [vertical]: '-2px',\n [horizontal]: '-2px',\n [`border-${vertical}`]: '3px solid white',\n [`border-${horizontal}`]: '3px solid white',\n [`border-${vertical}-${horizontal}-radius`]: '8px',\n });\n\n this.overlayEl.appendChild(marker);\n }\n\n this.container.appendChild(this.overlayEl);\n }\n\n private createCodeOutline(): void {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('class', 'qr-scanner-code-outline');\n\n Object.assign(svg.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '11',\n display: 'none',\n });\n\n const polygon = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'polygon',\n );\n polygon.setAttribute('fill', 'none');\n polygon.setAttribute('stroke', '#00ff00');\n polygon.setAttribute('stroke-width', '3');\n polygon.setAttribute('stroke-linejoin', 'round');\n\n // Animated stroke\n const animate = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'animate',\n );\n animate.setAttribute('attributeName', 'stroke-opacity');\n animate.setAttribute('values', '1;0.5;1');\n animate.setAttribute('dur', '1.5s');\n animate.setAttribute('repeatCount', 'indefinite');\n polygon.appendChild(animate);\n\n svg.appendChild(polygon);\n this.container.appendChild(svg);\n this.codeOutlineEl = svg;\n }\n\n private positionOverlay(): void {\n if (!this.overlayEl) return;\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '10',\n });\n }\n\n private positionOverlayToRegion(_region: ScanRegion): void {\n if (!this.overlayEl) return;\n\n // Keep the overlay at a container-relative centered size rather than\n // mapping scan region coordinates to display coordinates. With\n // object-fit: cover on a portrait phone with a landscape camera, the\n // mapped region extends beyond the container — fine once the camera is\n // visible but causes a jarring jump from the initial placeholder.\n // The overlay is a visual guide; the actual scan area (in the frame\n // extractor) is unaffected and may be larger than what's shown.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n // Only update size — initial CSS centering (top: 50%, left: 50%,\n // transform: translate(-50%, -50%)) persists and handles re-centering.\n Object.assign(this.overlayEl.style, {\n width: `${size}px`,\n height: `${size}px`,\n });\n }\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Calculate the default scan region: a centered square covering\n * 2/3 of the smaller video dimension.\n */\nexport function calculateDefaultScanRegion(video: HTMLVideoElement): ScanRegion {\n const videoWidth = video.videoWidth || video.width;\n const videoHeight = video.videoHeight || video.height;\n\n const smallerDimension = Math.min(videoWidth, videoHeight);\n const size = Math.round((smallerDimension * 2) / 3);\n\n return {\n x: Math.round((videoWidth - size) / 2),\n y: Math.round((videoHeight - size) / 2),\n width: size,\n height: size,\n };\n}\n","import { CameraManager } from './camera.js';\nimport { debug } from './debug.js';\nimport { FrameExtractor } from './frame-extractor.js';\nimport { ScanOverlay } from './overlay.js';\nimport { calculateDefaultScanRegion } from './scan-region.js';\nimport type {\n ScannerOptions,\n ScanResult,\n ScanRegion,\n InversionMode,\n WorkerRequest,\n WorkerResponse,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\ntype OnDecodeCallback = (result: ScanResult) => void;\n\n/**\n * Custom worker URL override. When set, this URL is used instead of the\n * default bundler-resolved worker. Useful for CJS consumers or non-standard\n * bundler setups.\n */\nlet customWorkerUrl: string | URL | null = null;\n\nexport function setWorkerUrl(url: string | URL): void {\n customWorkerUrl = url;\n}\n\nfunction resolveWorkerUrl(): string | URL {\n if (customWorkerUrl) {\n return customWorkerUrl;\n }\n // Standard pattern: modern bundlers (Vite, webpack 5, Parcel, esbuild)\n // resolve `new URL('./file', import.meta.url)` at build time,\n // copying worker.js to the output directory and returning the correct URL.\n // Falls back gracefully for CJS builds where import.meta is unavailable.\n try {\n return new URL('./worker.js', import.meta.url);\n } catch {\n throw new Error(\n '@agicash/qr-scanner: Could not resolve worker URL. ' +\n 'Call QrScanner.setWorkerUrl() with the path to the worker script before creating a scanner. ' +\n 'Example: QrScanner.setWorkerUrl(\"/path/to/@agicash/qr-scanner/dist/worker.js\")',\n );\n }\n}\n\nexport class Scanner {\n private video: HTMLVideoElement;\n private onDecode: OnDecodeCallback;\n private options: ScannerOptions;\n private camera: CameraManager;\n private frameExtractor: FrameExtractor | null = null;\n private worker: Worker | null = null;\n private overlay: ScanOverlay | null = null;\n private active = false;\n private paused = false;\n private destroyed = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: OnDecodeCallback,\n options: ScannerOptions = {},\n ) {\n this.video = video;\n this.onDecode = onDecode;\n this.options = options;\n\n this.camera = new CameraManager({\n preferredCamera: options.preferredCamera,\n cameraResolution: options.cameraResolution,\n });\n }\n\n async start(): Promise<void> {\n if (this.destroyed) {\n throw new Error('Scanner has been destroyed');\n }\n\n if (this.active && !this.paused) {\n return; // Already running\n }\n\n const t0 = performance.now();\n\n // Show overlay immediately (CSS-centered placeholder) so the UI looks\n // ready while the camera is still loading.\n if (\n !this.overlay &&\n (this.options.highlightScanRegion ||\n this.options.highlightCodeOutline ||\n this.options.overlay)\n ) {\n try {\n this.overlay = new ScanOverlay(this.video, {\n highlightScanRegion: this.options.highlightScanRegion ?? false,\n highlightCodeOutline: this.options.highlightCodeOutline ?? false,\n customOverlay: this.options.overlay,\n });\n this.overlay.setup();\n } catch {\n // Overlay setup failed (e.g., no parent element) — continue without overlay\n }\n }\n\n // Start camera\n await this.camera.start(this.video);\n debug(\n `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`,\n );\n\n // Now that video dimensions are known, position overlay exactly\n if (this.overlay) {\n this.overlay.updateScanRegion(this.getCurrentScanRegion());\n }\n\n // Create worker if needed\n if (!this.worker) {\n const tw = performance.now();\n this.worker = this.createWorker();\n debug(\n `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`,\n );\n }\n\n // Create frame extractor if needed\n if (!this.frameExtractor) {\n this.frameExtractor = new FrameExtractor(this.video, {\n maxScansPerSecond: this.options.maxScansPerSecond ?? 15,\n getScanRegion: () => this.getCurrentScanRegion(),\n });\n }\n\n // Start frame extraction loop\n this.frameExtractor.start((imageData) => {\n this.sendToWorker(imageData);\n });\n\n this.active = true;\n this.paused = false;\n\n debug(`[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`);\n }\n\n stop(): void {\n this.frameExtractor?.stop();\n this.camera.stop();\n this.video.srcObject = null;\n this.active = false;\n this.paused = false;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.stop();\n this.frameExtractor?.destroy();\n this.frameExtractor = null;\n this.overlay?.destroy();\n this.overlay = null;\n this.worker?.terminate();\n this.worker = null;\n this.destroyed = true;\n }\n\n async pause(stopStreamImmediately = true): Promise<boolean> {\n if (!this.active) return false;\n\n this.frameExtractor?.stop();\n this.paused = true;\n\n if (stopStreamImmediately) {\n this.camera.stop();\n this.video.srcObject = null;\n }\n\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: string): Promise<void> {\n const wasActive = this.active && !this.paused;\n if (wasActive) {\n this.frameExtractor?.stop();\n }\n\n await this.camera.setCamera(facingModeOrDeviceId, this.video);\n\n if (wasActive) {\n this.frameExtractor?.start((imageData) => {\n this.sendToWorker(imageData);\n });\n }\n }\n\n async hasFlash(): Promise<boolean> {\n return this.camera.hasFlash();\n }\n\n isFlashOn(): boolean {\n return this.camera.isFlashOn();\n }\n\n async toggleFlash(): Promise<void> {\n return this.camera.toggleFlash();\n }\n\n async turnFlashOn(): Promise<void> {\n return this.camera.turnFlashOn();\n }\n\n async turnFlashOff(): Promise<void> {\n return this.camera.turnFlashOff();\n }\n\n setInversionMode(mode: InversionMode): void {\n if (!this.worker) return;\n\n const options: Partial<ReaderOptions> = {};\n switch (mode) {\n case 'original':\n options.tryInvert = false;\n break;\n case 'invert':\n options.tryInvert = true;\n break;\n case 'both':\n options.tryInvert = true;\n break;\n }\n\n const msg: WorkerRequest = { type: 'configure', options };\n this.worker.postMessage(msg);\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isDestroyed(): boolean {\n return this.destroyed;\n }\n\n private getCurrentScanRegion(): ScanRegion {\n if (this.options.calculateScanRegion) {\n return this.options.calculateScanRegion(this.video);\n }\n return calculateDefaultScanRegion(this.video);\n }\n\n private createWorker(): Worker {\n const workerUrl = resolveWorkerUrl();\n const worker = new Worker(workerUrl, { type: 'module' });\n\n // Configure with custom decoder options\n if (this.options.decoderOptions) {\n const msg: WorkerRequest = {\n type: 'configure',\n options: this.options.decoderOptions,\n };\n worker.postMessage(msg);\n }\n\n worker.onmessage = (e: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(e.data);\n };\n\n worker.onerror = (err) => {\n console.error('QR Scanner worker error:', err);\n this.frameExtractor?.markWorkerIdle();\n };\n\n return worker;\n }\n\n private handleWorkerMessage(response: WorkerResponse): void {\n this.frameExtractor?.markWorkerIdle();\n\n if (response.type === 'ready') {\n return;\n }\n\n if (response.type === 'error') {\n this.options.onDecodeError?.(response.message);\n return;\n }\n\n if (response.type === 'result') {\n if (response.results.length > 0) {\n const result = response.results[0];\n this.onDecode(result);\n\n // Update overlay\n if (this.overlay) {\n const region = this.getCurrentScanRegion();\n this.overlay.updateScanRegion(region);\n this.overlay.updateCodeOutline(result.cornerPoints, region);\n }\n } else {\n this.options.onDecodeError?.('No QR code found');\n\n // Hide code outline when no QR found\n if (this.overlay) {\n this.overlay.updateCodeOutline(null);\n }\n }\n }\n }\n\n private sendToWorker(imageData: ImageData): void {\n if (!this.worker) return;\n\n const msg: WorkerRequest = { type: 'decode', imageData };\n this.worker.postMessage(msg, [imageData.data.buffer]);\n }\n}\n","import { readBarcodes, type ReaderOptions } from 'zxing-wasm/reader';\nimport type { ScanResult, ScanRegion, Point } from './types.js';\nimport { loadImageData } from './utils.js';\n\nconst defaultReaderOptions: ReaderOptions = {\n formats: ['QRCode'],\n tryHarder: true,\n tryInvert: true,\n tryRotate: true,\n tryDenoise: false,\n tryDownscale: true,\n maxNumberOfSymbols: 1,\n};\n\nfunction mapPosition(position: {\n topLeft: Point;\n topRight: Point;\n bottomLeft: Point;\n bottomRight: Point;\n}): Point[] {\n return [\n position.topLeft,\n position.topRight,\n position.bottomRight,\n position.bottomLeft,\n ];\n}\n\n/** Input types that zxing-wasm can handle directly (no canvas needed). */\ntype DirectInput = Blob | ArrayBuffer | Uint8Array | ImageData;\n\n/** Input types that need canvas-based pixel extraction. */\ntype CanvasInput = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;\n\nfunction isDirectInput(source: unknown): source is DirectInput {\n return (\n source instanceof Blob ||\n source instanceof ArrayBuffer ||\n source instanceof Uint8Array ||\n (typeof ImageData !== 'undefined' && source instanceof ImageData)\n );\n}\n\n/**\n * Scan a single image for QR codes. Does not require a camera or video stream.\n */\nexport async function scanImage(\n source:\n | CanvasInput\n | DirectInput\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n): Promise<ScanResult> {\n const readerOptions: ReaderOptions = {\n ...defaultReaderOptions,\n ...options?.decoderOptions,\n formats: ['QRCode'],\n };\n\n let input: DirectInput;\n if (isDirectInput(source)) {\n input = source;\n } else if (typeof source === 'string' || source instanceof URL) {\n // URL string - fetch and pass as ArrayBuffer\n const url = source instanceof URL ? source.href : source;\n const response = await fetch(url);\n input = await response.arrayBuffer();\n } else {\n // Canvas-based sources - extract ImageData\n input = await loadImageData(source, options?.scanRegion, options?.canvas);\n }\n\n const results = await readBarcodes(input, readerOptions);\n const valid = results.filter((r) => r.isValid);\n\n if (valid.length === 0) {\n throw new Error('No QR code found in the image');\n }\n\n const first = valid[0];\n return {\n data: first.text,\n cornerPoints: mapPosition(first.position),\n };\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Load an image source into an ImageData object, optionally cropping to a scan region.\n */\nexport async function loadImageData(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n scanRegion?: ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n): Promise<ImageData> {\n const img = await resolveImageSource(source);\n\n const sx = scanRegion?.x ?? 0;\n const sy = scanRegion?.y ?? 0;\n const sw = scanRegion?.width ?? (img.width - sx);\n const sh = scanRegion?.height ?? (img.height - sy);\n\n let drawCanvas: HTMLCanvasElement | OffscreenCanvas;\n let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n\n if (canvas) {\n drawCanvas = canvas;\n canvas.width = sw;\n canvas.height = sh;\n ctx = canvas.getContext('2d')!;\n } else if (typeof OffscreenCanvas !== 'undefined') {\n drawCanvas = new OffscreenCanvas(sw, sh);\n ctx = drawCanvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n drawCanvas = document.createElement('canvas');\n drawCanvas.width = sw;\n drawCanvas.height = sh;\n ctx = drawCanvas.getContext('2d')!;\n }\n\n ctx.drawImage(img as CanvasImageSource, sx, sy, sw, sh, 0, 0, sw, sh);\n return ctx.getImageData(0, 0, sw, sh);\n}\n\nasync function resolveImageSource(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n): Promise<HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap> {\n if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || source instanceof ImageBitmap) {\n return source;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && source instanceof OffscreenCanvas) {\n return source;\n }\n\n if (source instanceof File || source instanceof Blob) {\n return createImageBitmapFromBlob(source);\n }\n\n // URL or string\n const url = source instanceof URL ? source.href : source as string;\n const response = await fetch(url);\n const blob = await response.blob();\n return createImageBitmapFromBlob(blob);\n}\n\nasync function createImageBitmapFromBlob(blob: Blob): Promise<ImageBitmap> {\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(blob);\n }\n // Fallback for environments without createImageBitmap\n const url = URL.createObjectURL(blob);\n try {\n const img = new Image();\n img.src = url;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error('Failed to load image'));\n });\n return img as unknown as ImageBitmap;\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n","import { Scanner, setWorkerUrl } from './scanner.js';\nimport {\n CameraManager,\n CameraNotFoundError,\n CameraPermissionError,\n} from './camera.js';\nimport { setDebug } from './debug.js';\nimport { scanImage } from './scan-image.js';\nimport { setZXingModuleOverrides } from 'zxing-wasm/reader';\nimport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\nexport { CameraNotFoundError, CameraPermissionError };\n\nexport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n};\n\n/**\n * High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly.\n */\nclass QrScanner {\n private scanner: Scanner;\n\n constructor(\n videoElement: HTMLVideoElement,\n onDecode: (result: ScanResult) => void,\n options: ScannerOptions = {},\n ) {\n this.scanner = new Scanner(videoElement, onDecode, options);\n }\n\n /** Start camera and begin scanning. Resolves when camera is ready. */\n async start(): Promise<void> {\n return this.scanner.start();\n }\n\n /** Stop scanning and release the camera stream. */\n stop(): void {\n this.scanner.stop();\n }\n\n /** Stop scanning, release camera, terminate worker, clean up DOM. */\n destroy(): void {\n this.scanner.destroy();\n }\n\n /** Pause scanning. If stopStreamImmediately is false, camera stays on. */\n async pause(stopStreamImmediately?: boolean): Promise<boolean> {\n return this.scanner.pause(stopStreamImmediately);\n }\n\n /** Switch to a different camera by facing mode or device ID. */\n async setCamera(facingModeOrDeviceId: FacingMode | DeviceId): Promise<void> {\n return this.scanner.setCamera(facingModeOrDeviceId);\n }\n\n /** Check if the current camera supports flash/torch. */\n async hasFlash(): Promise<boolean> {\n return this.scanner.hasFlash();\n }\n\n /** Whether flash is currently on. */\n isFlashOn(): boolean {\n return this.scanner.isFlashOn();\n }\n\n /** Toggle flash on/off. */\n async toggleFlash(): Promise<void> {\n return this.scanner.toggleFlash();\n }\n\n /** Turn flash on. */\n async turnFlashOn(): Promise<void> {\n return this.scanner.turnFlashOn();\n }\n\n /** Turn flash off. */\n async turnFlashOff(): Promise<void> {\n return this.scanner.turnFlashOff();\n }\n\n /** Set the inversion mode for detecting inverted QR codes. */\n setInversionMode(mode: InversionMode): void {\n this.scanner.setInversionMode(mode);\n }\n\n // --- Static methods ---\n\n /** Check if the device has at least one camera. */\n static hasCamera(): Promise<boolean> {\n return CameraManager.hasCamera();\n }\n\n /** List available cameras. Pass true to request labels (triggers permission prompt). */\n static listCameras(requestLabels?: boolean): Promise<Camera[]> {\n return CameraManager.listCameras(requestLabels);\n }\n\n /**\n * Pre-load the WASM binary so it's ready when the scanner starts.\n * Call this early (e.g., on app init) to avoid delay on first scan.\n */\n static async preload(): Promise<void> {\n // Trigger WASM loading by doing a minimal scan\n const pixel = new Uint8ClampedArray([255, 255, 255, 255]);\n const img = new ImageData(pixel, 1, 1);\n try {\n await scanImage(img);\n } catch {\n // Expected — no QR code in a 1x1 image. The point was to load WASM.\n }\n }\n\n /**\n * Configure WASM loading. Call before creating any scanner instance.\n * @example\n * QrScanner.configureWasm({ locateFile: (filename) => `/wasm/${filename}` });\n */\n static configureWasm(overrides: Partial<EmscriptenModule>): void {\n setZXingModuleOverrides(overrides);\n }\n\n /**\n * Set a custom URL for the worker script. Call before creating any scanner.\n * Needed for CJS consumers or non-standard bundler setups.\n * By default, the worker URL is resolved via `new URL('./worker.js', import.meta.url)`,\n * which works with Vite, webpack 5, Parcel, and other modern bundlers.\n * @example\n * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');\n */\n static setWorkerUrl(url: string | URL): void {\n setWorkerUrl(url);\n }\n\n /**\n * Enable or disable debug logging (performance timings, camera selection).\n * Off by default. Useful for diagnosing camera issues in the browser console.\n * @example\n * QrScanner.setDebug(true);\n */\n static setDebug(enabled: boolean): void {\n setDebug(enabled);\n }\n\n /** Scan a single image (not a video stream). */\n static scanImage(\n source:\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | Blob\n | ArrayBuffer\n | Uint8Array\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n ): Promise<ScanResult> {\n return scanImage(source, options);\n }\n}\n\nexport default QrScanner;\n"],"mappings":"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","names":["t","enabled"]}
|
|
1
|
+
{"version":3,"sources":["../src/debug.ts","../src/camera.ts","../src/frame-extractor.ts","../src/overlay.ts","../src/scan-region.ts","../src/scanner.ts","../src/scan-image.ts","../src/utils.ts","../src/decoder-utils.ts","../src/index.ts"],"sourcesContent":["let enabled = false;\n\nexport function setDebug(on: boolean): void {\n enabled = on;\n}\n\nexport function debug(...args: unknown[]): void {\n if (enabled) console.debug(...args);\n}\n","import { debug } from './debug.js';\nimport type { FacingMode, DeviceId, Camera } from './types.js';\n\nexport class CameraPermissionError extends Error {\n constructor(\n message = 'Camera access denied. Please grant camera permission and try again.',\n ) {\n super(message);\n this.name = 'CameraPermissionError';\n }\n}\n\nexport class CameraNotFoundError extends Error {\n constructor(\n message = 'No camera found. Please connect a camera and try again.',\n ) {\n super(message);\n this.name = 'CameraNotFoundError';\n }\n}\n\nconst CACHE_KEY_PREFIX = '@agicash/qr-scanner:camera:';\n\nfunction getCachedDeviceId(facingMode: string): string | null {\n try {\n return localStorage.getItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n return null;\n }\n}\n\nfunction setCachedDeviceId(facingMode: string, deviceId: string): void {\n try {\n localStorage.setItem(`${CACHE_KEY_PREFIX}${facingMode}`, deviceId);\n } catch {\n // localStorage unavailable or full — ignore\n }\n}\n\nfunction clearCachedDeviceId(facingMode: string): void {\n try {\n localStorage.removeItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n // localStorage unavailable — ignore\n }\n}\n\nexport interface CameraConfig {\n preferredCamera?: FacingMode | DeviceId;\n cameraResolution?: {\n width?: MediaTrackConstraintSet['width'];\n height?: MediaTrackConstraintSet['height'];\n };\n}\n\nexport class CameraManager {\n private stream: MediaStream | null = null;\n private facingMode: FacingMode | DeviceId;\n private resolution: CameraConfig['cameraResolution'];\n\n constructor(config: CameraConfig = {}) {\n this.facingMode = config.preferredCamera ?? 'environment';\n this.resolution = config.cameraResolution;\n }\n\n async start(video: HTMLVideoElement): Promise<MediaStream> {\n if (this.stream) {\n return this.stream;\n }\n\n const t0 = performance.now();\n\n this.stream = await this.acquireStream();\n const t1 = performance.now();\n debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);\n\n // On some devices (e.g. Samsung S24 + Brave), facingMode: 'environment'\n // picks an ultrawide camera that lacks autofocus. Check and switch to a\n // better camera BEFORE showing on screen to avoid visible flicker.\n await this.ensureBestCamera();\n const t2 = performance.now();\n debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);\n\n video.srcObject = this.stream;\n video.setAttribute('playsinline', 'true');\n await video.play();\n const t3 = performance.now();\n debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);\n debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);\n\n // Cache the final camera so subsequent starts skip ensureBestCamera\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;\n if (finalDeviceId) {\n setCachedDeviceId(this.facingMode, finalDeviceId);\n }\n }\n\n return this.stream;\n }\n\n stop(): void {\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n }\n\n async setCamera(\n facingModeOrDeviceId: FacingMode | DeviceId,\n video: HTMLVideoElement,\n ): Promise<void> {\n this.stop();\n this.facingMode = facingModeOrDeviceId;\n await this.start(video);\n }\n\n getStream(): MediaStream | null {\n return this.stream;\n }\n\n async hasFlash(): Promise<boolean> {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n torch?: boolean;\n };\n return capabilities.torch === true;\n } catch {\n return false;\n }\n }\n\n isFlashOn(): boolean {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n const settings = track.getSettings() as MediaTrackSettings & {\n torch?: boolean;\n };\n return settings.torch === true;\n }\n\n async toggleFlash(): Promise<void> {\n if (this.isFlashOn()) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise<void> {\n await this.setTorch(true);\n }\n\n async turnFlashOff(): Promise<void> {\n await this.setTorch(false);\n }\n\n private async setTorch(on: boolean): Promise<void> {\n const track = this.getVideoTrack();\n if (!track) {\n throw new Error('No active camera stream');\n }\n\n try {\n await track.applyConstraints({\n advanced: [{ torch: on } as MediaTrackConstraintSet],\n });\n } catch {\n throw new Error('Flash/torch is not supported on this device');\n }\n }\n\n /**\n * If the current camera lacks continuous autofocus (e.g. an ultrawide sensor\n * picked by facingMode: 'environment'), find a better camera with the same\n * facing mode and replace this.stream. Called before assigning to the video\n * element so the user never sees the wrong camera.\n */\n private async ensureBestCamera(): Promise<void> {\n if (this.facingMode !== 'environment' && this.facingMode !== 'user') {\n debug('[QrScanner] ensureBestCamera: skipped (specific deviceId)');\n return;\n }\n\n const track = this.getVideoTrack();\n if (!track) return;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (\n !capabilities.focusMode ||\n capabilities.focusMode.includes('continuous')\n ) {\n // focusMode not reported (e.g. Safari/iOS) or has autofocus — skip.\n // Only enter the candidate loop when the browser explicitly reports\n // focusMode without 'continuous' (e.g. S24 + Brave ultrawide).\n debug(\n `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n return;\n }\n debug(\n `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n } catch (err) {\n debug(\n `[QrScanner] ensureBestCamera: skipped (getCapabilities failed: ${err instanceof Error ? err.message : err})`,\n );\n return;\n }\n\n // Current camera lacks continuous autofocus.\n // Enumerate devices while stream is active (ensures deviceIds are available).\n const currentDeviceId = track.getSettings().deviceId;\n\n let devices: MediaDeviceInfo[];\n try {\n devices = await navigator.mediaDevices.enumerateDevices();\n } catch (err) {\n debug(\n `[QrScanner] ensureBestCamera: skipped (enumerateDevices failed: ${err instanceof Error ? err.message : err})`,\n );\n return;\n }\n if (!Array.isArray(devices)) return;\n\n const candidates = devices.filter(\n (d) => d.kind === 'videoinput' && d.deviceId !== currentDeviceId,\n );\n debug(\n `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`,\n );\n if (candidates.length === 0) return;\n\n // Stop current stream — mobile devices only allow one active camera\n this.stop();\n\n for (const candidate of candidates) {\n const t = performance.now();\n let candidateStream: MediaStream;\n try {\n candidateStream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: candidate.deviceId },\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`,\n );\n } catch (err) {\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms — ${err instanceof Error ? err.name : err}`,\n );\n if (err instanceof DOMException && err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n continue;\n }\n\n const candidateTrack = candidateStream.getVideoTracks()[0];\n if (!candidateTrack) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Must match the desired facing mode\n const candidateSettings =\n candidateTrack.getSettings() as MediaTrackSettings & {\n facingMode?: string;\n };\n if (\n candidateSettings.facingMode &&\n candidateSettings.facingMode !== this.facingMode\n ) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Check if this camera supports continuous autofocus\n try {\n const candidateCaps =\n candidateTrack.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (candidateCaps.focusMode?.includes('continuous')) {\n this.stream = candidateStream;\n return;\n }\n } catch (err) {\n debug(\n `[QrScanner] ensureBestCamera: candidate getCapabilities failed — ${err instanceof Error ? err.message : err}`,\n );\n }\n\n for (const t of candidateStream.getTracks()) t.stop();\n }\n\n // No better camera found — re-open the original with progressively\n // simpler constraints so we never leave this.stream as null.\n const recoveryAttempts: MediaStreamConstraints[] = [\n // Original deviceId + resolution\n {\n video: {\n deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined,\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n },\n // facingMode + resolution\n this.buildConstraints(),\n // facingMode only\n this.buildConstraints(false),\n // Bare minimum — should always succeed if a camera exists\n { video: true, audio: false },\n ];\n\n let lastRecoveryError: unknown;\n for (const constraints of recoveryAttempts) {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n return;\n } catch (err) {\n if (err instanceof DOMException && err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err instanceof DOMException && err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n lastRecoveryError = err;\n debug(\n `[QrScanner] ensureBestCamera recovery failed: ${err instanceof Error ? err.name : err}`,\n );\n continue;\n }\n }\n debug(\n `[QrScanner] ensureBestCamera: all recovery attempts failed, last error: ${lastRecoveryError instanceof Error ? lastRecoveryError.message : lastRecoveryError}`,\n );\n }\n\n private getVideoTrack(): MediaStreamTrack | null {\n if (!this.stream) return null;\n const tracks = this.stream.getVideoTracks();\n return tracks[0] ?? null;\n }\n\n /**\n * Try getUserMedia with progressively simpler constraints.\n *\n * Some browsers (e.g. Brave on Samsung Galaxy S24) throw NotReadableError\n * when facingMode and resolution constraints are combined. Falling back to\n * fewer constraints lets us still open the camera on those browsers.\n */\n private async acquireStream(): Promise<MediaStream> {\n const attempts: {\n label: string;\n constraints: MediaStreamConstraints;\n isCachedDeviceId?: boolean;\n }[] = [];\n\n // If we've previously found a good camera for this facingMode, try it first\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const cachedId = getCachedDeviceId(this.facingMode);\n if (cachedId) {\n const video: MediaTrackConstraints = {\n deviceId: { exact: cachedId },\n };\n if (this.resolution?.width) video.width = this.resolution.width;\n else video.width = { ideal: 1920 };\n if (this.resolution?.height) video.height = this.resolution.height;\n else video.height = { ideal: 1080 };\n attempts.push({\n label: 'cached deviceId',\n constraints: { video, audio: false },\n isCachedDeviceId: true,\n });\n }\n }\n\n // Standard fallback chain\n attempts.push(\n { label: 'full constraints', constraints: this.buildConstraints() },\n { label: 'no resolution', constraints: this.buildConstraints(false) },\n { label: 'bare minimum', constraints: { video: true, audio: false } },\n );\n\n let lastError: unknown;\n for (const attempt of attempts) {\n const t = performance.now();\n try {\n const stream = await navigator.mediaDevices.getUserMedia(\n attempt.constraints,\n );\n debug(\n `[QrScanner] getUserMedia(${attempt.label}): ${(performance.now() - t).toFixed(0)}ms ✓`,\n );\n return stream;\n } catch (err) {\n debug(\n `[QrScanner] getUserMedia(${attempt.label}): ${(performance.now() - t).toFixed(0)}ms ✗ ${err instanceof Error ? err.name : err}`,\n );\n // Permission denied and no camera are fatal — no fallback can help.\n if (err instanceof DOMException && err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err instanceof DOMException && err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n // DOMException subtypes (NotReadableError, etc.) and\n // OverconstrainedError are retryable with simpler constraints.\n if (\n !(err instanceof DOMException) &&\n !(err instanceof OverconstrainedError)\n ) {\n throw err;\n }\n if (attempt.isCachedDeviceId) {\n // Stale cached deviceId (e.g. iOS regenerated device IDs after an\n // update). Clear it so subsequent attempts don't repeat the failure.\n clearCachedDeviceId(this.facingMode);\n debug(\n `[QrScanner] cleared stale cached deviceId for \"${this.facingMode}\"`,\n );\n }\n lastError = err;\n continue;\n }\n }\n\n throw lastError;\n }\n\n private buildConstraints(includeResolution = true): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n\n if (includeResolution) {\n video.width = this.resolution?.width ?? { ideal: 1920 };\n video.height = this.resolution?.height ?? { ideal: 1080 };\n }\n\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n video.facingMode = this.facingMode;\n } else {\n video.deviceId = { exact: this.facingMode };\n }\n\n return { video, audio: false };\n }\n\n static async hasCamera(): Promise<boolean> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.some((d) => d.kind === 'videoinput');\n } catch {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise<Camera[]> {\n if (requestLabels) {\n // Requesting labels requires a temporary stream to trigger the permission prompt\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n for (const track of stream.getTracks()) {\n track.stop();\n }\n } catch {\n // Permission denied — fall through with empty labels\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === 'videoinput')\n .map((d) => ({\n id: d.deviceId,\n label: d.label || `Camera ${d.deviceId.slice(0, 8)}`,\n }));\n }\n}\n","import type { ScanRegion } from './types.js';\n\nexport interface FrameExtractorConfig {\n maxScansPerSecond: number;\n getScanRegion: () => ScanRegion;\n}\n\nexport class FrameExtractor {\n private video: HTMLVideoElement;\n private canvas: HTMLCanvasElement | OffscreenCanvas;\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private rafId: number | null = null;\n private running = false;\n private workerBusy = false;\n private lastScanTime = -Infinity;\n private minInterval: number;\n private getScanRegion: () => ScanRegion;\n private onFrame: ((imageData: ImageData) => void) | null = null;\n\n constructor(video: HTMLVideoElement, config: FrameExtractorConfig) {\n this.video = video;\n this.minInterval = 1000 / config.maxScansPerSecond;\n this.getScanRegion = config.getScanRegion;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n this.canvas = new OffscreenCanvas(1, 1);\n this.ctx = this.canvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'none';\n this.ctx = this.canvas.getContext('2d')!;\n }\n }\n\n start(onFrame: (imageData: ImageData) => void): void {\n if (this.running) return;\n this.running = true;\n this.onFrame = onFrame;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n this.running = false;\n this.onFrame = null;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n destroy(): void {\n this.stop();\n if (this.canvas instanceof HTMLCanvasElement && this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n }\n\n markWorkerIdle(): void {\n this.workerBusy = false;\n }\n\n private tick = (): void => {\n if (!this.running) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n\n // Skip if worker is still processing previous frame\n if (this.workerBusy) return;\n\n // Rate limiting\n const now = performance.now();\n if (now - this.lastScanTime < this.minInterval) return;\n\n // Skip if video isn't ready\n if (this.video.readyState < 2) return;\n\n this.lastScanTime = now;\n\n const region = this.getScanRegion();\n const sx = region.x ?? 0;\n const sy = region.y ?? 0;\n const sw = region.width ?? this.video.videoWidth;\n const sh = region.height ?? this.video.videoHeight;\n\n if (sw <= 0 || sh <= 0) return;\n\n this.canvas.width = sw;\n this.canvas.height = sh;\n\n this.ctx.drawImage(this.video, sx, sy, sw, sh, 0, 0, sw, sh);\n const imageData = this.ctx.getImageData(0, 0, sw, sh);\n\n this.workerBusy = true;\n this.onFrame?.(imageData);\n };\n}\n","import type { ScanRegion, Point } from './types.js';\n\n/**\n * Compute the actual rendered position and size of the video content\n * within the element, accounting for object-fit: cover.\n *\n * With object-fit: cover the video is scaled up to fill the element and\n * cropped. The rendered content is larger than the element, centered,\n * with negative offsets for the cropped portions.\n *\n * When object-fit is the default (fill), the rendered size equals the\n * element size and offsets are zero — so this is backwards-compatible.\n */\nfunction getRenderedVideoRect(video: HTMLVideoElement): {\n offsetX: number;\n offsetY: number;\n width: number;\n height: number;\n} {\n const elementWidth = video.clientWidth;\n const elementHeight = video.clientHeight;\n const videoWidth = video.videoWidth || 1;\n const videoHeight = video.videoHeight || 1;\n\n const objectFit = getComputedStyle(video).objectFit;\n\n if (objectFit === 'cover' || objectFit === 'contain') {\n const scaleFn = objectFit === 'cover' ? Math.max : Math.min;\n const scale = scaleFn(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n // Default (fill / none / scale-down with no scaling needed): element dimensions\n return { offsetX: 0, offsetY: 0, width: elementWidth, height: elementHeight };\n}\n\nexport interface OverlayConfig {\n highlightScanRegion: boolean;\n highlightCodeOutline: boolean;\n customOverlay?: HTMLDivElement;\n}\n\nexport class ScanOverlay {\n private container: HTMLElement;\n private overlayEl: HTMLDivElement | null = null;\n private codeOutlineEl: SVGElement | null = null;\n private config: OverlayConfig;\n private video: HTMLVideoElement;\n\n constructor(video: HTMLVideoElement, config: OverlayConfig) {\n this.video = video;\n this.config = config;\n\n const parent = video.parentElement;\n if (!parent) {\n throw new Error(\n 'QrScanner: video element must have a parent element. ' +\n 'The parent should have position: relative.',\n );\n }\n this.container = parent;\n }\n\n setup(): void {\n if (this.config.customOverlay) {\n this.overlayEl = this.config.customOverlay;\n this.positionOverlay();\n return;\n }\n\n if (this.config.highlightScanRegion) {\n this.createScanRegionOverlay();\n }\n\n if (this.config.highlightCodeOutline) {\n this.createCodeOutline();\n }\n }\n\n updateScanRegion(region: ScanRegion): void {\n if (!this.overlayEl || this.config.customOverlay) return;\n this.positionOverlayToRegion(region);\n }\n\n updateCodeOutline(\n cornerPoints: Point[] | null,\n scanRegion?: ScanRegion,\n ): void {\n if (!this.codeOutlineEl) return;\n\n if (!cornerPoints || cornerPoints.length < 4) {\n this.codeOutlineEl.style.display = 'none';\n return;\n }\n\n this.codeOutlineEl.style.display = 'block';\n\n const polygon = this.codeOutlineEl.querySelector('polygon');\n if (!polygon) return;\n\n // Corner points are relative to the cropped scan region.\n // Add the scan region offset to get full video coordinates,\n // then scale to display coordinates (accounting for object-fit).\n const regionX = scanRegion?.x ?? 0;\n const regionY = scanRegion?.y ?? 0;\n const rendered = getRenderedVideoRect(this.video);\n const scaleX = rendered.width / this.video.videoWidth;\n const scaleY = rendered.height / this.video.videoHeight;\n\n const points = cornerPoints\n .map(\n (p) =>\n `${(p.x + regionX) * scaleX + rendered.offsetX},${(p.y + regionY) * scaleY + rendered.offsetY}`,\n )\n .join(' ');\n\n polygon.setAttribute('points', points);\n }\n\n destroy(): void {\n if (this.overlayEl && !this.config.customOverlay) {\n this.overlayEl.remove();\n }\n if (this.codeOutlineEl) {\n this.codeOutlineEl.remove();\n }\n this.overlayEl = null;\n this.codeOutlineEl = null;\n }\n\n private createScanRegionOverlay(): void {\n this.overlayEl = document.createElement('div');\n this.overlayEl.className = 'qr-scanner-region';\n\n // Overlay size: 3/4 of the smaller container dimension, centered.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: `${size}px`,\n height: `${size}px`,\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '8px',\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',\n pointerEvents: 'none',\n zIndex: '10',\n });\n\n // Corner markers\n const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\n for (const corner of corners) {\n const marker = document.createElement('div');\n marker.className = `qr-scanner-corner qr-scanner-corner-${corner}`;\n\n const [vertical, horizontal] = corner.split('-');\n\n Object.assign(marker.style, {\n position: 'absolute',\n width: '24px',\n height: '24px',\n [vertical]: '-2px',\n [horizontal]: '-2px',\n [`border-${vertical}`]: '3px solid white',\n [`border-${horizontal}`]: '3px solid white',\n [`border-${vertical}-${horizontal}-radius`]: '8px',\n });\n\n this.overlayEl.appendChild(marker);\n }\n\n this.container.appendChild(this.overlayEl);\n }\n\n private createCodeOutline(): void {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('class', 'qr-scanner-code-outline');\n\n Object.assign(svg.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '11',\n display: 'none',\n });\n\n const polygon = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'polygon',\n );\n polygon.setAttribute('fill', 'none');\n polygon.setAttribute('stroke', '#00ff00');\n polygon.setAttribute('stroke-width', '3');\n polygon.setAttribute('stroke-linejoin', 'round');\n\n // Animated stroke\n const animate = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'animate',\n );\n animate.setAttribute('attributeName', 'stroke-opacity');\n animate.setAttribute('values', '1;0.5;1');\n animate.setAttribute('dur', '1.5s');\n animate.setAttribute('repeatCount', 'indefinite');\n polygon.appendChild(animate);\n\n svg.appendChild(polygon);\n this.container.appendChild(svg);\n this.codeOutlineEl = svg;\n }\n\n private positionOverlay(): void {\n if (!this.overlayEl) return;\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '10',\n });\n }\n\n private positionOverlayToRegion(_region: ScanRegion): void {\n if (!this.overlayEl) return;\n\n // Keep the overlay at a container-relative centered size rather than\n // mapping scan region coordinates to display coordinates. With\n // object-fit: cover on a portrait phone with a landscape camera, the\n // mapped region extends beyond the container — fine once the camera is\n // visible but causes a jarring jump from the initial placeholder.\n // The overlay is a visual guide; the actual scan area (in the frame\n // extractor) is unaffected and may be larger than what's shown.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n // Only update size — initial CSS centering (top: 50%, left: 50%,\n // transform: translate(-50%, -50%)) persists and handles re-centering.\n Object.assign(this.overlayEl.style, {\n width: `${size}px`,\n height: `${size}px`,\n });\n }\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Calculate the default scan region: a centered square covering\n * 2/3 of the smaller video dimension.\n */\nexport function calculateDefaultScanRegion(video: HTMLVideoElement): ScanRegion {\n const videoWidth = video.videoWidth || video.width;\n const videoHeight = video.videoHeight || video.height;\n\n const smallerDimension = Math.min(videoWidth, videoHeight);\n const size = Math.round((smallerDimension * 2) / 3);\n\n return {\n x: Math.round((videoWidth - size) / 2),\n y: Math.round((videoHeight - size) / 2),\n width: size,\n height: size,\n };\n}\n","import { CameraManager } from './camera.js';\nimport { debug } from './debug.js';\nimport { FrameExtractor } from './frame-extractor.js';\nimport { ScanOverlay } from './overlay.js';\nimport { calculateDefaultScanRegion } from './scan-region.js';\nimport type {\n ScannerOptions,\n ScanResult,\n ScanRegion,\n InversionMode,\n WorkerRequest,\n WorkerResponse,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\ntype OnDecodeCallback = (result: ScanResult) => void;\n\n/**\n * Custom worker URL override. When set, this URL is used instead of the\n * default bundler-resolved worker. Useful for CJS consumers or non-standard\n * bundler setups.\n */\nlet customWorkerUrl: string | URL | null = null;\n\nexport function setWorkerUrl(url: string | URL): void {\n customWorkerUrl = url;\n}\n\nfunction resolveWorkerUrl(): string | URL {\n if (customWorkerUrl) {\n return customWorkerUrl;\n }\n // Standard pattern: modern bundlers (Vite, webpack 5, Parcel, esbuild)\n // resolve `new URL('./file', import.meta.url)` at build time,\n // copying worker.js to the output directory and returning the correct URL.\n // Falls back gracefully for CJS builds where import.meta is unavailable.\n try {\n return new URL('./worker.js', import.meta.url);\n } catch {\n throw new Error(\n '@agicash/qr-scanner: Could not resolve worker URL. ' +\n 'Call QrScanner.setWorkerUrl() with the path to the worker script before creating a scanner. ' +\n 'Example: QrScanner.setWorkerUrl(\"/path/to/@agicash/qr-scanner/dist/worker.js\")',\n );\n }\n}\n\nexport class Scanner {\n private video: HTMLVideoElement;\n private onDecode: OnDecodeCallback;\n private options: ScannerOptions;\n private camera: CameraManager;\n private frameExtractor: FrameExtractor | null = null;\n private worker: Worker | null = null;\n private overlay: ScanOverlay | null = null;\n private active = false;\n private paused = false;\n private destroyed = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: OnDecodeCallback,\n options: ScannerOptions = {},\n ) {\n this.video = video;\n this.onDecode = onDecode;\n this.options = options;\n\n this.camera = new CameraManager({\n preferredCamera: options.preferredCamera,\n cameraResolution: options.cameraResolution,\n });\n }\n\n async start(): Promise<void> {\n if (this.destroyed) {\n throw new Error('Scanner has been destroyed');\n }\n\n if (this.active && !this.paused) {\n return; // Already running\n }\n\n const t0 = performance.now();\n\n // Show overlay immediately (CSS-centered placeholder) so the UI looks\n // ready while the camera is still loading.\n if (\n !this.overlay &&\n (this.options.highlightScanRegion ||\n this.options.highlightCodeOutline ||\n this.options.overlay)\n ) {\n try {\n this.overlay = new ScanOverlay(this.video, {\n highlightScanRegion: this.options.highlightScanRegion ?? false,\n highlightCodeOutline: this.options.highlightCodeOutline ?? false,\n customOverlay: this.options.overlay,\n });\n this.overlay.setup();\n } catch {\n // Overlay setup failed (e.g., no parent element) — continue without overlay\n }\n }\n\n // Start camera\n await this.camera.start(this.video);\n debug(\n `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`,\n );\n\n // Now that video dimensions are known, position overlay exactly\n if (this.overlay) {\n this.overlay.updateScanRegion(this.getCurrentScanRegion());\n }\n\n // Create worker if needed\n if (!this.worker) {\n const tw = performance.now();\n this.worker = this.createWorker();\n debug(\n `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`,\n );\n }\n\n // Create frame extractor if needed\n if (!this.frameExtractor) {\n this.frameExtractor = new FrameExtractor(this.video, {\n maxScansPerSecond: this.options.maxScansPerSecond ?? 15,\n getScanRegion: () => this.getCurrentScanRegion(),\n });\n }\n\n // Start frame extraction loop\n this.frameExtractor.start((imageData) => {\n this.sendToWorker(imageData);\n });\n\n this.active = true;\n this.paused = false;\n\n debug(`[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`);\n }\n\n stop(): void {\n this.frameExtractor?.stop();\n this.camera.stop();\n this.video.srcObject = null;\n this.active = false;\n this.paused = false;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.stop();\n this.frameExtractor?.destroy();\n this.frameExtractor = null;\n this.overlay?.destroy();\n this.overlay = null;\n this.worker?.terminate();\n this.worker = null;\n this.destroyed = true;\n }\n\n async pause(stopStreamImmediately = true): Promise<boolean> {\n if (!this.active) return false;\n\n this.frameExtractor?.stop();\n this.paused = true;\n\n if (stopStreamImmediately) {\n this.camera.stop();\n this.video.srcObject = null;\n }\n\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: string): Promise<void> {\n const wasActive = this.active && !this.paused;\n if (wasActive) {\n this.frameExtractor?.stop();\n }\n\n await this.camera.setCamera(facingModeOrDeviceId, this.video);\n\n if (wasActive) {\n this.frameExtractor?.start((imageData) => {\n this.sendToWorker(imageData);\n });\n }\n }\n\n async hasFlash(): Promise<boolean> {\n return this.camera.hasFlash();\n }\n\n isFlashOn(): boolean {\n return this.camera.isFlashOn();\n }\n\n async toggleFlash(): Promise<void> {\n return this.camera.toggleFlash();\n }\n\n async turnFlashOn(): Promise<void> {\n return this.camera.turnFlashOn();\n }\n\n async turnFlashOff(): Promise<void> {\n return this.camera.turnFlashOff();\n }\n\n setInversionMode(mode: InversionMode): void {\n if (!this.worker) return;\n\n const options: Partial<ReaderOptions> = {\n tryInvert: mode !== 'original',\n };\n\n const msg: WorkerRequest = { type: 'configure', options };\n this.worker.postMessage(msg);\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isDestroyed(): boolean {\n return this.destroyed;\n }\n\n private getCurrentScanRegion(): ScanRegion {\n if (this.options.calculateScanRegion) {\n return this.options.calculateScanRegion(this.video);\n }\n return calculateDefaultScanRegion(this.video);\n }\n\n private createWorker(): Worker {\n const workerUrl = resolveWorkerUrl();\n const worker = new Worker(workerUrl, { type: 'module' });\n\n // Configure with custom decoder options\n if (this.options.decoderOptions) {\n const msg: WorkerRequest = {\n type: 'configure',\n options: this.options.decoderOptions,\n };\n worker.postMessage(msg);\n }\n\n worker.onmessage = (e: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(e.data);\n };\n\n worker.onerror = (err) => {\n console.error('QR Scanner worker error:', err);\n this.frameExtractor?.markWorkerIdle();\n };\n\n return worker;\n }\n\n private handleWorkerMessage(response: WorkerResponse): void {\n this.frameExtractor?.markWorkerIdle();\n\n if (response.type === 'ready') {\n return;\n }\n\n if (response.type === 'error') {\n this.options.onDecodeError?.(response.message);\n return;\n }\n\n if (response.type === 'result') {\n if (response.results.length > 0) {\n const result = response.results[0];\n this.onDecode(result);\n\n // Update overlay\n if (this.overlay) {\n const region = this.getCurrentScanRegion();\n this.overlay.updateScanRegion(region);\n this.overlay.updateCodeOutline(result.cornerPoints, region);\n }\n } else {\n this.options.onDecodeError?.('No QR code found');\n\n // Hide code outline when no QR found\n if (this.overlay) {\n this.overlay.updateCodeOutline(null);\n }\n }\n }\n }\n\n private sendToWorker(imageData: ImageData): void {\n if (!this.worker) return;\n\n const msg: WorkerRequest = { type: 'decode', imageData };\n this.worker.postMessage(msg, [imageData.data.buffer]);\n }\n}\n","import { readBarcodes, type ReaderOptions } from 'zxing-wasm/reader';\nimport type { ScanResult, ScanRegion } from './types.js';\nimport { loadImageData } from './utils.js';\nimport { DEFAULT_READER_OPTIONS, mapPosition } from './decoder-utils.js';\n\n/** Input types that zxing-wasm can handle directly (no canvas needed). */\ntype DirectInput = Blob | ArrayBuffer | Uint8Array | ImageData;\n\n/** Input types that need canvas-based pixel extraction. */\ntype CanvasInput = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;\n\nfunction isDirectInput(source: unknown): source is DirectInput {\n return (\n source instanceof Blob ||\n source instanceof ArrayBuffer ||\n source instanceof Uint8Array ||\n (typeof ImageData !== 'undefined' && source instanceof ImageData)\n );\n}\n\n/**\n * Scan a single image for QR codes. Does not require a camera or video stream.\n */\nexport async function scanImage(\n source:\n | CanvasInput\n | DirectInput\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n): Promise<ScanResult> {\n const readerOptions: ReaderOptions = {\n ...DEFAULT_READER_OPTIONS,\n ...options?.decoderOptions,\n formats: ['QRCode'],\n };\n\n let input: DirectInput;\n if (isDirectInput(source)) {\n input = source;\n } else if (typeof source === 'string' || source instanceof URL) {\n // URL string - fetch and pass as ArrayBuffer\n const url = source instanceof URL ? source.href : source;\n const response = await fetch(url);\n input = await response.arrayBuffer();\n } else {\n // Canvas-based sources - extract ImageData\n input = await loadImageData(source, options?.scanRegion, options?.canvas);\n }\n\n const results = await readBarcodes(input, readerOptions);\n const valid = results.filter((r) => r.isValid);\n\n if (valid.length === 0) {\n throw new Error('No QR code found in the image');\n }\n\n const first = valid[0];\n return {\n data: first.text,\n cornerPoints: mapPosition(first.position),\n };\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Load an image source into an ImageData object, optionally cropping to a scan region.\n */\nexport async function loadImageData(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n scanRegion?: ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n): Promise<ImageData> {\n const img = await resolveImageSource(source);\n\n const sx = scanRegion?.x ?? 0;\n const sy = scanRegion?.y ?? 0;\n const sw = scanRegion?.width ?? (img.width - sx);\n const sh = scanRegion?.height ?? (img.height - sy);\n\n let drawCanvas: HTMLCanvasElement | OffscreenCanvas;\n let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n\n if (canvas) {\n drawCanvas = canvas;\n canvas.width = sw;\n canvas.height = sh;\n ctx = canvas.getContext('2d')!;\n } else if (typeof OffscreenCanvas !== 'undefined') {\n drawCanvas = new OffscreenCanvas(sw, sh);\n ctx = drawCanvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n drawCanvas = document.createElement('canvas');\n drawCanvas.width = sw;\n drawCanvas.height = sh;\n ctx = drawCanvas.getContext('2d')!;\n }\n\n ctx.drawImage(img as CanvasImageSource, sx, sy, sw, sh, 0, 0, sw, sh);\n return ctx.getImageData(0, 0, sw, sh);\n}\n\nasync function resolveImageSource(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n): Promise<HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap> {\n if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || source instanceof ImageBitmap) {\n return source;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && source instanceof OffscreenCanvas) {\n return source;\n }\n\n if (source instanceof File || source instanceof Blob) {\n return createImageBitmapFromBlob(source);\n }\n\n // URL or string\n const url = source instanceof URL ? source.href : source as string;\n const response = await fetch(url);\n const blob = await response.blob();\n return createImageBitmapFromBlob(blob);\n}\n\nasync function createImageBitmapFromBlob(blob: Blob): Promise<ImageBitmap> {\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(blob);\n }\n // Fallback for environments without createImageBitmap\n const url = URL.createObjectURL(blob);\n try {\n const img = new Image();\n img.src = url;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error('Failed to load image'));\n });\n return img as unknown as ImageBitmap;\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n","import type { ReaderOptions } from 'zxing-wasm/reader';\nimport type { Point } from './types.js';\n\nexport const DEFAULT_READER_OPTIONS: ReaderOptions = {\n formats: ['QRCode'],\n tryHarder: true,\n tryInvert: true,\n tryRotate: true,\n tryDenoise: false,\n tryDownscale: true,\n maxNumberOfSymbols: 1,\n};\n\nexport function mapPosition(position: {\n topLeft: Point;\n topRight: Point;\n bottomLeft: Point;\n bottomRight: Point;\n}): Point[] {\n return [\n position.topLeft,\n position.topRight,\n position.bottomRight,\n position.bottomLeft,\n ];\n}\n","import { Scanner, setWorkerUrl } from './scanner.js';\nimport {\n CameraManager,\n CameraNotFoundError,\n CameraPermissionError,\n} from './camera.js';\nimport { setDebug } from './debug.js';\nimport { scanImage } from './scan-image.js';\nimport { setZXingModuleOverrides } from 'zxing-wasm/reader';\nimport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\nexport { CameraNotFoundError, CameraPermissionError };\n\nexport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n};\n\n/**\n * High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly.\n */\nclass QrScanner {\n private scanner: Scanner;\n\n constructor(\n videoElement: HTMLVideoElement,\n onDecode: (result: ScanResult) => void,\n options: ScannerOptions = {},\n ) {\n this.scanner = new Scanner(videoElement, onDecode, options);\n }\n\n /** Start camera and begin scanning. Resolves when camera is ready. */\n async start(): Promise<void> {\n return this.scanner.start();\n }\n\n /** Stop scanning and release the camera stream. */\n stop(): void {\n this.scanner.stop();\n }\n\n /** Stop scanning, release camera, terminate worker, clean up DOM. */\n destroy(): void {\n this.scanner.destroy();\n }\n\n /** Pause scanning. If stopStreamImmediately is false, camera stays on. */\n async pause(stopStreamImmediately?: boolean): Promise<boolean> {\n return this.scanner.pause(stopStreamImmediately);\n }\n\n /** Switch to a different camera by facing mode or device ID. */\n async setCamera(facingModeOrDeviceId: FacingMode | DeviceId): Promise<void> {\n return this.scanner.setCamera(facingModeOrDeviceId);\n }\n\n /** Check if the current camera supports flash/torch. */\n async hasFlash(): Promise<boolean> {\n return this.scanner.hasFlash();\n }\n\n /** Whether flash is currently on. */\n isFlashOn(): boolean {\n return this.scanner.isFlashOn();\n }\n\n /** Toggle flash on/off. */\n async toggleFlash(): Promise<void> {\n return this.scanner.toggleFlash();\n }\n\n /** Turn flash on. */\n async turnFlashOn(): Promise<void> {\n return this.scanner.turnFlashOn();\n }\n\n /** Turn flash off. */\n async turnFlashOff(): Promise<void> {\n return this.scanner.turnFlashOff();\n }\n\n /** Set the inversion mode for detecting inverted QR codes. */\n setInversionMode(mode: InversionMode): void {\n this.scanner.setInversionMode(mode);\n }\n\n // --- Static methods ---\n\n /** Check if the device has at least one camera. */\n static hasCamera(): Promise<boolean> {\n return CameraManager.hasCamera();\n }\n\n /** List available cameras. Pass true to request labels (triggers permission prompt). */\n static listCameras(requestLabels?: boolean): Promise<Camera[]> {\n return CameraManager.listCameras(requestLabels);\n }\n\n /**\n * Pre-load the WASM binary so it's ready when the scanner starts.\n * Call this early (e.g., on app init) to avoid delay on first scan.\n */\n static async preload(): Promise<void> {\n // Trigger WASM loading by doing a minimal scan\n const pixel = new Uint8ClampedArray([255, 255, 255, 255]);\n const img = new ImageData(pixel, 1, 1);\n try {\n await scanImage(img);\n } catch {\n // Expected — no QR code in a 1x1 image. The point was to load WASM.\n }\n }\n\n /**\n * Configure WASM loading. Call before creating any scanner instance.\n * @example\n * QrScanner.configureWasm({ locateFile: (filename) => `/wasm/${filename}` });\n */\n static configureWasm(overrides: Partial<EmscriptenModule>): void {\n setZXingModuleOverrides(overrides);\n }\n\n /**\n * Set a custom URL for the worker script. Call before creating any scanner.\n * Needed for CJS consumers or non-standard bundler setups.\n * By default, the worker URL is resolved via `new URL('./worker.js', import.meta.url)`,\n * which works with Vite, webpack 5, Parcel, and other modern bundlers.\n * @example\n * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');\n */\n static setWorkerUrl(url: string | URL): void {\n setWorkerUrl(url);\n }\n\n /**\n * Enable or disable debug logging (performance timings, camera selection).\n * Off by default. Useful for diagnosing camera issues in the browser console.\n * @example\n * QrScanner.setDebug(true);\n */\n static setDebug(enabled: boolean): void {\n setDebug(enabled);\n }\n\n /** Scan a single image (not a video stream). */\n static scanImage(\n source:\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | Blob\n | ArrayBuffer\n | Uint8Array\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n ): Promise<ScanResult> {\n return scanImage(source, options);\n }\n}\n\nexport default QrScanner;\n"],"mappings":"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","names":["t","enabled"]}
|