@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 CHANGED
@@ -94,6 +94,16 @@ QrScanner.scanImage(source, options?): Promise<ScanResult>
94
94
  QrScanner.preload(): Promise<void>
95
95
  QrScanner.configureWasm(overrides): void
96
96
  QrScanner.setWorkerUrl(url): void
97
+ QrScanner.setDebug(enabled): void
98
+ ```
99
+
100
+ ## Debug Logging
101
+
102
+ The library includes performance profiling logs (camera acquisition timing, best-camera selection, etc.) that are **off by default**. Enable them at runtime for debugging:
103
+
104
+ ```ts
105
+ QrScanner.setDebug(true); // logs appear in console
106
+ QrScanner.setDebug(false); // back to silent (default)
97
107
  ```
98
108
 
99
109
  ## Worker Loading
package/dist/index.cjs CHANGED
@@ -62,6 +62,12 @@ function setCachedDeviceId(facingMode, deviceId) {
62
62
  } catch {
63
63
  }
64
64
  }
65
+ function clearCachedDeviceId(facingMode) {
66
+ try {
67
+ localStorage.removeItem(`${CACHE_KEY_PREFIX}${facingMode}`);
68
+ } catch {
69
+ }
70
+ }
65
71
  var CameraManager = class {
66
72
  constructor(config = {}) {
67
73
  this.stream = null;
@@ -175,14 +181,20 @@ var CameraManager = class {
175
181
  debug(
176
182
  `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`
177
183
  );
178
- } catch {
184
+ } catch (err) {
185
+ debug(
186
+ `[QrScanner] ensureBestCamera: skipped (getCapabilities failed: ${err instanceof Error ? err.message : err})`
187
+ );
179
188
  return;
180
189
  }
181
190
  const currentDeviceId = track.getSettings().deviceId;
182
191
  let devices;
183
192
  try {
184
193
  devices = await navigator.mediaDevices.enumerateDevices();
185
- } catch {
194
+ } catch (err) {
195
+ debug(
196
+ `[QrScanner] ensureBestCamera: skipped (enumerateDevices failed: ${err instanceof Error ? err.message : err})`
197
+ );
186
198
  return;
187
199
  }
188
200
  if (!Array.isArray(devices)) return;
@@ -209,10 +221,13 @@ var CameraManager = class {
209
221
  debug(
210
222
  `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`
211
223
  );
212
- } catch {
224
+ } catch (err) {
213
225
  debug(
214
- `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`
226
+ `[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}`
215
227
  );
228
+ if (err instanceof DOMException && err.name === "NotAllowedError") {
229
+ throw new CameraPermissionError();
230
+ }
216
231
  continue;
217
232
  }
218
233
  const candidateTrack = candidateStream.getVideoTracks()[0];
@@ -231,27 +246,52 @@ var CameraManager = class {
231
246
  this.stream = candidateStream;
232
247
  return;
233
248
  }
234
- } catch {
249
+ } catch (err) {
250
+ debug(
251
+ `[QrScanner] ensureBestCamera: candidate getCapabilities failed \u2014 ${err instanceof Error ? err.message : err}`
252
+ );
235
253
  }
236
254
  for (const t2 of candidateStream.getTracks()) t2.stop();
237
255
  }
238
- try {
239
- this.stream = await navigator.mediaDevices.getUserMedia({
256
+ const recoveryAttempts = [
257
+ // Original deviceId + resolution
258
+ {
240
259
  video: {
241
260
  deviceId: currentDeviceId ? { exact: currentDeviceId } : void 0,
242
261
  width: this.resolution?.width ?? { ideal: 1920 },
243
262
  height: this.resolution?.height ?? { ideal: 1080 }
244
263
  },
245
264
  audio: false
246
- });
247
- } catch {
265
+ },
266
+ // facingMode + resolution
267
+ this.buildConstraints(),
268
+ // facingMode only
269
+ this.buildConstraints(false),
270
+ // Bare minimum — should always succeed if a camera exists
271
+ { video: true, audio: false }
272
+ ];
273
+ let lastRecoveryError;
274
+ for (const constraints of recoveryAttempts) {
248
275
  try {
249
- this.stream = await navigator.mediaDevices.getUserMedia(
250
- this.buildConstraints()
276
+ this.stream = await navigator.mediaDevices.getUserMedia(constraints);
277
+ return;
278
+ } catch (err) {
279
+ if (err instanceof DOMException && err.name === "NotAllowedError") {
280
+ throw new CameraPermissionError();
281
+ }
282
+ if (err instanceof DOMException && err.name === "NotFoundError") {
283
+ throw new CameraNotFoundError();
284
+ }
285
+ lastRecoveryError = err;
286
+ debug(
287
+ `[QrScanner] ensureBestCamera recovery failed: ${err instanceof Error ? err.name : err}`
251
288
  );
252
- } catch {
289
+ continue;
253
290
  }
254
291
  }
292
+ debug(
293
+ `[QrScanner] ensureBestCamera: all recovery attempts failed, last error: ${lastRecoveryError instanceof Error ? lastRecoveryError.message : lastRecoveryError}`
294
+ );
255
295
  }
256
296
  getVideoTrack() {
257
297
  if (!this.stream) return null;
@@ -266,12 +306,10 @@ var CameraManager = class {
266
306
  * fewer constraints lets us still open the camera on those browsers.
267
307
  */
268
308
  async acquireStream() {
269
- const labels = [];
270
309
  const attempts = [];
271
310
  if (this.facingMode === "environment" || this.facingMode === "user") {
272
311
  const cachedId = getCachedDeviceId(this.facingMode);
273
312
  if (cachedId) {
274
- labels.push("cached deviceId");
275
313
  const video = {
276
314
  deviceId: { exact: cachedId }
277
315
  };
@@ -279,42 +317,50 @@ var CameraManager = class {
279
317
  else video.width = { ideal: 1920 };
280
318
  if (this.resolution?.height) video.height = this.resolution.height;
281
319
  else video.height = { ideal: 1080 };
282
- attempts.push({ video, audio: false });
320
+ attempts.push({
321
+ label: "cached deviceId",
322
+ constraints: { video, audio: false },
323
+ isCachedDeviceId: true
324
+ });
283
325
  }
284
326
  }
285
- labels.push("full constraints", "no resolution", "bare minimum");
286
327
  attempts.push(
287
- // facingMode/deviceId + resolution
288
- this.buildConstraints(),
289
- // facingMode/deviceId only, no resolution
290
- this.buildConstraints(false),
291
- // Bare minimum
292
- { video: true, audio: false }
328
+ { label: "full constraints", constraints: this.buildConstraints() },
329
+ { label: "no resolution", constraints: this.buildConstraints(false) },
330
+ { label: "bare minimum", constraints: { video: true, audio: false } }
293
331
  );
294
332
  let lastError;
295
- for (let i = 0; i < attempts.length; i++) {
333
+ for (const attempt of attempts) {
296
334
  const t = performance.now();
297
335
  try {
298
- const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);
336
+ const stream = await navigator.mediaDevices.getUserMedia(
337
+ attempt.constraints
338
+ );
299
339
  debug(
300
- `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms \u2713`
340
+ `[QrScanner] getUserMedia(${attempt.label}): ${(performance.now() - t).toFixed(0)}ms \u2713`
301
341
  );
302
342
  return stream;
303
343
  } catch (err) {
304
344
  debug(
305
- `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms \u2717 ${err instanceof DOMException ? err.name : err}`
345
+ `[QrScanner] getUserMedia(${attempt.label}): ${(performance.now() - t).toFixed(0)}ms \u2717 ${err instanceof Error ? err.name : err}`
306
346
  );
307
- if (err instanceof DOMException) {
308
- if (err.name === "NotAllowedError") {
309
- throw new CameraPermissionError();
310
- }
311
- if (err.name === "NotFoundError") {
312
- throw new CameraNotFoundError();
313
- }
314
- lastError = err;
315
- continue;
347
+ if (err instanceof DOMException && err.name === "NotAllowedError") {
348
+ throw new CameraPermissionError();
349
+ }
350
+ if (err instanceof DOMException && err.name === "NotFoundError") {
351
+ throw new CameraNotFoundError();
316
352
  }
317
- throw err;
353
+ if (!(err instanceof DOMException) && !(err instanceof OverconstrainedError)) {
354
+ throw err;
355
+ }
356
+ if (attempt.isCachedDeviceId) {
357
+ clearCachedDeviceId(this.facingMode);
358
+ debug(
359
+ `[QrScanner] cleared stale cached deviceId for "${this.facingMode}"`
360
+ );
361
+ }
362
+ lastError = err;
363
+ continue;
318
364
  }
319
365
  }
320
366
  throw lastError;
@@ -424,9 +470,6 @@ var FrameExtractor = class {
424
470
  markWorkerIdle() {
425
471
  this.workerBusy = false;
426
472
  }
427
- markWorkerBusy() {
428
- this.workerBusy = true;
429
- }
430
473
  };
431
474
 
432
475
  // src/overlay.ts
@@ -436,22 +479,9 @@ function getRenderedVideoRect(video) {
436
479
  const videoWidth = video.videoWidth || 1;
437
480
  const videoHeight = video.videoHeight || 1;
438
481
  const objectFit = getComputedStyle(video).objectFit;
439
- if (objectFit === "cover") {
440
- const scale = Math.max(
441
- elementWidth / videoWidth,
442
- elementHeight / videoHeight
443
- );
444
- const renderedWidth = videoWidth * scale;
445
- const renderedHeight = videoHeight * scale;
446
- return {
447
- offsetX: (elementWidth - renderedWidth) / 2,
448
- offsetY: (elementHeight - renderedHeight) / 2,
449
- width: renderedWidth,
450
- height: renderedHeight
451
- };
452
- }
453
- if (objectFit === "contain") {
454
- const scale = Math.min(
482
+ if (objectFit === "cover" || objectFit === "contain") {
483
+ const scaleFn = objectFit === "cover" ? Math.max : Math.min;
484
+ const scale = scaleFn(
455
485
  elementWidth / videoWidth,
456
486
  elementHeight / videoHeight
457
487
  );
@@ -773,18 +803,9 @@ var Scanner = class {
773
803
  }
774
804
  setInversionMode(mode) {
775
805
  if (!this.worker) return;
776
- const options = {};
777
- switch (mode) {
778
- case "original":
779
- options.tryInvert = false;
780
- break;
781
- case "invert":
782
- options.tryInvert = true;
783
- break;
784
- case "both":
785
- options.tryInvert = true;
786
- break;
787
- }
806
+ const options = {
807
+ tryInvert: mode !== "original"
808
+ };
788
809
  const msg = { type: "configure", options };
789
810
  this.worker.postMessage(msg);
790
811
  }
@@ -917,8 +938,8 @@ async function createImageBitmapFromBlob(blob) {
917
938
  }
918
939
  }
919
940
 
920
- // src/scan-image.ts
921
- var defaultReaderOptions = {
941
+ // src/decoder-utils.ts
942
+ var DEFAULT_READER_OPTIONS = {
922
943
  formats: ["QRCode"],
923
944
  tryHarder: true,
924
945
  tryInvert: true,
@@ -935,12 +956,14 @@ function mapPosition(position) {
935
956
  position.bottomLeft
936
957
  ];
937
958
  }
959
+
960
+ // src/scan-image.ts
938
961
  function isDirectInput(source) {
939
962
  return source instanceof Blob || source instanceof ArrayBuffer || source instanceof Uint8Array || typeof ImageData !== "undefined" && source instanceof ImageData;
940
963
  }
941
964
  async function scanImage(source, options) {
942
965
  const readerOptions = {
943
- ...defaultReaderOptions,
966
+ ...DEFAULT_READER_OPTIONS,
944
967
  ...options?.decoderOptions,
945
968
  formats: ["QRCode"]
946
969
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../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"],"sourcesContent":["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","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"],"mappings":"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","names":["t","import_reader","enabled"]}
1
+ {"version":3,"sources":["../src/index.ts","../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"],"sourcesContent":["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","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"],"mappings":"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","names":["t","import_reader","enabled"]}