Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 975x 975x 975x 975x 10x 10x 10x 10x 10x 9x 9x 10x 23x 9x 7x 7x 4x 4x 4x | /**
* The point in the pipeline a decode gave up at (SPEC §3.3). Reported so a
* caller — or a dev-mode overlay — can tell "no code in this frame" apart from
* "a code was found but is too damaged to read", which are completely
* different problems for the person holding the camera.
*/
export type QRDecodeStage =
/** No symbol was located in the image at all. */
| "detect"
/** A symbol was located but its format or version word is unreadable. */
| "format"
/** The codewords carry more errors than the error-correction level can absorb. */
| "ec"
/** The corrected data is not a valid QR bit stream. */
| "parse";
/**
* Thrown when an image cannot be decoded into a QR payload.
*
* A failed decode is a normal, expected outcome for any given camera frame, so
* the scanning loop treats this as "try the next frame" rather than an error
* condition. It becomes visible to a consumer through `decodeImage`/`scanFile`,
* where the caller asked about one specific image.
*
* @example
* ```ts
* try {
* await decodeFile(file);
* } catch (error) {
* if (error instanceof QRDecodeError && error.stage === "ec") {
* toast("Found a code, but it is too damaged to read — try a sharper photo.");
* }
* }
* ```
*/
export class QRDecodeError extends Error {
readonly stage: QRDecodeStage;
constructor(stage: QRDecodeStage, message: string, readonly cause?: unknown) {
super(message);
this.name = "QRDecodeError";
this.stage = stage;
}
}
/** Why a camera could not be opened, mapped from the underlying `DOMException`. */
export type QRCameraErrorReason =
/** The user denied permission, or a policy blocks camera access. */
| "denied"
/** No camera matches the requested constraints. */
| "not-found"
/** A camera exists but the OS or another application is holding it. */
| "in-use"
/** The constraints cannot be satisfied by any available camera. */
| "over-constrained"
/** `getUserMedia` is unavailable — an insecure origin, or no media devices. */
| "unsupported"
/** Anything the platform reported that does not map to the above. */
| "unknown";
/**
* Thrown when the camera cannot be started. The reason is normalized because
* every browser words its `DOMException` differently, and a UI needs to say
* "allow camera access" versus "close the app that is using your camera" —
* which are not the same message.
*/
export class QRCameraError extends Error {
readonly reason: QRCameraErrorReason;
constructor(reason: QRCameraErrorReason, message: string, readonly cause?: unknown) {
super(message);
this.name = "QRCameraError";
this.reason = reason;
}
/**
* Adopt a camera failure raised elsewhere.
*
* `@usefy/use-user-media` does the `DOMException` normalization — it owns the
* camera, and duplicating that mapping here would give two sources of truth
* for what "NotReadableError" means. What this package owes its consumers is
* *its own* error type: nobody should have to import a sibling package to
* narrow a `catch`. So the reason and message are carried across verbatim and
* the original is kept as `cause`.
*/
static from(error: unknown): QRCameraError {
if (error instanceof QRCameraError) return error;
const reason = readReason(error);
const message =
error instanceof Error && error.message ? error.message : "The camera could not be started.";
return new QRCameraError(reason, message, error);
}
}
const REASONS: ReadonlySet<string> = new Set<QRCameraErrorReason>([
"denied",
"not-found",
"in-use",
"over-constrained",
"unsupported",
"unknown",
]);
/** A `UserMediaError`'s `reason`, when the error carries one this package knows. */
function readReason(error: unknown): QRCameraErrorReason {
if (typeof error !== "object" || error === null || !("reason" in error)) return "unknown";
const reason = String((error as { reason: unknown }).reason);
return REASONS.has(reason) ? (reason as QRCameraErrorReason) : "unknown";
}
/**
* Thrown when a capability the caller explicitly asked for does not exist in
* this environment — `engine: "native"` without `BarcodeDetector`, or a decode
* attempted on the server.
*
* Only ever thrown for an *explicit* request: `engine: "auto"` silently uses
* whatever is available, which is the whole point of the default.
*/
export class QRUnsupportedError extends Error {
constructor(message: string, readonly cause?: unknown) {
super(message);
this.name = "QRUnsupportedError";
}
}
|