attestid_js_sdk 0.1.11 → 0.1.13
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 +22 -0
- package/dist/core/index.cjs +166 -31
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +50 -1
- package/dist/core/index.d.ts +50 -1
- package/dist/core/index.js +165 -31
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +62 -62
- package/dist/react/index.d.cts +31 -0
- package/dist/react/index.d.ts +31 -0
- package/dist/react/index.js +62 -62
- package/dist/styles/attestid.css +31 -0
- package/package.json +1 -1
package/dist/core/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/index.ts","../../src/core/errors.ts","../../src/core/encoding.ts","../../src/core/http.ts","../../src/core/photoQuality.ts","../../src/core/api.ts","../../src/core/deviceKey.ts","../../src/core/utils.ts","../../src/core/client.ts","../../src/core/liveness.ts","../../src/core/camera.ts","../../src/core/verify.ts","../../src/core/device.ts","../../src/core/qrcodegen.ts","../../src/core/qrcode.ts","../../src/core/handoff.ts"],"sourcesContent":["export { AttestID } from './client';\nexport type { WaitForLivenessOptions } from './client';\nexport type { SubmitLivenessVideoOptions } from './api';\nexport {\n recordLiveness,\n openFrontCamera,\n livenessCaptureSupported,\n pickRecorderMimeType,\n scriptDurationMs,\n segmentAt,\n promptFor,\n LIVENESS_PROMPTS,\n FLASH_CSS,\n RECORDING_TAIL_MS,\n checkCaptureConditions,\n classifyConditions,\n meanLuma,\n CAPTURE_ISSUE_PROMPTS,\n MIN_GOOD_FPS,\n MIN_GOOD_BRIGHTNESS,\n MAX_GOOD_BRIGHTNESS,\n} from './liveness';\nexport type { LivenessRecording, RecordLivenessOptions, CaptureConditions, CaptureIssue } from './liveness';\nexport { openCameraSession, DOC_ASPECT } from './camera';\nexport { rawSessionId, faceMatchOutcome, canRetryLiveness, RETRYABLE_LIVENESS_REASONS } from './utils';\nexport { AttestIDError, AuthError, KYCError, CameraError, ValidationError } from './errors';\nexport { verifyAttestation, verifyWebhook, SignatureError } from './verify';\nexport type { VerifyAttestationOptions, VerifyWebhookOptions } from './verify';\nexport { canonicalJson } from './encoding';\nexport { checkPhotoQuality } from './photoQuality';\nexport { createSdkSession } from './api';\nexport { isMobileDevice } from './device';\nexport { getQrMatrix } from './qrcode';\nexport type { QrErrorCorrection } from './qrcode';\nexport {\n generateHandoffToken,\n buildHandoffUrl,\n readHandoffParams,\n currentUrlWithoutHandoffParams,\n clearHandoffParamsFromAddressBar,\n HANDOFF_PARAM_KEYS,\n} from './handoff';\nexport type { HandoffParams } from './handoff';\nexport type {\n AttestIDConfig,\n AttestIDConfigServer,\n AttestIDConfigBrowser,\n IdType,\n StartKYCOptions,\n StartKYCResponse,\n LivenessCredentials,\n LivenessProvider,\n LivenessAction,\n LivenessSegment,\n LivenessChallenge,\n LivenessVideoResult,\n DocumentInfo,\n ChecksInfo,\n KYCResult,\n SdkSessionResponse,\n AttestationClaims,\n CaptureAssurance,\n} from './types';\nexport type { PhotoQualityResult } from './photoQuality';\n","export class AttestIDError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n ) {\n super(message);\n this.name = 'AttestIDError';\n }\n}\n\nexport class AuthError extends AttestIDError {\n constructor(message: string) {\n super(message, 'AUTH_ERROR', 401);\n this.name = 'AuthError';\n }\n}\n\nexport class KYCError extends AttestIDError {\n constructor(message: string, status?: number) {\n super(message, 'KYC_ERROR', status);\n this.name = 'KYCError';\n }\n}\n\nexport class CameraError extends AttestIDError {\n constructor(message: string) {\n super(message, 'CAMERA_ERROR');\n this.name = 'CameraError';\n }\n}\n\nexport class ValidationError extends AttestIDError {\n constructor(message: string) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n","/**\n * Small encoding/crypto helpers shared by device signing, attestation and\n * webhook verification. WebCrypto only (browsers, Node ≥ 19, Deno, edge\n * runtimes), with no Node-specific imports, so the package stays isomorphic.\n */\nimport { AttestIDError } from './errors';\n\nexport function subtle(): SubtleCrypto {\n const s = (globalThis as { crypto?: Crypto }).crypto?.subtle;\n if (!s) {\n throw new AttestIDError(\n 'WebCrypto (globalThis.crypto.subtle) is not available in this runtime. ' +\n 'Use a modern browser, Node.js 19+, or a runtime that provides WebCrypto.',\n 'CRYPTO_UNAVAILABLE',\n );\n }\n return s;\n}\n\nexport function hasWebCrypto(): boolean {\n return !!(globalThis as { crypto?: Crypto }).crypto?.subtle;\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let bin = '';\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n return btoa(bin);\n}\n\nexport function bytesToBase64Url(bytes: Uint8Array): string {\n return bytesToBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function base64UrlToBytes(s: string): Uint8Array {\n const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4);\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function hexToBytes(hex: string): Uint8Array | null {\n if (hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) return null;\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nexport const utf8 = (s: string): Uint8Array => new TextEncoder().encode(s);\n\n/**\n * Exactly this view's bytes as a standalone ArrayBuffer, for WebCrypto inputs.\n * TS 5.7+ types a Uint8Array's buffer as ArrayBufferLike, which BufferSource\n * rejects. `slice()` copies into a fresh ArrayBuffer and types the same on\n * every TS version, including the older ones that consumers compiling this\n * source (e.g. attestid-demo on TS 5.5) use.\n */\nexport function buf(bytes: Uint8Array): ArrayBuffer {\n return bytes.slice().buffer as ArrayBuffer;\n}\n\nexport async function sha256(data: Uint8Array): Promise<Uint8Array> {\n return new Uint8Array(await subtle().digest('SHA-256', buf(data)));\n}\n\n/**\n * Deterministic JSON matching the API's `canonical_json` (Python\n * `json.dumps(sort_keys=True, separators=(\",\", \":\"), ensure_ascii=False)`):\n * object keys sorted recursively, no whitespace. Used to recompute an\n * attestation's `document_sha256` from the document block you received.\n */\nexport function canonicalJson(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null';\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).filter(k => obj[k] !== undefined).sort();\n return `{${keys.map(k => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(',')}}`;\n}\n\n/** RFC 4122 v4 id: uses crypto.randomUUID when present, otherwise getRandomValues. */\nexport function randomId(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n const b = new Uint8Array(16);\n if (c?.getRandomValues) c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6] & 0x0f) | 0x40;\n b[8] = (b[8] & 0x3f) | 0x80;\n const h = bytesToHex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n","/**\n * POST with an `Idempotency-Key` and safe automatic retries.\n *\n * The API (SECURITY.md P3) stores the response to every keyed POST /v1/kyc/*\n * for 24 h. Retrying with the same key and the same body replays that\n * response instead of creating a second session, upload or billed completion.\n * That makes it safe to retry on a flaky mobile connection.\n *\n * Retry rules:\n * - network error (outcome unknown) → same key, same body. If the first\n * attempt actually landed, the server replays its response.\n * - 409 idempotency_request_in_progress → wait (Retry-After), same key + body.\n * - 5xx / 429 → the server did NOT store anything, so the request is rebuilt\n * with a fresh key. That lets a one-time device-signing nonce be re-issued\n * instead of replayed (a replayed nonce would flag the capture as invalid).\n * - any other status → returned to the caller as-is.\n */\nimport { randomId } from './encoding';\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Base backoff in ms (doubled each retry). Default 500. */\n baseDelayMs?: number;\n}\n\nexport interface BuiltRequest {\n body: BodyInit;\n headers?: Record<string, string>;\n}\n\nconst RETRY_FRESH_STATUSES = new Set([429, 500, 502, 503, 504]);\nconst MAX_RETRY_AFTER_MS = 10_000;\n\nexport const defaultSleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));\nlet sleep = defaultSleep;\n/** @internal test hook */\nexport function _setSleep(fn: (ms: number) => Promise<void>): void {\n sleep = fn;\n}\n\nfunction retryAfterMs(res: Response, fallback: number): number {\n const raw = res.headers?.get?.('retry-after');\n const secs = raw ? Number(raw) : NaN;\n return Number.isFinite(secs) && secs >= 0 ? Math.min(secs * 1000, MAX_RETRY_AFTER_MS) : fallback;\n}\n\nexport async function postIdempotent(\n fetchImpl: FetchLike,\n url: string,\n build: () => Promise<BuiltRequest> | BuiltRequest,\n options: RetryOptions = {},\n): Promise<Response> {\n const attempts = Math.max(1, options.attempts ?? 3);\n const base = options.baseDelayMs ?? 500;\n\n let key = randomId();\n let req = await build();\n\n for (let attempt = 1; ; attempt++) {\n const last = attempt >= attempts;\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: 'POST',\n headers: { ...req.headers, 'Idempotency-Key': key },\n body: req.body,\n });\n } catch (err) {\n if (last) throw err;\n await sleep(base * 2 ** (attempt - 1));\n continue; // outcome unknown → same key + body\n }\n\n if (last || res.ok) return res;\n if (res.status === 409) {\n await sleep(retryAfterMs(res, base));\n continue; // still running server-side → same key + body\n }\n if (RETRY_FRESH_STATUSES.has(res.status)) {\n await sleep(retryAfterMs(res, base * 2 ** (attempt - 1)));\n key = randomId();\n req = await build();\n continue;\n }\n return res;\n }\n}\n","export interface PhotoQualityResult {\n ok: boolean;\n issues: string[];\n}\n\n// Captures come from a live getUserMedia() video frame (camera.ts), not a\n// native camera-app still — video frames are inherently softer (sensor\n// binning, continuous-autofocus hunting, slight handheld motion) even when\n// perfectly legible, so this must stay well below the value that would make\n// sense for a dedicated photo. 60 flagged plenty of genuinely fine captures.\nconst BLUR_THRESHOLD = 40;\nconst DARK_THRESHOLD = 35;\nconst BRIGHT_THRESHOLD = 225;\nconst MIN_FILE_BYTES = 30_000;\n\n// ISO 7810 ID-1 (national ID card / driver's licence) aspect ratio, ± tolerance\n// for perspective distortion in a handheld photo.\nconst ID1_ASPECT = 85.6 / 54;\nconst ID1_ASPECT_TOLERANCE = 0.15;\n\n// ── Face presence (best-effort, progressive enhancement) ────────────────────\n//\n// Scope: of every `failure_reason` the API can return, only the ones a client\n// can plausibly judge from the raw image *before* the backend's OCR/liveness/\n// fraud pipeline ever runs belong here — see docs/kyc-pipeline.md §9 in the\n// attestid_api repo for the authoritative Failed/Rejected/Fraudulent split.\n// That rules out anything requiring real OCR (document_not_recognized,\n// document_low_confidence, ocr_failed, id_number_unclear, name_unclear,\n// date_of_birth_unclear, expiry_date_unclear) or a facial-attribute model\n// (face_occluded, eyes_not_open, sunglasses_detected) — none of those are\n// worth bundling an ML/OCR library into every consumer's app for; the backend\n// classifies all of those Rejected precisely because the SDK can't check them.\n// Two exceptions don't need OCR to catch. document_is_passport /\n// document_is_not_passport is an aspect-ratio mismatch, which\n// checkDocumentIdentity() below flags client-side too (subtler cases still\n// fall through to the backend's dual-reader check). And a photo with no MRZ\n// at all (for a passport) or no printed fields at all (for a card), the\n// cases behind most mrz_failed / ocr_failed / document_not_recognized\n// results, is recognisable from text *layout* alone without reading a\n// character; see analyseTextLayout().\n//\n// face_detection_failed (0 faces) and multiple_people_detected (>1 faces) are\n// both Failed instead — a plain face *count* is exactly what the\n// browser-native Shape Detection API gives us for free, so both are flagged\n// here rather than wasting an API round trip on a mistake the client could\n// already see.\ninterface ShapeDetectionFace {\n boundingBox: { width: number; height: number };\n}\ninterface ShapeDetectionFaceDetector {\n detect(image: CanvasImageSource): Promise<ShapeDetectionFace[]>;\n}\ninterface ShapeDetectionFaceDetectorCtor {\n new (options?: { maxDetectedFaces?: number; fastMode?: boolean }): ShapeDetectionFaceDetector;\n}\n\n/**\n * Best-effort face detection via the browser-native Shape Detection API\n * (Chromium only as of writing). Returns `null` — never `[]` — when the API\n * is unsupported or the detector itself throws, so callers can tell \"could\n * not check\" apart from \"no face found\" and avoid blocking a legitimate\n * submission just because the browser lacks the API.\n *\n * maxDetectedFaces is set above 2 (rather than the minimum needed for a\n * plain presence check) because many national ID cards carry a \"ghost\n * image\" security watermark — a small duplicate of the primary photo\n * printed elsewhere on the card — which the detector happily reports as an\n * extra face. Filtering that out (see significantFaceCount() below) needs\n * to actually see it rather than have it silently crowd out real faces\n * against a cap of 2.\n */\nexport async function detectFaces(source: CanvasImageSource): Promise<ShapeDetectionFace[] | null> {\n const FaceDetectorCtor = (globalThis as { FaceDetector?: ShapeDetectionFaceDetectorCtor }).FaceDetector;\n if (!FaceDetectorCtor) return null;\n try {\n return await new FaceDetectorCtor({ maxDetectedFaces: 5, fastMode: true }).detect(source);\n } catch {\n return null;\n }\n}\n\n/**\n * Best-effort face count via the browser-native Shape Detection API. Thin\n * wrapper over detectFaces() that returns the raw, unfiltered count —\n * callers that care about ghost-image false positives should use\n * detectFaces() + significantFaceCount() instead.\n */\nexport async function detectFaceCount(source: CanvasImageSource): Promise<number | null> {\n const faces = await detectFaces(source);\n return faces === null ? null : faces.length;\n}\n\n/**\n * Counts faces that are large enough to plausibly be a real person in\n * frame, discarding faces that are much smaller than the largest one\n * detected. A ghost-image watermark is always a fraction of the size of the\n * primary ID photo, so this tells a second real person apart from that\n * watermark without needing OCR or a purpose-built model. Two people\n * actually in frame are typically at comparable distance from the camera\n * and so stay above the ratio; this is a heuristic, not a guarantee.\n */\nconst GHOST_IMAGE_AREA_RATIO = 0.4;\n\nexport function significantFaceCount(faces: ShapeDetectionFace[]): number {\n if (faces.length <= 1) return faces.length;\n const areas = faces.map(f => f.boundingBox.width * f.boundingBox.height);\n const maxArea = Math.max(...areas);\n return areas.filter(a => a >= maxArea * GHOST_IMAGE_AREA_RATIO).length;\n}\n\n// Must match the backend's magic-byte gate (api/v1/kyc.py) exactly — JPEG and\n// PNG are the only formats the API accepts, checked by signature rather than\n// file extension or the (spoofable) Content-Type header, both of which an\n// attacker fully controls (e.g. naming a script \"photo.jpg\" or setting\n// Content-Type: image/jpeg on arbitrary bytes).\nconst JPEG_MAGIC = [0xff, 0xd8, 0xff];\nconst PNG_MAGIC = [0x89, 0x50, 0x4e, 0x47];\n\n/**\n * True only for bytes that open with a genuine JPEG or PNG signature — the\n * sole two formats the API accepts. Used both by checkPhotoQuality() (the\n * advisory pre-upload UX check) and directly by startKYC() (core/api.ts) as\n * a hard gate before any bytes leave the browser, so a caller that bypasses\n * checkPhotoQuality — or a non-React consumer using the core client\n * directly — still can't hand the upload endpoint an arbitrary file.\n */\nexport async function hasSupportedMagicBytes(photo: Blob): Promise<boolean> {\n const header = new Uint8Array(await photo.slice(0, 4).arrayBuffer());\n const matches = (magic: number[]) => magic.every((byte, i) => header[i] === byte);\n return matches(JPEG_MAGIC) || matches(PNG_MAGIC);\n}\n\n/**\n * Counts rows within [x0, x0+aw) whose brightness variance falls in the\n * \"printed text\" band — high enough contrast to be ink/MRZ glyphs on a\n * background, but not so extreme it's a hard edge or sensor noise. Shared by\n * the passport data-page detector and the cross-type document/unknown-image\n * check below.\n */\nfunction countTextRows(grey: Float32Array, w: number, h: number, x0: number, aw: number): number {\n let textRows = 0;\n for (let y = 0; y < h; y++) {\n let sum = 0, sumSq = 0;\n for (let x = x0; x < x0 + aw; x++) {\n const v = grey[y * w + x];\n sum += v;\n sumSq += v * v;\n }\n const mean = sum / aw;\n const variance = sumSq / aw - mean * mean;\n if (variance > 150 && mean > 40 && mean < 215) textRows++;\n }\n return textRows;\n}\n\n// ── MRZ / printed-text structure (no OCR) ──────────────────────────────────\n//\n// The backend reads passports from the MRZ (failing with mrz_failed) and ID\n// cards / licences from their printed fields (ocr_failed /\n// document_not_recognized). Both fail late, after a full upload and round\n// trip, when the user photographed something with no MRZ or no readable\n// fields at all: the passport cover, a visa page, the back of a card, a\n// card held too far away. The checks below catch those cases from text\n// *structure* alone, without reading a single character:\n//\n// - An MRZ is two (TD3/TD2) or three (TD1) lines of fixed-pitch OCR-B glyphs\n// that run the full width of the page with no spaces (empty positions are\n// '<' fillers). Under a horizontal-edge map that reads as two or more\n// dense, unbroken, evenly spaced bands of equal height spanning the page.\n// Nothing else on a passport looks like that.\n// - A card's front carries several separate printed lines (header, surname,\n// names, date of birth, ID number). A card back, a blank surface or a photo\n// of something else doesn't.\n//\n// The image is split into vertical strips and lines are found per strip, so\n// the ID photo (a tall block of edges) can't merge the text lines beside it\n// into one blob. A small skew search (±4°) straightens a slightly tilted\n// capture first, otherwise two MRZ lines drift into each other across the\n// page width.\n//\n// Like everything in this file this is a pre-filter, not OCR: a present but\n// unreadable MRZ (glare across one character, say) still goes to the backend.\n\n/** Minimum brightness step between neighbouring pixels counted as a glyph edge. */\nconst EDGE_DELTA = 28;\n/** medianStep() at or above this means a grainy frame that gets smoothed first. */\nconst NOISY_MEDIAN_STEP = 9;\n/** tan(-4°) … tan(4°) in 1° steps; 0 first so ties keep the image unrotated. */\nconst SKEW_TANS = [0, 0.0175, -0.0175, 0.0349, -0.0349, 0.0524, -0.0524, 0.0699, -0.0699];\nconst STRIPS = 8;\n/** An MRZ line must run unbroken through at least this many strips (75% of the width). */\nconst MRZ_MIN_STRIPS = Math.ceil(STRIPS * 0.75);\nconst CELL = 8;\n/** An ID card front has at least this many separate printed lines… */\nconst MIN_ID_TEXT_LINES = 4;\n/** …at least this many of them left-aligned in one column. */\nconst MIN_ID_ALIGNED_LINES = 3;\nconst TEXT_MAX_BLEED = 0.3;\nconst MRZ_MAX_BLEED = 0.25;\n/** Minimum fixed-pitch score for an MRZ line (see periodicity()). */\nconst MRZ_MIN_PERIODICITY = 0.15;\n\nexport const MRZ_NOT_FOUND_MESSAGE =\n 'We could not find the machine-readable zone (MRZ): the two lines of letters, numbers and <<< ' +\n 'at the bottom of the photo page. Photograph the page with your photo on it (not the cover or a ' +\n 'visa page), keep the passport straight, and make sure both MRZ lines are fully inside the frame, ' +\n 'sharp and free of glare.';\n\nexport function idTextNotFoundMessage(idType: string): string {\n const doc = idType === 'drivers_licence' ? \"driver's licence\" : 'ID card';\n return (\n `We could not detect readable printed details on your ${doc}. Photograph the front of the ` +\n 'card (the side with your photo, name and ID number) so it fills the frame, in good light ' +\n 'and without glare.'\n );\n}\n\nexport interface TextBand {\n top: number;\n bottom: number;\n center: number;\n height: number;\n strip: number;\n /** Index of the first cell with glyph edges, within the strip. */\n firstCell: number;\n /** Fraction of the strip width between the band's first and last glyph cell. */\n spanFrac: number;\n /** Fraction of cells inside that span that contain glyph edges. */\n coverage: number;\n /** Glyph edges per pixel inside the band's span. */\n density: number;\n /** Edge level just above and below the band relative to inside it. Low = clean gap, like printed lines. */\n bleed: number;\n}\n\nexport interface TextLayout {\n /** Distinct printed lines found anywhere in the region. */\n textLines: number;\n /** Most printed lines sharing one left edge (a column of fields). */\n alignedLines: number;\n /** True when an MRZ-shaped block (2-3 dense, fixed-pitch, full-width lines) is present. */\n hasMrz: boolean;\n}\n\nexport interface MrzLineCandidate {\n top: number;\n bottom: number;\n center: number;\n height: number;\n /** Peak autocorrelation of the column edge profile, i.e. how fixed-pitch the glyphs are. */\n periodicity: number;\n /** Mean bleed of the member bands (see TextBand.bleed). */\n bleed: number;\n}\n\nexport interface TextMeasurements {\n bands: TextBand[];\n /** Full-width dense lines (see MRZ_MIN_STRIPS), top to bottom. */\n fullWidthLines: MrzLineCandidate[];\n}\n\n/**\n * Finds band-shaped clusters of glyph edges in the region [x0, x0+aw) of a\n * greyscale image, per vertical strip, after straightening any small tilt.\n * analyseTextLayout() turns these into a verdict. Exported for tests.\n */\nexport function measureTextBands(\n grey: Float32Array,\n w: number,\n h: number,\n x0 = 0,\n aw = w,\n): TextMeasurements {\n // 1. Horizontal edge map, kept sparse since most pixels aren't edges. A\n // grainy low-light frame is box-smoothed (3×3) first, otherwise the\n // grain fills the gaps between printed lines. Clean frames skip it:\n // smoothing would soften the small MRZ glyphs of a two-page spread.\n const src = medianStep(grey, w, h, x0, aw) >= NOISY_MEDIAN_STEP ? boxSmooth(grey, w, h, x0, aw) : null;\n const at = src ? (y: number, x: number) => src[y * aw + x] : (y: number, x: number) => grey[y * w + x0 + x];\n const ex: number[] = [];\n const ey: number[] = [];\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < aw - 1; x++) {\n if (Math.abs(at(y, x + 1) - at(y, x)) > EDGE_DELTA) {\n ex.push(x);\n ey.push(y);\n }\n }\n }\n if (ex.length === 0) return { bands: [], fullWidthLines: [] };\n\n // 2. Skew: pick the shear that makes the row profile sharpest. Text lines\n // give tall narrow peaks when level and smear out when tilted.\n let bestTan = 0, bestScore = -1;\n for (const t of SKEW_TANS) {\n const shift = Math.ceil(Math.abs(t) * aw);\n const counts = new Int32Array(h + shift + 1);\n for (let i = 0; i < ex.length; i++) {\n counts[ey[i] + Math.round(ex[i] * t) + (t < 0 ? shift : 0)]++;\n }\n let score = 0;\n for (let r = 0; r < counts.length; r++) score += counts[r] * counts[r];\n if (score > bestScore) { bestScore = score; bestTan = t; }\n }\n\n // 3. Bucket the straightened edges into rows × strips × cells.\n const shift = Math.ceil(Math.abs(bestTan) * aw);\n const rows = h + shift + 1;\n const er = new Int32Array(ex.length);\n const stripW = Math.max(1, Math.floor(aw / STRIPS));\n const cps = Math.ceil(stripW / CELL);\n const cells = new Uint16Array(rows * STRIPS * cps);\n for (let i = 0; i < ex.length; i++) {\n const r = ey[i] + Math.round(ex[i] * bestTan) + (bestTan < 0 ? shift : 0);\n er[i] = r;\n const strip = Math.min(STRIPS - 1, Math.floor(ex[i] / stripW));\n const cell = Math.min(cps - 1, Math.floor((ex[i] - strip * stripW) / CELL));\n cells[(r * STRIPS + strip) * cps + cell]++;\n }\n\n // 4. Bands of consecutive \"texty\" rows per strip, split at internal\n // valleys so two tightly spaced lines aren't read as one.\n const minH = Math.max(3, Math.round(h * 0.012));\n const maxH = Math.max(minH + 2, Math.round(h * 0.12));\n const rowMin = Math.max(4, Math.round(stripW * 0.03));\n const bands: TextBand[] = [];\n\n for (let s = 0; s < STRIPS; s++) {\n const rowCount = new Int32Array(rows);\n for (let r = 0; r < rows; r++) {\n let n = 0;\n const base = (r * STRIPS + s) * cps;\n for (let c = 0; c < cps; c++) n += cells[base + c];\n rowCount[r] = n;\n }\n\n const runs: Array<[number, number]> = [];\n let start = -1, gap = 0;\n for (let r = 0; r <= rows; r++) {\n const texty = r < rows && rowCount[r] >= rowMin;\n if (texty) {\n if (start < 0) start = r;\n gap = 0;\n } else if (start >= 0 && (++gap > 1 || r === rows)) {\n runs.push([start, r - gap]);\n start = -1;\n gap = 0;\n }\n }\n\n const split = (a: number, b: number): Array<[number, number]> => {\n if (b - a + 1 < 2 * minH + 1) return [[a, b]];\n let peak = 0;\n for (let r = a; r <= b; r++) peak = Math.max(peak, rowCount[r]);\n let valley = -1, valleyVal = Infinity;\n for (let r = a + minH; r <= b - minH; r++) {\n if (rowCount[r] < valleyVal) { valleyVal = rowCount[r]; valley = r; }\n }\n if (valley < 0 || valleyVal >= peak * 0.4) return [[a, b]];\n return [...split(a, valley - 1), ...split(valley + 1, b)];\n };\n\n for (const [a, b] of runs.flatMap(([a, b]) => split(a, b))) {\n const height = b - a + 1;\n if (height < minH || height > maxH) continue;\n let first = -1, last = -1, active = 0, inside = 0;\n for (let c = 0; c < cps; c++) {\n let n = 0;\n for (let r = a; r <= b; r++) n += cells[(r * STRIPS + s) * cps + c];\n if (n >= 2) {\n if (first < 0) first = c;\n last = c;\n active++;\n }\n inside += n;\n }\n if (first < 0) continue;\n const span = last - first + 1;\n // Edges in the two rows either side, over the same span, vs inside.\n let outside = 0, outsideRows = 0;\n for (const r of [a - 2, a - 1, b + 1, b + 2]) {\n if (r < 0 || r >= rows) continue;\n outsideRows++;\n for (let c = first; c <= last; c++) outside += cells[(r * STRIPS + s) * cps + c];\n }\n const insidePerRow = inside / height;\n bands.push({\n top: a,\n bottom: b,\n center: (a + b) / 2,\n height,\n strip: s,\n firstCell: first,\n spanFrac: span / cps,\n coverage: active / span,\n density: inside / (span * CELL * height),\n bleed: outsideRows > 0 && insidePerRow > 0 ? outside / outsideRows / insidePerRow : 0,\n });\n }\n }\n\n // 5. Full-width dense lines: bands on the same row across most strips.\n const fullWidthLines = clusterBands(bands.filter(b => b.spanFrac >= 0.75 && b.coverage >= 0.85))\n .filter(g => new Set(g.map(b => b.strip)).size >= MRZ_MIN_STRIPS)\n .map(g => {\n const top = Math.min(...g.map(b => b.top));\n const bottom = Math.max(...g.map(b => b.bottom));\n const height = g.reduce((sum, b) => sum + b.height, 0) / g.length;\n // Column edge profile across the whole line; fixed-pitch OCR-B glyphs\n // repeat at one character pitch, proportional print and texture don't.\n const profile = new Float64Array(aw);\n for (let i = 0; i < ex.length; i++) if (er[i] >= top && er[i] <= bottom) profile[ex[i]]++;\n const bleed = g.reduce((sum, b) => sum + b.bleed, 0) / g.length;\n return { top, bottom, center: (top + bottom) / 2, height, periodicity: periodicity(profile, height), bleed };\n });\n\n return { bands, fullWidthLines };\n}\n\n/** Median brightness step between horizontal neighbours: a cheap sensor-noise estimate. */\nfunction medianStep(grey: Float32Array, w: number, h: number, x0: number, aw: number): number {\n const hist = new Uint32Array(256);\n let n = 0;\n for (let y = 0; y < h; y++) {\n const row = y * w + x0;\n for (let x = 0; x < aw - 1; x++) {\n hist[Math.min(255, Math.round(Math.abs(grey[row + x + 1] - grey[row + x])))]++;\n n++;\n }\n }\n let acc = 0;\n for (let v = 0; v < 256; v++) {\n acc += hist[v];\n if (acc >= n / 2) return v;\n }\n return 255;\n}\n\n/** 3×3 box blur of the region, returned as an aw-wide array. */\nfunction boxSmooth(grey: Float32Array, w: number, h: number, x0: number, aw: number): Float32Array {\n const out = new Float32Array(aw * h);\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < aw; x++) {\n let sum = 0, n = 0;\n for (let yy = Math.max(0, y - 1); yy <= Math.min(h - 1, y + 1); yy++) {\n for (let xx = Math.max(0, x - 1); xx <= Math.min(aw - 1, x + 1); xx++) {\n sum += grey[yy * w + x0 + xx];\n n++;\n }\n }\n out[y * aw + x] = sum / n;\n }\n }\n return out;\n}\n\nfunction clusterBands(items: TextBand[]): TextBand[][] {\n const sorted = [...items].sort((p, q) => p.center - q.center);\n const groups: TextBand[][] = [];\n for (const band of sorted) {\n const g = groups[groups.length - 1];\n const ref = g?.[g.length - 1];\n if (ref && Math.abs(band.center - ref.center) <= 0.6 * Math.max(band.height, ref.height)) g.push(band);\n else groups.push([band]);\n }\n return groups;\n}\n\n/** Highest normalised autocorrelation of `profile` at lags of 0.4-1.6 × glyph height. */\nfunction periodicity(profile: Float64Array, glyphHeight: number): number {\n const n = profile.length;\n let mean = 0;\n for (let i = 0; i < n; i++) mean += profile[i];\n mean /= n;\n let denom = 0;\n for (let i = 0; i < n; i++) denom += (profile[i] - mean) ** 2;\n if (denom === 0) return 0;\n let best = 0;\n const lo = Math.max(2, Math.round(glyphHeight * 0.4));\n const hi = Math.min(n - 1, Math.round(glyphHeight * 1.6));\n for (let lag = lo; lag <= hi; lag++) {\n let num = 0;\n for (let i = 0; i + lag < n; i++) num += (profile[i] - mean) * (profile[i + lag] - mean);\n best = Math.max(best, num / denom);\n }\n return best;\n}\n\n/**\n * Finds printed text lines in the region [x0, x0+aw) of a greyscale image\n * and reports whether they include an MRZ. Pure pixel arithmetic, about\n * 5-20 ms on a 640 px image.\n */\nexport function analyseTextLayout(\n grey: Float32Array,\n w: number,\n h: number,\n x0 = 0,\n aw = w,\n): TextLayout {\n const { bands, fullWidthLines } = measureTextBands(grey, w, h, x0, aw);\n\n // Printed lines: wider than tall, with a clean gap above and below.\n // Texture and photo detail make bands too, but they bleed into the rows\n // around them.\n const stripCells = Math.ceil(Math.max(1, Math.floor(aw / STRIPS)) / CELL);\n const clean = bands.filter(b =>\n b.spanFrac >= 0.2 && b.coverage >= 0.35 && b.bleed <= TEXT_MAX_BLEED\n && (b.spanFrac * stripCells * CELL) / b.height >= 3);\n const textLines = clusterBands(clean).length;\n // Fields on a card sit in a column, so their lines start at the same x.\n let alignedLines = 0;\n for (const b of clean) {\n const n = clean.filter(o => o.strip === b.strip && Math.abs(o.firstCell - b.firstCell) <= 1).length;\n alignedLines = Math.max(alignedLines, n);\n }\n\n // MRZ: two clean, fixed-pitch, full-width lines of equal glyph height,\n // one line pitch (about twice the glyph height) apart. Runs are found over\n // *all* full-width lines first, so that a paragraph of print (four or\n // more evenly spaced lines) is never mistaken for one even if some of its\n // lines score like an MRZ. A run of three still passes, allowing for a\n // printed rule or field line right beside the MRZ.\n const isMrzLine = (l: MrzLineCandidate) => l.bleed <= MRZ_MAX_BLEED && l.periodicity >= MRZ_MIN_PERIODICITY;\n const stacked = (p: MrzLineCandidate, q: MrzLineCandidate) => {\n const avgH = (p.height + q.height) / 2;\n const pitch = q.center - p.center;\n return pitch >= 1.3 * avgH && pitch <= 2.8 * avgH\n && Math.max(p.height, q.height) / Math.min(p.height, q.height) <= 1.4;\n };\n let hasMrz = false;\n let runStart = 0;\n for (let i = 1; i <= fullWidthLines.length; i++) {\n if (i < fullWidthLines.length && stacked(fullWidthLines[i - 1], fullWidthLines[i])) continue;\n const run = fullWidthLines.slice(runStart, i);\n if (run.length >= 2 && run.length <= 3) {\n for (let j = 1; j < run.length; j++) {\n if (isMrzLine(run[j - 1]) && isMrzLine(run[j])) hasMrz = true;\n }\n }\n runStart = i;\n }\n\n return { textLines, alignedLines, hasMrz };\n}\n\n// Same progressive-enhancement pattern as FaceDetector above: Chromium's\n// Shape Detection TextDetector, where present, actually reads characters.\n// Only ever used to *pass* a photo the structural check above doubted, never\n// to reject one, so its uneven availability and accuracy can't add false\n// rejections.\ninterface ShapeDetectionText {\n rawValue?: string;\n}\ninterface ShapeDetectionTextDetectorCtor {\n new (): { detect(image: CanvasImageSource): Promise<ShapeDetectionText[]> };\n}\n\n/** Returns detected text blocks, or `null` when the API is unsupported or throws. */\nexport async function detectText(source: CanvasImageSource): Promise<ShapeDetectionText[] | null> {\n const TextDetectorCtor = (globalThis as { TextDetector?: ShapeDetectionTextDetectorCtor }).TextDetector;\n if (!TextDetectorCtor) return null;\n try {\n return await new TextDetectorCtor().detect(source);\n } catch {\n return null;\n }\n}\n\n/** True for a string that reads like an MRZ line: ≥25 chars of A-Z, 0-9 and at least one '<' filler. */\nexport function looksLikeMrzLine(text: string): boolean {\n const compact = text.replace(/\\s+/g, '').toUpperCase();\n return compact.length >= 25 && /^[A-Z0-9<]+$/.test(compact) && compact.includes('<');\n}\n\nfunction laplacianVariance(grey: Float32Array, w: number, h: number, x0: number, aw: number): number {\n let lapSum = 0, lapCount = 0;\n for (let y = 1; y < h - 1; y++) {\n for (let x = x0 + 1; x < x0 + aw - 1; x++) {\n const lap =\n 4 * grey[y * w + x]\n - grey[(y - 1) * w + x]\n - grey[(y + 1) * w + x]\n - grey[y * w + (x - 1)]\n - grey[y * w + (x + 1)];\n lapSum += lap * lap;\n lapCount++;\n }\n }\n return lapCount > 0 ? lapSum / lapCount : 0;\n}\n\n/**\n * Passport-specific checks: the data page must show an MRZ, and must be sharp.\n *\n * Two-page open spread (ratio > 2.0): both halves are scanned independently,\n * and the half with the MRZ is the data page. Only that half's blur is\n * evaluated, so a blurry opposite page never triggers a rejection.\n *\n * Single-page photo: the full image is treated as one region.\n *\n * `browserFoundMrz` is the TextDetector's verdict (see detectText()). When it\n * read an MRZ line the structural check missed, the photo passes on to the\n * blur check, using the region with the most text as the data page.\n */\nexport function checkPassportDocument(\n grey: Float32Array,\n w: number,\n h: number,\n browserFoundMrz = false,\n): string[] {\n const ratio = w / h;\n const regions: Array<{ x0: number; aw: number }> =\n ratio > 2.0\n ? [{ x0: 0, aw: Math.floor(w / 2) }, { x0: Math.floor(w / 2), aw: w - Math.floor(w / 2) }]\n : [{ x0: 0, aw: w }];\n\n let dataPage = regions.find(({ x0, aw }) => analyseTextLayout(grey, w, h, x0, aw).hasMrz);\n if (!dataPage && browserFoundMrz) {\n dataPage = regions.reduce((best, r) =>\n countTextRows(grey, w, h, r.x0, r.aw) > countTextRows(grey, w, h, best.x0, best.aw) ? r : best);\n }\n if (!dataPage) return [MRZ_NOT_FOUND_MESSAGE];\n\n if (laplacianVariance(grey, w, h, dataPage.x0, dataPage.aw) < BLUR_THRESHOLD) {\n return [\n 'The data page is too blurry — hold the camera steady and ensure the ' +\n 'MRZ text at the bottom of the data page is in sharp focus.',\n ];\n }\n return [];\n}\n\n/**\n * National ID / driver's licence check: the backend reads these by OCR of\n * the printed fields on the front, so the photo must show several separate\n * lines of print. `browserTextBlocks` is the TextDetector's block count (see\n * detectText()); three or more blocks read by the browser also passes.\n */\nexport function checkReadableText(\n grey: Float32Array,\n w: number,\n h: number,\n idType: string,\n browserTextBlocks: number | null = null,\n): string[] {\n if ((browserTextBlocks ?? 0) >= 3) return [];\n const layout = analyseTextLayout(grey, w, h);\n if (layout.textLines >= MIN_ID_TEXT_LINES && layout.alignedLines >= MIN_ID_ALIGNED_LINES) return [];\n return [idTextNotFoundMessage(idType)];\n}\n\n/**\n * Cross-document-type checks, run for every idType before the type-specific\n * ones above.\n *\n * 1. Unknown-image gate: every genuine ID document — card or passport —\n * has printed text somewhere in frame (name, ID number, dates, MRZ), which\n * shows up as high-contrast rows under countTextRows(). A photo of\n * something else entirely (a selfie, a random object, a blank surface, a\n * screenshot) has essentially none. This is a coarse heuristic, not OCR,\n * so the bar is set low — it only catches images with no document-like\n * structure at all, not blurry-but-genuine documents (blur is handled\n * separately above).\n *\n * 2. Type-mismatch gate: an open passport spread is unmistakably wide\n * (ratio > 2.0) and an ID-1 card has a fixed, distinctive ratio (~1.585,\n * ISO 7810). Uploading the wrong type against the caller-declared idType\n * is flagged in both directions, mirroring the backend's own\n * document_is_passport / document_is_not_passport check (see\n * docs/kyc-pipeline.md §9 in attestid_api) so the user gets the same\n * guidance without waiting on a round trip. This only catches the obvious\n * aspect-ratio case — subtler mismatches still fall through to the\n * backend's dual-OCR-reader check.\n */\nexport function checkDocumentIdentity(\n grey: Float32Array,\n w: number,\n h: number,\n idType: string,\n // Lets checkPhotoQuality() pass in a scan it already ran, instead of this\n // function repeating it. Optional so direct/test callers behave as before.\n precomputedTotalTextRows?: number,\n): string[] {\n const ratio = w / h;\n\n const totalTextRows = precomputedTotalTextRows ?? countTextRows(grey, w, h, 0, w);\n if (totalTextRows < Math.max(2, h * 0.02)) {\n return [\n 'This does not look like an identity document — no printed text was detected. ' +\n 'Please photograph your ID card or passport data page on a plain, well-lit background.',\n ];\n }\n\n if (idType === 'passport') {\n if (ratio < 2.0 && Math.abs(ratio - ID1_ASPECT) < ID1_ASPECT_TOLERANCE) {\n return [\n \"You selected Passport as the document type, but this looks like a National ID or Driver's Licence. \" +\n \"Please go back and select the correct document type.\",\n ];\n }\n } else if (ratio > 2.0) {\n return [\n \"You selected National ID or Driver's Licence, but this looks like a Passport. \" +\n \"Please go back and select 'Passport' as the document type.\",\n ];\n }\n\n return [];\n}\n\n/**\n * Analyses a photo for blur, exposure, and (where the browser supports it)\n * face presence before it is submitted to the AttestID API. Returns a list\n * of human-readable issues; `ok` is true only when the list is empty.\n *\n * Run this before calling startVerification() to avoid unnecessary API calls\n * and to give users clear guidance on retaking a poor-quality photo. This is\n * intentionally not exhaustive — reasons that require real OCR or a facial-\n * attribute model (blurry field reads, sunglasses, closed eyes, …) can only\n * be determined by the backend pipeline; see the scope note above\n * detectFaceCount(). Obviously-wrong-document-type and unrecognizable images\n * *are* caught here (see checkDocumentIdentity()), and so are photos the\n * backend couldn't read at all: a passport with no visible MRZ, or a card\n * with no printed fields (see checkPassportDocument() / checkReadableText()).\n *\n * @param photo - The image to check (File or Blob)\n * @param idType - Document type; passports must show an MRZ, cards must show printed fields\n */\nexport async function checkPhotoQuality(\n photo: Blob,\n idType = 'national_id',\n): Promise<PhotoQualityResult> {\n const issues: string[] = [];\n\n if (photo.size < MIN_FILE_BYTES) {\n issues.push('File is too small — this does not appear to be a real photo.');\n return { ok: false, issues };\n }\n\n if (!(await hasSupportedMagicBytes(photo))) {\n issues.push('Unsupported file type — please upload a JPEG or PNG photo.');\n return { ok: false, issues };\n }\n\n return new Promise(resolve => {\n const img = new Image();\n const url = URL.createObjectURL(photo);\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve({ ok: false, issues: ['Could not read the image — please try a different photo.'] });\n };\n\n img.onload = async () => {\n URL.revokeObjectURL(url);\n\n // Downscale to 640 px on the longest side — enough for all checks\n const scale = Math.min(1, 640 / Math.max(img.width, img.height));\n const w = Math.round(img.width * scale);\n const h = Math.round(img.height * scale);\n\n const canvas = document.createElement('canvas');\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext('2d')!;\n // The bilinear filtering the canvas would otherwise apply while\n // downscaling is itself a low-pass blur, stacked on top of whatever\n // real sharpness the source frame has — exactly the confound that\n // makes the Laplacian check below mistake a resized-down but genuinely\n // sharp capture for a blurry one. Nearest-neighbour sampling leaves\n // the source's actual edge contrast alone.\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(img, 0, 0, w, h);\n\n // Fire this off now — it's a separate (often off-thread) browser API\n // call, so let it run concurrently with the pixel analysis below\n // instead of waiting until that's done to even start it.\n const facesPromise = detectFaces(canvas);\n const textPromise = detectText(canvas);\n\n const { data } = ctx.getImageData(0, 0, w, h);\n const len = w * h;\n\n // Greyscale + average brightness\n const grey = new Float32Array(len);\n let totalLum = 0;\n for (let i = 0; i < len; i++) {\n const lum = 0.299 * data[i * 4] + 0.587 * data[i * 4 + 1] + 0.114 * data[i * 4 + 2];\n grey[i] = lum;\n totalLum += lum;\n }\n const brightness = totalLum / len;\n\n // Laplacian variance — measures edge sharpness; low = blurry. Skipped\n // entirely for passports: checkPassportDocument() below computes blur\n // per-region instead (so a blurry non-data page is never penalised),\n // which makes this full-image pass wasted work in that case.\n if (idType !== 'passport' && laplacianVariance(grey, w, h, 0, w) < BLUR_THRESHOLD)\n issues.push('Photo is too blurry — hold the camera steady and ensure the document is in sharp focus.');\n if (brightness < DARK_THRESHOLD)\n issues.push('Image is too dark — move to a well-lit area.');\n if (brightness > BRIGHT_THRESHOLD)\n issues.push('Image is overexposed — avoid direct light or glare shining onto the document.');\n\n // Unknown-image and wrong-document-type gates run first — if the image\n // isn't a document at all, or is clearly the other document type, the\n // passport data-page check below would just add a confusing second\n // message about the same underlying problem.\n const totalTextRows = countTextRows(grey, w, h, 0, w);\n const identityIssues = checkDocumentIdentity(grey, w, h, idType, totalTextRows);\n issues.push(...identityIssues);\n\n // The backend reads a passport from its MRZ and a card from its printed\n // fields, so a photo with neither is rejected here instead of failing\n // after the upload. A blurry, dark or glary photo hides both, so when\n // one of those issues is already listed only that root cause is shown.\n if (identityIssues.length === 0) {\n const texts = await textPromise;\n if (idType === 'passport') {\n const browserFoundMrz = texts?.some(t => looksLikeMrzLine(t.rawValue ?? '')) ?? false;\n const passportIssues = checkPassportDocument(grey, w, h, browserFoundMrz);\n if (!(issues.length > 0 && passportIssues[0] === MRZ_NOT_FOUND_MESSAGE)) issues.push(...passportIssues);\n } else {\n if (issues.length === 0) issues.push(...checkReadableText(grey, w, h, idType, texts?.length ?? null));\n }\n }\n\n // Zero faces maps to face_detection_failed, more than one to\n // multiple_people_detected — both Failed (see the comment on\n // detectFaces() above). `null` means the browser doesn't support the\n // Shape Detection API; never block on that, just skip the check.\n // significantFaceCount() discards faces much smaller than the\n // largest one detected, so an ID card's \"ghost image\" watermark\n // doesn't get mistaken for a second person.\n const faces = await facesPromise;\n const faceCount = faces === null ? null : significantFaceCount(faces);\n if (faceCount === 0) {\n issues.push('No face was detected on your document. Please ensure the photo on your ID is clearly visible and try again.');\n } else if (faceCount !== null && faceCount > 1) {\n issues.push('More than one face was detected in the photo. Please upload a photo showing only your ID document.');\n }\n\n resolve({ ok: issues.length === 0, issues });\n };\n\n img.src = url;\n });\n}\n","import { AuthError, KYCError, ValidationError } from './errors';\nimport { postIdempotent, type FetchLike, type RetryOptions } from './http';\nimport { hasSupportedMagicBytes } from './photoQuality';\nimport type { CaptureSignature } from './deviceKey';\nimport type {\n SdkSessionResponse,\n StartKYCOptions,\n StartKYCResponse,\n KYCResult,\n LivenessCredentials,\n LivenessChallenge,\n LivenessVideoResult,\n LivenessProvider,\n} from './types';\n\n/** Optional transport settings: custom fetch (e.g. mTLS) and POST retry policy. */\nexport interface Transport {\n fetch?: FetchLike;\n retry?: RetryOptions;\n}\n\n// Resolved per call (not captured at import), so a test or polyfill that\n// replaces globalThis.fetch later is still honoured.\nconst fetchOf = (t?: Transport): FetchLike => t?.fetch ?? ((input, init) => fetch(input, init));\n\n/**\n * Extracts the API's own error message verbatim, instead of the raw response\n * body. The API always errors with FastAPI's `{\"detail\": ...}` envelope,\n * where `detail` is either a plain string or an object carrying a\n * human-readable `message` (see api/v1/kyc.py) — either way we surface that\n * exact string, unmodified, so the SDK never paraphrases or drops detail the\n * backend intentionally chose to send.\n *\n * Sanitized against anything that isn't that expected shape: unparsable\n * bodies, or a `detail` that's neither a string nor an object with a string\n * `message`, fall back to the raw response text rather than surfacing\n * `[object Object]`, `undefined`, or throwing while handling an error.\n */\nasync function extractErrorMessage(res: Response): Promise<string> {\n const text = await res.text();\n try {\n const detail = (JSON.parse(text) as { detail?: unknown }).detail;\n if (typeof detail === 'string') return detail;\n if (detail !== null && typeof detail === 'object') {\n const message = (detail as { message?: unknown }).message;\n if (typeof message === 'string') return message;\n }\n } catch {\n // Not JSON (or not the { detail } shape) — fall through to the raw text.\n }\n return text;\n}\n\nasync function handleResponse<T>(res: Response): Promise<T> {\n if (res.ok) return res.json() as Promise<T>;\n const message = await extractErrorMessage(res);\n if (res.status === 401 || res.status === 403) throw new AuthError(message);\n throw new KYCError(message, res.status);\n}\n\n/**\n * Exchange the API key (+ its paired access key ID) for a short-lived SDK\n * token bound to userId. Every other KYC call presents this token instead\n * of the raw API key.\n */\nexport async function createSdkSession(\n apiUrl: string,\n apiKey: string,\n accessKeyId: string,\n userId: string,\n transport?: Transport,\n): Promise<SdkSessionResponse> {\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/session`,\n () => ({\n headers: {\n 'X-API-Key': apiKey,\n 'X-Access-Key-Id': accessKeyId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ user_id: userId }),\n }),\n transport?.retry,\n );\n return handleResponse<SdkSessionResponse>(res);\n}\n\n/**\n * Upload the ID photo. `signCapture`, when given, is called once per attempt\n * that needs a fresh request (see http.ts). Each signature uses a single-use\n * nonce, so a rebuilt retry must re-sign instead of replaying the old\n * signature. A null result uploads the photo unsigned.\n */\n/** Tells the API which liveness capture flows this app can run. */\nexport const LIVENESS_FLOWS_HEADER = 'X-AttestID-Liveness-Flows';\n\nfunction sdkHeaders(sdkToken: string, livenessFlows?: readonly LivenessProvider[]): Record<string, string> {\n const headers: Record<string, string> = { 'Authorization': `Bearer ${sdkToken}` };\n if (livenessFlows?.length) headers[LIVENESS_FLOWS_HEADER] = livenessFlows.join(',');\n return headers;\n}\n\nexport async function startKYC(\n apiUrl: string,\n sdkToken: string,\n options: StartKYCOptions,\n transport?: Transport,\n signCapture?: () => Promise<CaptureSignature | null>,\n livenessFlows?: readonly LivenessProvider[],\n): Promise<StartKYCResponse> {\n // Hard gate on file format, independent of checkPhotoQuality() — a caller\n // using the core client directly (no React hook) never runs that advisory\n // check, so this is the one place every upload is guaranteed to pass\n // through. Only a genuine JPEG/PNG signature is accepted; extension and\n // Content-Type are both attacker-controlled and never trusted.\n if (!(await hasSupportedMagicBytes(options.idPhoto))) {\n throw new ValidationError('Unsupported image format — only JPEG and PNG photos are accepted.');\n }\n\n const build = async () => {\n const form = new FormData();\n form.append('user_id', options.userId);\n form.append('id_type', options.idType);\n form.append('id_photo', options.idPhoto);\n form.append('consent_given_at', options.consentGivenAt.toISOString());\n form.append('processing_purpose', options.processingPurpose ?? 'identity_verification');\n const signature = signCapture ? await signCapture() : null;\n if (signature) {\n // All four or none: a partial set is treated as a failed signature.\n for (const [k, v] of Object.entries(signature)) form.append(k, v);\n }\n return { headers: sdkHeaders(sdkToken, livenessFlows), body: form };\n };\n\n const res = await postIdempotent(fetchOf(transport), `${apiUrl}/v1/kyc/start`, build, transport?.retry);\n return handleResponse<StartKYCResponse>(res);\n}\n\nexport async function getLivenessCredentials(\n apiUrl: string,\n sdkToken: string,\n transport?: Transport,\n): Promise<LivenessCredentials> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/liveness-credentials`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n return handleResponse<LivenessCredentials>(res);\n}\n\nconst enc = encodeURIComponent;\n\n/** The random liveness script + nonce for an AttestID Vision session. */\nexport async function getLivenessChallenge(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n transport?: Transport,\n): Promise<LivenessChallenge> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-challenge`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n return handleResponse<LivenessChallenge>(res);\n}\n\nexport interface SubmitLivenessVideoOptions {\n /** From getLivenessChallenge(). */\n nonce: string;\n /** True if the recorded frames are mirrored. A browser's front camera records un-mirrored frames (only the preview is mirrored), so the default false is right for recordLiveness(). */\n mirrored?: boolean;\n}\n\n/**\n * Upload the liveness video. Like startKYC(), `signCapture` is called once\n * per attempt that needs a fresh request, so a retried upload is re-signed\n * with a fresh single-use nonce. A null result uploads the video unsigned.\n */\nexport async function submitLivenessVideo(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n video: Blob,\n options: SubmitLivenessVideoOptions,\n transport?: Transport,\n signCapture?: () => Promise<CaptureSignature | null>,\n): Promise<LivenessVideoResult> {\n const type = (video.type || 'video/webm').split(';')[0];\n const ext = type === 'video/mp4' ? 'mp4' : type === 'video/quicktime' ? 'mov' : 'webm';\n const build = async () => {\n const form = new FormData();\n // Re-wrap so the part's Content-Type is the bare media type the API\n // accepts ('video/webm', not 'video/webm;codecs=vp8').\n form.append('video', new File([video], `liveness.${ext}`, { type }));\n form.append('nonce', options.nonce);\n form.append('mirrored', options.mirrored ? 'true' : 'false');\n const signature = signCapture ? await signCapture() : null;\n if (signature) {\n for (const [k, v] of Object.entries(signature)) form.append(k, v);\n }\n return { headers: { 'Authorization': `Bearer ${sdkToken}` }, body: form };\n };\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-video`,\n build,\n transport?.retry,\n );\n return handleResponse<LivenessVideoResult>(res);\n}\n\n/** Poll target after submitLivenessVideo() answered status 'in_progress'. */\nexport async function getLivenessStatus(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n transport?: Transport,\n): Promise<LivenessVideoResult> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-status`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n return handleResponse<LivenessVideoResult>(res);\n}\n\n/**\n * Reopen a verification that failed only at liveness with a fresh liveness\n * session, reusing the ID photo already uploaded by startKYC(). Same response\n * shape as startKYC(): run the liveness flow it names, then completeKYC().\n */\nexport async function retryLiveness(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n transport?: Transport,\n livenessFlows?: readonly LivenessProvider[],\n): Promise<StartKYCResponse> {\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-retry`,\n () => ({ headers: sdkHeaders(sdkToken, livenessFlows), body: new FormData() }),\n transport?.retry,\n );\n return handleResponse<StartKYCResponse>(res);\n}\n\nexport async function completeKYC(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n deviceId?: string,\n transport?: Transport,\n): Promise<KYCResult> {\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/complete`,\n () => {\n const form = new FormData();\n form.append('verification_id', verificationId);\n if (deviceId) form.append('device_id', deviceId);\n return { headers: { 'Authorization': `Bearer ${sdkToken}` }, body: form };\n },\n transport?.retry,\n );\n return handleResponse<KYCResult>(res);\n}\n","/**\n * Device-bound capture signing (AttestID SECURITY.md P6), browser side.\n *\n * The SDK keeps one ECDSA P-256 key pair per browser profile. The private\n * key is created **non-extractable** with WebCrypto and persisted in\n * IndexedDB as a CryptoKey object: JavaScript, including this SDK, can use\n * it to sign but can never read its bytes. Before each upload:\n *\n * 1. POST /v1/kyc/device/challenge → nonce, then POST /v1/kyc/device/register\n * with the public key. Registration runs every session: the server\n * de-duplicates the same key, so a server-side erasure of old device\n * keys can never leave a stale id behind.\n * 2. A fresh challenge, then sign\n * \"attestid-capture-v1|<sha256 hex of the exact uploaded bytes>|<user_id>|<nonce>|<unix ts>\"\n * and send the four capture_* fields with POST /v1/kyc/start.\n *\n * What this proves: the photo was submitted from the same device that holds\n * the registered key, within a few minutes, through this SDK flow, so\n * images replayed or injected straight into the API (no key, wrong key,\n * reused nonce) are caught. What it doesn't prove (web only): that the bytes\n * came from the physical camera. Browsers have no hardware attestation, so\n * web keys stay at the server's \"key_bound\" level, never \"hardware_attested\".\n *\n * Best-effort by design: if anything fails (no WebCrypto/IndexedDB, network,\n * an older API without these endpoints) the SDK sends **no** capture fields\n * at all. That's assurance \"none\", which isn't penalised. A partial or broken\n * signature would make the server hold the verification for fraud review, so\n * one is never sent.\n */\nimport { buf, bytesToBase64, bytesToHex, hasWebCrypto, sha256, subtle, utf8 } from './encoding';\nimport type { FetchLike } from './http';\n\nexport interface CaptureSignature {\n device_key_id: string;\n capture_nonce: string;\n capture_timestamp: string;\n capture_signature: string;\n}\n\nconst DB_NAME = 'attestid-sdk';\nconst STORE = 'device-keys';\nconst KEY_ID = 'capture-signing-v1';\n\nlet memoryKey: Promise<CryptoKeyPair> | null = null;\n\nfunction idb(): IDBFactory | undefined {\n return (globalThis as { indexedDB?: IDBFactory }).indexedDB;\n}\n\nfunction openDb(factory: IDBFactory): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = factory.open(DB_NAME, 1);\n req.onupgradeneeded = () => req.result.createObjectStore(STORE);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction idbRequest<T>(db: IDBDatabase, mode: IDBTransactionMode, op: (s: IDBObjectStore) => IDBRequest): Promise<T> {\n return new Promise((resolve, reject) => {\n const req = op(db.transaction(STORE, mode).objectStore(STORE));\n req.onsuccess = () => resolve(req.result as T);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction generate(): Promise<CryptoKeyPair> {\n return subtle().generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify']);\n}\n\n/**\n * The device's key pair: loaded from IndexedDB, or created and stored there.\n * Falls back to a per-page-load in-memory key when IndexedDB is unavailable\n * (private mode, some webviews, tests). That's still a valid registered key, just not persistent.\n */\nexport function getDeviceKeyPair(): Promise<CryptoKeyPair> {\n const factory = idb();\n if (!factory) return (memoryKey ??= generate());\n return (async () => {\n try {\n const db = await openDb(factory);\n try {\n const existing = await idbRequest<CryptoKeyPair | undefined>(db, 'readonly', s => s.get(KEY_ID));\n if (existing?.privateKey && existing.publicKey) return existing;\n const pair = await generate();\n await idbRequest(db, 'readwrite', s => s.put(pair, KEY_ID));\n return pair;\n } finally {\n db.close();\n }\n } catch {\n return (memoryKey ??= generate());\n }\n })();\n}\n\nexport function deviceBindingSupported(): boolean {\n return hasWebCrypto();\n}\n\nasync function postJson<T>(fetchImpl: FetchLike, url: string, token: string, body?: unknown): Promise<T> {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) throw new Error(`${url} → HTTP ${res.status}`);\n return res.json() as Promise<T>;\n}\n\nasync function challenge(fetchImpl: FetchLike, apiUrl: string, token: string): Promise<string> {\n return (await postJson<{ nonce: string }>(fetchImpl, `${apiUrl}/v1/kyc/device/challenge`, token)).nonce;\n}\n\n/** Register this device's public key for the SDK token's end user. Returns device_key_id. */\nexport async function registerDeviceKey(\n fetchImpl: FetchLike,\n apiUrl: string,\n token: string,\n pair: CryptoKeyPair,\n): Promise<string> {\n const spki = new Uint8Array(await subtle().exportKey('spki', pair.publicKey));\n const nonce = await challenge(fetchImpl, apiUrl, token);\n const res = await postJson<{ device_key_id: string }>(fetchImpl, `${apiUrl}/v1/kyc/device/register`, token, {\n platform: 'web',\n public_key: bytesToBase64(spki),\n nonce,\n });\n return res.device_key_id;\n}\n\nexport function captureMessage(imageSha256Hex: string, userId: string, nonce: string, ts: number): string {\n return `attestid-capture-v1|${imageSha256Hex}|${userId}|${nonce}|${ts}`;\n}\n\n/**\n * Register (idempotently) and sign one capture. Returns null instead of\n * throwing on any failure, so the upload goes out unsigned rather than with\n * a broken signature.\n */\nexport async function signCapture(\n fetchImpl: FetchLike,\n apiUrl: string,\n token: string,\n userId: string,\n photo: Blob,\n): Promise<CaptureSignature | null> {\n try {\n if (!deviceBindingSupported()) return null;\n const pair = await getDeviceKeyPair();\n const deviceKeyId = await registerDeviceKey(fetchImpl, apiUrl, token, pair);\n const imageHash = bytesToHex(await sha256(new Uint8Array(await photo.arrayBuffer())));\n const nonce = await challenge(fetchImpl, apiUrl, token);\n const ts = Math.floor(Date.now() / 1000);\n // WebCrypto ECDSA returns raw r||s (64 bytes), which the API accepts as-is.\n const sig = new Uint8Array(\n await subtle().sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n pair.privateKey,\n buf(utf8(captureMessage(imageHash, userId, nonce, ts))),\n ),\n );\n return {\n device_key_id: deviceKeyId,\n capture_nonce: nonce,\n capture_timestamp: String(ts),\n capture_signature: bytesToBase64(sig),\n };\n } catch {\n return null;\n }\n}\n\n/** @internal test hook */\nexport function _resetDeviceKeyCache(): void {\n memoryKey = null;\n}\n","import type { IdType, KYCResult } from './types';\n\n/** Strips provider prefix (\"aws:\" / \"azure:\") from a liveness session token */\nexport function rawSessionId(token: string): string {\n const idx = token.indexOf(':');\n return idx !== -1 ? token.slice(idx + 1) : token;\n}\n\n/** ISO 7810 document aspect ratios (width / height) */\nexport const DOC_ASPECT: Record<IdType, number> = {\n national_id: 85.6 / 54, // ID-1 — all national ID cards regardless of country\n drivers_licence: 85.6 / 54, // ID-1\n passport: 125 / 88, // ID-3 booklet data page\n};\n\n/**\n * How the API ruled on the face match. Derived from the API's own decision\n * rather than re-applying a threshold here: the gate is server config and\n * differs per matcher (Rekognition vs attestid_vision).\n */\nexport function faceMatchOutcome(result: KYCResult): 'pass' | 'review' | 'fail' {\n const flags = result.checks?.fraud_flags ?? [];\n if (flags.includes('face_match_borderline')) return 'review';\n if (result.failure_reason === 'face_match_failed' || flags.includes('repeated_face_match_failure')) {\n return 'fail';\n }\n return 'pass';\n}\n\n/**\n * Face-check failures that look like a bad capture rather than an attack. A\n * verification that failed with one of these can retry the face check with\n * AttestID.retryLiveness(), without uploading the ID photo again. Includes a\n * scored face-match miss (face_match_failed): the ID photo was read and\n * compared, so only the selfie is retaken. Mirrors the API's\n * RETRYABLE_LIVENESS_REASONS. The API has the final say (it also caps\n * retries and limits how long after upload they're allowed).\n */\nexport const RETRYABLE_LIVENESS_REASONS: ReadonlySet<string> = new Set([\n 'liveness_failed',\n 'liveness_confidence_too_low',\n 'liveness_session_missing',\n 'liveness_reference_image_missing',\n 'liveness_in_progress',\n 'face_match_failed',\n]);\n\n/** True if this result failed only at the face check, so retryLiveness() can be offered. */\nexport function canRetryLiveness(result: KYCResult): boolean {\n if (result.verified || !result.failure_reason) return false;\n return RETRYABLE_LIVENESS_REASONS.has(result.failure_reason.split(':', 1)[0]);\n}\n","import {\n createSdkSession,\n startKYC,\n getLivenessCredentials,\n getLivenessChallenge,\n submitLivenessVideo,\n getLivenessStatus,\n retryLiveness,\n completeKYC,\n type SubmitLivenessVideoOptions,\n type Transport,\n} from './api';\nimport { signCapture } from './deviceKey';\nimport { rawSessionId } from './utils';\nimport { AttestIDError, AuthError } from './errors';\nimport type {\n AttestIDConfig,\n AttestIDConfigServer,\n StartKYCOptions,\n StartKYCResponse,\n LivenessCredentials,\n LivenessChallenge,\n LivenessProvider,\n LivenessVideoResult,\n KYCResult,\n} from './types';\n\nconst sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));\n\n/** The flows this SDK ships components for: LivenessDetector and VisionLivenessCapture. */\nconst DEFAULT_LIVENESS_FLOWS: readonly LivenessProvider[] = ['aws', 'vision'];\n\nexport interface WaitForLivenessOptions {\n /** Poll interval. Default 1500 ms. */\n intervalMs?: number;\n /** Give up after this long. Default 120 s. */\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\n// Refresh a bit before actual expiry so an in-flight request never races the\n// backend's own clock.\nconst TOKEN_REFRESH_MARGIN_MS = 15_000;\n\nfunction isServerConfig(config: AttestIDConfig): config is AttestIDConfigServer {\n return 'apiKey' in config;\n}\n\n// A bundler shipping code to a browser, React Native, or an edge runtime never\n// exposes a real Node.js process — only an actual Node backend process does.\n// This is what we gate apiKey/accessKeyId on: those are long-lived secrets that\n// must never end up in code a bundler ships to a client. Accessed via\n// globalThis (rather than the bare `process` identifier) so this isomorphic\n// package doesn't need a hard @types/node dependency.\nfunction isTrustedServerEnvironment(): boolean {\n const proc = (globalThis as { process?: { versions?: { node?: string } } }).process;\n return !!proc?.versions?.node;\n}\n\n// http:// is only tolerated against a loopback backend — i.e. a developer\n// running the API locally without TLS. Anything else (a real host, an IP,\n// a tunnel/staging domain) must be https, since apiKey/accessKeyId, the\n// bearer sdk_token, and the ID photo itself all travel over this URL.\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);\n\nfunction assertSecureApiUrl(apiUrl: string): void {\n let url: URL;\n try {\n url = new URL(apiUrl);\n } catch {\n throw new AttestIDError(`apiUrl must be a valid absolute URL — got \"${apiUrl}\".`, 'INSECURE_CONFIG');\n }\n if (url.protocol === 'https:') return;\n if (url.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname)) return;\n throw new AttestIDError(\n `apiUrl must use https:// — got \"${apiUrl}\". Sending it over plain http exposes your API key, ` +\n 'SDK token, and uploaded ID photos to anyone on the network path. http://localhost (or ' +\n '127.0.0.1/::1) is allowed for local development against a backend running without TLS.',\n 'INSECURE_CONFIG',\n );\n}\n\nexport class AttestID {\n private readonly config: AttestIDConfig & { region: string };\n private readonly transport: Transport;\n private readonly deviceBinding: boolean;\n private readonly livenessFlows: readonly LivenessProvider[];\n\n // The backend never sees anything but this short-lived token past session\n // creation — the SDK exchanges the API key (server mode) or calls your\n // getSdkToken callback (browser mode) once per user/expiry and reuses the\n // result for every subsequent KYC call.\n private tokenPromise: Promise<string> | null = null;\n private tokenUserId: string | null = null;\n private tokenExpiresAt = 0;\n private tokenInFlight = false;\n\n constructor(config: AttestIDConfig) {\n assertSecureApiUrl(config.apiUrl);\n if (isServerConfig(config) && !isTrustedServerEnvironment()) {\n throw new AttestIDError(\n 'apiKey/accessKeyId must only be used in a trusted Node.js backend process — a bundler ' +\n 'would ship this long-lived secret to every client, letting anyone extract it. ' +\n 'In browser or mobile app code, configure `getSdkToken` instead: a callback that fetches ' +\n 'an already-minted sdk_token from your own backend, which holds apiKey server-side and ' +\n 'calls POST /kyc/session. See the integration guide for the backend-proxy pattern.',\n 'INSECURE_CONFIG',\n );\n }\n this.config = { region: 'us-east-1', ...config };\n this.transport = { fetch: config.fetch, retry: config.retry };\n this.livenessFlows = config.livenessFlows?.length ? [...config.livenessFlows] : DEFAULT_LIVENESS_FLOWS;\n // Device binding signs with a key that lives on the end user's device.\n // A backend process holding apiKey is never that device, so it's off in\n // server mode whatever the config says.\n this.deviceBinding = !isServerConfig(config) && (config.deviceBinding ?? true);\n }\n\n /**\n * Obtain a short-lived SDK token bound to userId, reusing an in-flight or\n * still-valid token for the same user instead of minting a new one on every\n * call. Server mode exchanges apiKey/accessKeyId itself; browser mode\n * delegates to your getSdkToken callback.\n */\n private getSdkToken(userId: string): Promise<string> {\n const sameUser = this.tokenUserId === userId;\n const stillValid = Date.now() < this.tokenExpiresAt - TOKEN_REFRESH_MARGIN_MS;\n // Reuse the cached token when it's still valid, OR when a mint for this same\n // user is already in flight. The in-flight case matters: startVerification()\n // and getLivenessCredentials() are fired together (Promise.all in\n // useAttestID), both call this synchronously before the first resolves, and\n // tokenExpiresAt isn't set yet — without the tokenInFlight check the second\n // call mints its own token, doubling the round-trips to your backend and to\n // POST /kyc/session.\n if (this.tokenPromise && sameUser && (stillValid || this.tokenInFlight)) {\n return this.tokenPromise;\n }\n\n this.tokenUserId = userId;\n this.tokenInFlight = true;\n this.tokenExpiresAt = 0;\n const config = this.config;\n const sessionPromise = isServerConfig(config)\n ? createSdkSession(config.apiUrl, config.apiKey, config.accessKeyId, userId, this.transport)\n : config.getSdkToken(userId);\n const minted = sessionPromise.then(session => {\n // In server mode, createSdkSession() already throws on a non-2xx\n // response. In browser mode, `session` comes from the consumer's own\n // getSdkToken callback — e.g. a backend proxy response parsed without\n // checking res.ok — so an auth failure upstream can resolve here as a\n // plain object with no sdk_token instead of rejecting. Catch that now\n // rather than silently sending \"Authorization: Bearer undefined\" on\n // the next call, which fails with a confusing, unrelated 401.\n if (!session?.sdk_token) {\n throw new AuthError(\n 'getSdkToken() did not return a valid sdk_token — check that your backend\\'s ' +\n '/api/attestid-session endpoint (or equivalent) returned a successful response.',\n );\n }\n this.tokenExpiresAt = Date.now() + session.expires_in * 1000;\n this.tokenInFlight = false;\n return session.sdk_token;\n });\n // A failed mint must not stay cached — the next call should retry instead of\n // replaying the same rejection. Only clear if this is still the current\n // attempt (a later getSdkToken() may already have replaced it).\n minted.catch(() => {\n if (this.tokenPromise === minted) {\n this.tokenPromise = null;\n this.tokenUserId = null;\n this.tokenExpiresAt = 0;\n this.tokenInFlight = false;\n }\n });\n this.tokenPromise = minted;\n return minted;\n }\n\n /**\n * Start a KYC verification — uploads the ID photo and returns a liveness\n * session token to pass to the FaceLivenessDetector component.\n *\n * `options.consentGivenAt` is mandatory and must be the timestamp of an\n * actual user action (e.g. ticking a consent checkbox), not the moment\n * this method happens to be called. Plain JavaScript callers bypassing\n * TypeScript's type check are still rejected here at runtime.\n */\n async startVerification(options: StartKYCOptions): Promise<StartKYCResponse> {\n if (!options.consentGivenAt) {\n throw new AttestIDError(\n 'consentGivenAt is required — capture the timestamp of an explicit user consent action before starting verification',\n 'CONSENT_REQUIRED',\n );\n }\n const token = await this.getSdkToken(options.userId);\n const { apiUrl } = this.config;\n const fetchImpl = this.transport.fetch ?? ((input: string, init?: RequestInit) => fetch(input, init));\n const signer = this.deviceBinding\n ? () => signCapture(fetchImpl, apiUrl, token, options.userId, options.idPhoto)\n : undefined;\n return startKYC(apiUrl, token, options, this.transport, signer, this.livenessFlows);\n }\n\n /**\n * Fetch temporary AWS credentials and the Cognito identity pool ID from the\n * backend. Call this alongside startVerification() for the same userId to\n * configure Amplify before mounting FaceLivenessDetector.\n */\n async getLivenessCredentials(userId: string): Promise<LivenessCredentials> {\n const token = await this.getSdkToken(userId);\n return getLivenessCredentials(this.config.apiUrl, token, this.transport);\n }\n\n /**\n * Returns the raw liveness session ID (strips provider prefix) ready to\n * pass directly to the FaceLivenessDetector `sessionId` prop.\n */\n getLivenessSessionId(token: string): string {\n return rawSessionId(token);\n }\n\n /**\n * Which face-check flow this verification uses. The API picks it per\n * session (a super admin can switch between AWS Rekognition and AttestID\n * Vision, with automatic fallback), so branch on this rather than assuming\n * the Amplify flow. Responses from older API versions carry no provider:\n * that means 'aws'.\n */\n livenessProvider(start: StartKYCResponse): LivenessProvider {\n return start.liveness_provider ?? 'aws';\n }\n\n private async currentToken(method: string): Promise<string> {\n if (!this.tokenPromise) {\n throw new AttestIDError(`${method}() must be called after startVerification()`, 'AUTH_ERROR');\n }\n return this.tokenPromise;\n }\n\n /**\n * AttestID Vision only: the random, single-use liveness script and nonce.\n * Play `script` while recording (recordLiveness / VisionLivenessCapture) and\n * upload with submitLivenessVideo() before `expires_at`.\n */\n async getLivenessChallenge(verificationId: string): Promise<LivenessChallenge> {\n const token = await this.currentToken('getLivenessChallenge');\n return getLivenessChallenge(this.config.apiUrl, token, verificationId, this.transport);\n }\n\n /**\n * AttestID Vision only: upload the recorded liveness video. With device\n * binding on, the video is signed with the same device key as the ID photo.\n * If the result's status is 'in_progress', call waitForLivenessResult()\n * next. Either way, finish with completeVerification(), which turns the\n * liveness outcome into the final result.\n */\n async submitLivenessVideo(\n verificationId: string,\n video: Blob,\n options: SubmitLivenessVideoOptions,\n ): Promise<LivenessVideoResult> {\n const token = await this.currentToken('submitLivenessVideo');\n const { apiUrl } = this.config;\n const fetchImpl = this.transport.fetch ?? ((input: string, init?: RequestInit) => fetch(input, init));\n const userId = this.tokenUserId ?? '';\n const signer = this.deviceBinding\n ? () => signCapture(fetchImpl, apiUrl, token, userId, video)\n : undefined;\n return submitLivenessVideo(apiUrl, token, verificationId, video, options, this.transport, signer);\n }\n\n /**\n * AttestID Vision only: poll until the liveness decision is ready\n * (status 'done'). Don't call completeVerification() before this resolves,\n * because completing while liveness is still in progress fails the\n * verification.\n */\n async waitForLivenessResult(\n verificationId: string,\n options: WaitForLivenessOptions = {},\n ): Promise<LivenessVideoResult> {\n const token = await this.currentToken('waitForLivenessResult');\n const interval = options.intervalMs ?? 1500;\n const deadline = Date.now() + (options.timeoutMs ?? 120_000);\n for (;;) {\n if (options.signal?.aborted) {\n throw new AttestIDError('Stopped waiting for the liveness result.', 'LIVENESS_ABORTED');\n }\n const r = await getLivenessStatus(this.config.apiUrl, token, verificationId, this.transport);\n if (r.status !== 'in_progress') return r;\n if (Date.now() + interval > deadline) {\n throw new AttestIDError('Timed out waiting for the liveness result.', 'LIVENESS_TIMEOUT');\n }\n await sleep(interval);\n }\n }\n\n /**\n * Retry the face check on a verification that failed only at liveness\n * (see canRetryLiveness()), without uploading the ID photo again. Returns a\n * fresh liveness session in the same shape as startVerification(): branch on\n * livenessProvider() and run that flow again, then completeVerification().\n *\n * The API refuses (KYCError, status 409/410) for spoof, face-match or fraud\n * failures, once the retry cap is used up, or too long after the upload.\n * Start a new verification in that case.\n */\n async retryLiveness(verificationId: string): Promise<StartKYCResponse> {\n if (this.tokenUserId === null) {\n throw new AttestIDError('retryLiveness() must be called after startVerification()', 'AUTH_ERROR');\n }\n // getSdkToken rather than currentToken: a retry can come minutes after\n // startVerification(), so refresh the token if it's about to expire.\n const token = await this.getSdkToken(this.tokenUserId);\n return retryLiveness(this.config.apiUrl, token, verificationId, this.transport, this.livenessFlows);\n }\n\n /**\n * Complete the KYC verification after the liveness check passes.\n * Returns the final result with document info, face-match score, and fraud flags.\n * Must be called after startVerification() in the same client instance —\n * it reuses that call's SDK token rather than minting a new one.\n */\n async completeVerification(verificationId: string, deviceId?: string): Promise<KYCResult> {\n const token = await this.currentToken('completeVerification');\n return completeKYC(this.config.apiUrl, token, verificationId, deviceId, this.transport);\n }\n\n /** The Cognito region. */\n get region(): string {\n return this.config.region;\n }\n\n /**\n * The Cognito identity pool ID if provided in config.\n * Prefer getLivenessCredentials() to fetch it dynamically from the backend.\n */\n get identityPoolId(): string | undefined {\n return this.config.identityPoolId;\n }\n}\n","/**\n * AttestID Vision liveness capture, browser side.\n *\n * The API hands out a random, single-use script per attempt (see\n * getLivenessChallenge): hold still → a sequence of full-screen colour\n * flashes → a few actions (turn, blink, smile, open mouth). This module\n * records the front camera with MediaRecorder while the UI plays that script.\n * The server then checks that the video really follows it: the right actions\n * at the right times, the screen colours reflected on real skin in the right\n * order, a pulse, no print/screen/mask, the same face throughout. A\n * pre-recorded or generated video can't know the script in advance.\n *\n * Timing matters. The server reads the script against the video's own\n * timestamps, so the script clock starts on MediaRecorder's `start` event,\n * the moment frame 0 is captured, and the UI must show each segment exactly\n * when onSegment says so. Browsers keep front-camera *recordings*\n * un-mirrored (only a preview is mirrored, via CSS), so uploads use\n * mirrored=false.\n */\nimport { AttestIDError, CameraError } from './errors';\nimport type { LivenessAction, LivenessSegment } from './types';\n\n/** Default user-facing prompts. Override per language in your own UI. */\nexport const LIVENESS_PROMPTS: Record<LivenessAction | 'steady' | 'flash', string> = {\n steady: 'Look straight at the camera and hold still',\n flash: 'Keep looking at the screen',\n turn_left: 'Slowly turn your head to your left',\n turn_right: 'Slowly turn your head to your right',\n blink: 'Blink',\n smile: 'Smile',\n open_mouth: 'Open your mouth',\n};\n\n/** CSS colours for flash segments: pure, full-brightness primaries. */\nexport const FLASH_CSS: Record<NonNullable<LivenessSegment['color']>, string> = {\n red: '#ff0000',\n green: '#00ff00',\n blue: '#0000ff',\n};\n\n/** Recording keeps running this long past the script, so the last action isn't cut off. */\nexport const RECORDING_TAIL_MS = 500;\n\nexport function scriptDurationMs(script: LivenessSegment[]): number {\n return script.reduce((max, s) => Math.max(max, s.end_ms), 0);\n}\n\nexport function segmentAt(script: LivenessSegment[], elapsedMs: number): LivenessSegment | undefined {\n return script.find(s => elapsedMs >= s.start_ms && elapsedMs < s.end_ms);\n}\n\nexport function promptFor(segment: LivenessSegment | undefined): string {\n if (!segment) return '';\n if (segment.kind === 'challenge' && segment.action) return LIVENESS_PROMPTS[segment.action];\n return LIVENESS_PROMPTS[segment.kind === 'flash' ? 'flash' : 'steady'];\n}\n\n// MP4 first: Safari (incl. every iOS browser) only records MP4, and Chrome's\n// MP4 is the most widely decodable. WebM for Firefox and older Chromium.\nconst MIME_CANDIDATES = [\n 'video/mp4;codecs=avc1',\n 'video/mp4',\n 'video/webm;codecs=vp8',\n 'video/webm;codecs=vp9',\n 'video/webm',\n];\n\nexport function pickRecorderMimeType(): string | undefined {\n const MR = (globalThis as { MediaRecorder?: typeof MediaRecorder }).MediaRecorder;\n if (!MR) return undefined;\n if (typeof MR.isTypeSupported !== 'function') return '';\n return MIME_CANDIDATES.find(m => MR.isTypeSupported(m));\n}\n\nexport function livenessCaptureSupported(): boolean {\n const md = (globalThis as { navigator?: Navigator }).navigator?.mediaDevices;\n return !!md?.getUserMedia && pickRecorderMimeType() !== undefined;\n}\n\n/** Front camera at a size the server analyses well (≥ 360p short side, ~30 fps), video only. */\nexport async function openFrontCamera(): Promise<MediaStream> {\n try {\n return await navigator.mediaDevices.getUserMedia({\n audio: false,\n video: {\n facingMode: 'user',\n width: { ideal: 640 },\n height: { ideal: 480 },\n frameRate: { ideal: 30, min: 15 },\n },\n });\n } catch {\n throw new CameraError('Camera access denied. Grant permission and try again.');\n }\n}\n\n// ── Pre-capture lighting check ──────────────────────────────────────────────\n//\n// The same idea as AWS Face Liveness, Onfido and Smile ID: check the light\n// before recording and coach the user, instead of failing after the whole\n// script. In dim light a webcam lengthens its exposure and drops to 10–15 fps\n// whatever getUserMedia asked for, and the server's liveness layers all\n// degrade with it. Thresholds mirror the server (low_light_max_fps,\n// low_light_face_luma in attestid_vision's config).\nexport const MIN_GOOD_FPS = 20;\nexport const MIN_GOOD_BRIGHTNESS = 60;\nexport const MAX_GOOD_BRIGHTNESS = 230;\n\nexport type CaptureIssue = 'too_dark' | 'too_bright' | 'low_frame_rate';\n\nexport interface CaptureConditions {\n /** Frames per second the camera actually delivered, or null if the browser can't tell. */\n fps: number | null;\n /** Mean luminance (0–255) of the centre of the frame, where the face sits. */\n brightness: number | null;\n issue: CaptureIssue | null;\n}\n\nexport const CAPTURE_ISSUE_PROMPTS: Record<CaptureIssue, string> = {\n too_dark: 'It’s too dark. Move somewhere brighter or face a light.',\n low_frame_rate: 'It’s too dark. Move somewhere brighter or face a light.',\n too_bright: 'It’s too bright. Move out of direct sunlight.',\n};\n\n/** Mean BT.601 luminance of RGBA pixel data. */\nexport function meanLuma(rgba: ArrayLike<number>): number {\n let sum = 0;\n const n = Math.floor(rgba.length / 4);\n for (let i = 0; i < n * 4; i += 4) sum += 0.299 * rgba[i] + 0.587 * rgba[i + 1] + 0.114 * rgba[i + 2];\n return n ? sum / n : 0;\n}\n\nexport function classifyConditions(fps: number | null, brightness: number | null): CaptureIssue | null {\n if (brightness !== null && brightness < MIN_GOOD_BRIGHTNESS) return 'too_dark';\n if (brightness !== null && brightness > MAX_GOOD_BRIGHTNESS) return 'too_bright';\n if (fps !== null && fps < MIN_GOOD_FPS) return 'low_frame_rate';\n return null;\n}\n\ntype FrameCallbackVideo = HTMLVideoElement & {\n requestVideoFrameCallback?: (cb: (now: number, meta: { presentedFrames: number }) => void) => number;\n cancelVideoFrameCallback?: (handle: number) => void;\n};\n\nfunction measureFps(video: FrameCallbackVideo, sampleMs: number): Promise<number | null> {\n if (typeof video.requestVideoFrameCallback !== 'function') return Promise.resolve(null);\n return new Promise(resolve => {\n let first: { t: number; frames: number } | null = null;\n let last: { t: number; frames: number } | null = null;\n let handle = 0;\n const onFrame = (now: number, meta: { presentedFrames: number }) => {\n if (!first) first = { t: now, frames: meta.presentedFrames };\n last = { t: now, frames: meta.presentedFrames };\n handle = video.requestVideoFrameCallback!(onFrame);\n };\n handle = video.requestVideoFrameCallback!(onFrame);\n setTimeout(() => {\n video.cancelVideoFrameCallback?.(handle);\n if (!first || !last || last.t <= first.t) return resolve(null);\n resolve((1000 * (last.frames - first.frames)) / (last.t - first.t));\n }, sampleMs);\n });\n}\n\nfunction measureBrightness(video: HTMLVideoElement): number | null {\n const w = video.videoWidth;\n const h = video.videoHeight;\n if (!w || !h || typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = 64;\n canvas.height = 48;\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) return null;\n // Centre half of the frame: roughly the oval the face is asked to fill.\n ctx.drawImage(video, w / 4, h / 4, w / 2, h / 2, 0, 0, 64, 48);\n try {\n return meanLuma(ctx.getImageData(0, 0, 64, 48).data);\n } catch {\n return null;\n }\n}\n\n/**\n * Measure the live preview for `sampleMs`: delivered frame rate and the\n * brightness where the face sits. `video` must be playing the camera stream.\n */\nexport async function checkCaptureConditions(video: HTMLVideoElement, sampleMs = 1000): Promise<CaptureConditions> {\n const fps = await measureFps(video, sampleMs);\n const brightness = measureBrightness(video);\n return { fps, brightness, issue: classifyConditions(fps, brightness) };\n}\n\nexport interface LivenessRecording {\n video: Blob;\n mimeType: string;\n durationMs: number;\n}\n\nexport interface RecordLivenessOptions {\n /** Called whenever the active segment changes (undefined in the trailing tail). Drive your UI from this. */\n onSegment?: (segment: LivenessSegment | undefined, elapsedMs: number) => void;\n /** Called on every animation frame with the elapsed time, e.g. for a progress bar. */\n onTick?: (elapsedMs: number, totalMs: number) => void;\n /** Abort recording; the promise rejects with code LIVENESS_ABORTED. */\n signal?: AbortSignal;\n}\n\n/**\n * Record `stream` while playing `script`. Resolves with the video once the\n * script (plus a short tail) has finished. Does not stop the stream's tracks;\n * the caller owns the camera.\n */\nexport function recordLiveness(\n stream: MediaStream,\n script: LivenessSegment[],\n options: RecordLivenessOptions = {},\n): Promise<LivenessRecording> {\n const mimeType = pickRecorderMimeType();\n if (mimeType === undefined) {\n return Promise.reject(\n new AttestIDError('This browser cannot record video (MediaRecorder unavailable).', 'LIVENESS_UNSUPPORTED'),\n );\n }\n const total = scriptDurationMs(script);\n const stopAt = total + RECORDING_TAIL_MS;\n\n return new Promise((resolve, reject) => {\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(stream, {\n ...(mimeType ? { mimeType } : {}),\n videoBitsPerSecond: 2_500_000,\n });\n } catch (e) {\n reject(new AttestIDError(`Could not start recording: ${String(e)}`, 'LIVENESS_UNSUPPORTED'));\n return;\n }\n\n const chunks: Blob[] = [];\n let t0 = 0;\n let current: LivenessSegment | undefined | null = null; // null = not started yet\n let raf = 0;\n let aborted = false;\n\n const now = () => (globalThis.performance?.now?.() ?? Date.now());\n const schedule = (fn: () => void) =>\n typeof requestAnimationFrame === 'function' ? requestAnimationFrame(fn) : (setTimeout(fn, 16) as unknown as number);\n const cancel = (id: number) =>\n typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame(id) : clearTimeout(id);\n\n const tick = () => {\n const elapsed = now() - t0;\n const seg = segmentAt(script, elapsed);\n if (seg !== current) {\n current = seg;\n options.onSegment?.(seg, elapsed);\n }\n options.onTick?.(Math.min(elapsed, total), total);\n if (elapsed >= stopAt) {\n if (recorder.state !== 'inactive') recorder.stop();\n return;\n }\n raf = schedule(tick);\n };\n\n const onAbort = () => {\n aborted = true;\n cancel(raf);\n if (recorder.state !== 'inactive') recorder.stop();\n reject(new AttestIDError('Liveness recording was cancelled.', 'LIVENESS_ABORTED'));\n };\n if (options.signal?.aborted) {\n onAbort();\n return;\n }\n options.signal?.addEventListener('abort', onAbort, { once: true });\n\n recorder.ondataavailable = e => {\n if (e.data && e.data.size > 0) chunks.push(e.data);\n };\n recorder.onerror = () => {\n cancel(raf);\n reject(new AttestIDError('Recording failed.', 'LIVENESS_RECORDING_FAILED'));\n };\n recorder.onstart = () => {\n t0 = now();\n raf = schedule(tick);\n };\n recorder.onstop = () => {\n cancel(raf);\n options.signal?.removeEventListener('abort', onAbort);\n if (aborted) return;\n const type = recorder.mimeType || mimeType || 'video/webm';\n const video = new Blob(chunks, { type });\n if (video.size === 0) {\n reject(new AttestIDError('Recording produced no video.', 'LIVENESS_RECORDING_FAILED'));\n return;\n }\n resolve({ video, mimeType: type, durationMs: Math.round(now() - t0) });\n };\n\n // One chunk per second keeps memory flat on long-ish recordings and means\n // a mid-recording tab freeze still leaves usable data.\n recorder.start(1000);\n });\n}\n","import { CameraError } from './errors';\nimport type { IdType } from './types';\nimport { DOC_ASPECT } from './utils';\n\nexport interface CameraSession {\n stream: MediaStream;\n captureFrame: (frameEl: HTMLElement, videoEl: HTMLVideoElement) => Promise<File>;\n stop: () => void;\n}\n\nexport async function openCameraSession(idType: IdType): Promise<CameraSession> {\n let stream: MediaStream;\n try {\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n facingMode: 'environment',\n // 1080p is already well above what OCR needs for a card-sized\n // document — asking for 4K (the previous ideal) made the browser\n // drive a much heavier live-preview + capture pipeline than\n // necessary, which is a real crash risk on lower-end/older phones.\n width: { ideal: 1920 },\n height: { ideal: 1080 },\n // @ts-expect-error — focusMode not in all TS lib versions\n focusMode: 'continuous',\n },\n });\n } catch (e) {\n throw new CameraError('Camera access denied. Grant permission and try again.');\n }\n\n function captureFrame(frameEl: HTMLElement, videoEl: HTMLVideoElement): Promise<File> {\n return new Promise((resolve, reject) => {\n const vRect = videoEl.getBoundingClientRect();\n const fRect = frameEl.getBoundingClientRect();\n\n // `.camera-preview` renders with `object-fit: cover`: the video is\n // scaled *uniformly* to fill its box and the overflow is cropped\n // centered — it is not stretched independently on x/y. Map the\n // on-screen focus-frame rect back to native video pixels through that\n // same scale + centered-crop transform so the capture matches exactly\n // what's visible inside the frame.\n const scale = Math.max(vRect.width / videoEl.videoWidth, vRect.height / videoEl.videoHeight);\n const offsetX = (videoEl.videoWidth * scale - vRect.width) / 2;\n const offsetY = (videoEl.videoHeight * scale - vRect.height) / 2;\n\n const sx = (offsetX + (fRect.left - vRect.left)) / scale;\n const sy = (offsetY + (fRect.top - vRect.top)) / scale;\n const sw = fRect.width / scale;\n const sh = fRect.height / scale;\n\n const canvas = document.createElement('canvas');\n canvas.width = sw;\n canvas.height = sh;\n canvas.getContext('2d')!.drawImage(videoEl, sx, sy, sw, sh, 0, 0, sw, sh);\n canvas.toBlob(blob => {\n if (!blob) { reject(new CameraError('Failed to capture frame')); return; }\n resolve(new File([blob], 'id-photo.jpg', { type: 'image/jpeg' }));\n }, 'image/jpeg', 0.95);\n });\n }\n\n function stop() {\n stream.getTracks().forEach(t => t.stop());\n }\n\n void idType; // aspect ratio is consumed by the UI layer via DOC_ASPECT\n return { stream, captureFrame, stop };\n}\n\nexport { DOC_ASPECT };\n","/**\n * Verify what AttestID signs. Meant for **your backend**, though it runs\n * anywhere WebCrypto exists (Node 19+, browsers, Deno, edge runtimes).\n *\n * verifyAttestation() the `attestation` on a KYC result (ES256 JWS, SECURITY.md P1)\n * verifyWebhook() an incoming webhook: the HMAC `AttestID-Signature`\n * (any of several secrets, for no-downtime rotation, P4)\n * and/or the asymmetric `AttestID-JWS` header (P1)\n *\n * Public keys come from `${apiUrl}/.well-known/jwks.json` and are cached for\n * 10 minutes, refetched once when an unknown `kid` shows up (key rotation).\n */\nimport { AttestIDError } from './errors';\nimport {\n base64UrlToBytes,\n buf,\n bytesToBase64Url,\n canonicalJson,\n hexToBytes,\n sha256,\n subtle,\n utf8,\n} from './encoding';\nimport type { FetchLike } from './http';\nimport type { AttestationClaims, DocumentInfo } from './types';\n\nexport class SignatureError extends AttestIDError {\n constructor(message: string) {\n super(message, 'SIGNATURE_INVALID');\n this.name = 'SignatureError';\n }\n}\n\ninterface Jwk {\n kty: string;\n crv: string;\n x: string;\n y: string;\n kid: string;\n alg?: string;\n}\n\nconst JWKS_TTL_MS = 10 * 60_000;\nconst jwksCache = new Map<string, { at: number; keys: Map<string, CryptoKey> }>();\n\n/** @internal test hook */\nexport function _clearJwksCache(): void {\n jwksCache.clear();\n}\n\nasync function loadJwks(apiUrl: string, fetchImpl: FetchLike): Promise<Map<string, CryptoKey>> {\n const res = await fetchImpl(`${apiUrl.replace(/\\/$/, '')}/.well-known/jwks.json`);\n if (!res.ok) throw new AttestIDError(`Could not fetch AttestID JWKS (HTTP ${res.status})`, 'JWKS_UNAVAILABLE');\n const { keys } = (await res.json()) as { keys: Jwk[] };\n const out = new Map<string, CryptoKey>();\n for (const k of keys ?? []) {\n if (k.kty !== 'EC' || k.crv !== 'P-256') continue;\n out.set(\n k.kid,\n await subtle().importKey(\n 'jwk',\n { kty: 'EC', crv: 'P-256', x: k.x, y: k.y, ext: true },\n { name: 'ECDSA', namedCurve: 'P-256' },\n false,\n ['verify'],\n ),\n );\n }\n jwksCache.set(apiUrl, { at: Date.now(), keys: out });\n return out;\n}\n\nasync function keyFor(apiUrl: string, kid: string, fetchImpl: FetchLike): Promise<CryptoKey> {\n const cached = jwksCache.get(apiUrl);\n const fresh = !cached || Date.now() - cached.at >= JWKS_TTL_MS;\n let keys = fresh ? await loadJwks(apiUrl, fetchImpl) : cached.keys;\n if (!keys.has(kid) && !fresh) keys = await loadJwks(apiUrl, fetchImpl); // key rotated since caching\n const key = keys.get(kid);\n if (!key) throw new SignatureError(`Unknown AttestID signing key id \"${kid}\"`);\n return key;\n}\n\nfunction parseJson<T>(bytes: Uint8Array, what: string): T {\n try {\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n } catch {\n throw new SignatureError(`Malformed ${what}`);\n }\n}\n\nasync function verifyCompact(\n headerB64: string,\n payloadB64: string,\n sigB64: string,\n opts: { apiUrl: string; fetch?: FetchLike },\n): Promise<Record<string, unknown>> {\n const header = parseJson<Record<string, unknown>>(base64UrlToBytes(headerB64), 'JWS header');\n // Pin the algorithm: never let the token choose (blocks alg=none / HS256 confusion).\n if (header.alg !== 'ES256' || typeof header.kid !== 'string') {\n throw new SignatureError('Unexpected JWS algorithm or missing kid');\n }\n const key = await keyFor(opts.apiUrl, header.kid, opts.fetch ?? fetch);\n const ok = await subtle().verify(\n { name: 'ECDSA', hash: 'SHA-256' },\n key,\n buf(base64UrlToBytes(sigB64)),\n buf(utf8(`${headerB64}.${payloadB64}`)),\n );\n if (!ok) throw new SignatureError('Signature does not verify');\n return header;\n}\n\nexport interface VerifyAttestationOptions {\n /** Your AttestID API base URL. The JWKS is fetched from here. */\n apiUrl: string;\n /** Require the attestation to be for this verification id. */\n verificationId?: string;\n /** Require it to belong to this company id. */\n companyId?: string;\n /**\n * The `document` block you received alongside the attestation. When given,\n * its canonical SHA-256 must match `document_sha256`, which proves the\n * identity data wasn't altered after AttestID issued the result.\n */\n document?: DocumentInfo | Record<string, unknown> | null;\n /** Custom fetch (e.g. one configured for mTLS). */\n fetch?: FetchLike;\n}\n\n/**\n * Verify a KYC result's `attestation` and return its signed claims. Throws\n * `SignatureError` if the signature, the expected ids or the document hash\n * don't match. Trust `claims.verified` / `claims.status`, which are signed,\n * over the unsigned response fields.\n */\nexport async function verifyAttestation(\n attestation: string,\n options: VerifyAttestationOptions,\n): Promise<AttestationClaims> {\n const parts = attestation.split('.');\n if (parts.length !== 3 || !parts[1]) throw new SignatureError('Malformed attestation');\n const header = await verifyCompact(parts[0], parts[1], parts[2], options);\n if (header.typ !== 'attestid-verification+jws') throw new SignatureError('Not an AttestID verification attestation');\n const claims = parseJson<AttestationClaims>(base64UrlToBytes(parts[1]), 'attestation payload');\n\n if (options.verificationId && claims.sub !== options.verificationId) {\n throw new SignatureError('Attestation is for a different verification');\n }\n if (options.companyId && claims.company_id !== options.companyId) {\n throw new SignatureError('Attestation is for a different company');\n }\n if (options.document !== undefined) {\n const expected = options.document\n ? bytesToBase64Url(await sha256(utf8(canonicalJson(options.document))))\n : null;\n if (claims.document_sha256 !== expected) {\n throw new SignatureError('Document data does not match the signed attestation');\n }\n }\n return claims;\n}\n\nexport interface VerifyWebhookOptions {\n /**\n * Your webhook secret(s). Pass both the new and the old secret during a\n * rotation; the delivery verifies if any one of them matches.\n */\n secrets?: string | string[];\n /** Your AttestID API base URL. Enables `AttestID-JWS` verification. */\n apiUrl?: string;\n /** Max age of the signed timestamp in seconds (replay window). Default 300. */\n toleranceSeconds?: number;\n fetch?: FetchLike;\n}\n\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\nfunction header(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === 'function') return (headers as Headers).get(name) ?? undefined;\n const rec = headers as Record<string, string | string[] | undefined>;\n const hit = Object.keys(rec).find(k => k.toLowerCase() === name.toLowerCase());\n const v = hit ? rec[hit] : undefined;\n return Array.isArray(v) ? v[0] : v;\n}\n\nasync function verifyHmac(body: Uint8Array, sigHeader: string, secrets: string[], tolerance: number): Promise<void> {\n const pairs = sigHeader.split(',').map(p => p.split('=', 2) as [string, string]);\n const t = pairs.find(([k]) => k === 't')?.[1];\n const sigs = pairs.filter(([k]) => k === 'v1').map(([, v]) => v);\n if (!t || !sigs.length) throw new SignatureError('Malformed AttestID-Signature header');\n if (Math.abs(Date.now() / 1000 - Number(t)) > tolerance) throw new SignatureError('Webhook timestamp outside tolerance (possible replay)');\n\n const message = new Uint8Array([...utf8(`${t}.`), ...body]);\n for (const secret of secrets) {\n const key = await subtle().importKey('raw', buf(utf8(secret)), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);\n for (const hex of sigs) {\n const sig = hexToBytes(hex);\n // subtle.verify is a constant-time comparison.\n if (sig && (await subtle().verify('HMAC', key, buf(sig), buf(message)))) return;\n }\n }\n throw new SignatureError('AttestID-Signature does not match any configured secret');\n}\n\n/**\n * Verify an incoming AttestID webhook. Pass the **raw** request body\n * (exactly the bytes received, before JSON parsing) and the request headers.\n * Checks the HMAC signature when `secrets` is given and the JWS signature\n * when `apiUrl` is given. At least one is required, and both is best.\n * Returns the parsed payload.\n */\nexport async function verifyWebhook<T = Record<string, unknown>>(\n rawBody: string | Uint8Array | ArrayBuffer,\n headers: HeaderSource,\n options: VerifyWebhookOptions,\n): Promise<T> {\n const body =\n typeof rawBody === 'string' ? utf8(rawBody) : rawBody instanceof Uint8Array ? rawBody : new Uint8Array(rawBody);\n const tolerance = options.toleranceSeconds ?? 300;\n const secrets = options.secrets === undefined ? [] : ([] as string[]).concat(options.secrets);\n if (!secrets.length && !options.apiUrl) {\n throw new AttestIDError('verifyWebhook needs `secrets`, `apiUrl`, or both', 'INVALID_ARGUMENT');\n }\n\n if (secrets.length) {\n const sig = header(headers, 'AttestID-Signature');\n if (!sig) throw new SignatureError('Missing AttestID-Signature header');\n await verifyHmac(body, sig, secrets, tolerance);\n }\n\n if (options.apiUrl) {\n const jws = header(headers, 'AttestID-JWS');\n if (!jws) throw new SignatureError('Missing AttestID-JWS header');\n const parts = jws.split('.');\n if (parts.length !== 3 || parts[1] !== '') throw new SignatureError('Malformed AttestID-JWS (expected detached JWS)');\n const h = await verifyCompact(parts[0], bytesToBase64Url(body), parts[2], { apiUrl: options.apiUrl, fetch: options.fetch });\n if (h.typ !== 'attestid-webhook+jws') throw new SignatureError('Not an AttestID webhook signature');\n if (typeof h.iat !== 'number' || Math.abs(Date.now() / 1000 - h.iat) > tolerance) {\n throw new SignatureError('Webhook JWS iat outside tolerance (possible replay)');\n }\n }\n\n return parseJson<T>(body, 'webhook body');\n}\n","/**\n * Best-effort phone/tablet detection. AttestIDVerifier gates ID document\n * capture on this: a desktop/laptop never gets a \"Take Photo\" button — it's\n * shown a QR/link handoff to continue on a phone instead. A phone's rear\n * camera focuses at card distance and is much harder to spoof than pointing\n * a document at a webcam, so this is a deliberate restriction, not just a\n * UX nicety.\n */\nexport function isMobileDevice(): boolean {\n if (typeof navigator === 'undefined') return false;\n\n // Chromium's User-Agent Client Hints: the most reliable signal when\n // present, since — unlike the UA string — it isn't routinely spoofed by\n // browser extensions or embedded webviews.\n const uaData = (navigator as Navigator & { userAgentData?: { mobile?: boolean } }).userAgentData;\n if (typeof uaData?.mobile === 'boolean') return uaData.mobile;\n\n const ua = navigator.userAgent || '';\n if (/Android|iPhone|iPod|IEMobile|BlackBerry|Opera Mini|Windows Phone/i.test(ua)) return true;\n\n // iPadOS 13+ identifies as \"Macintosh\" with no \"iPad\" token in the UA, but\n // — unlike an actual Mac — reports multi-touch support. That combination\n // is the standard way to tell the two apart.\n if (/iPad/i.test(ua) || (ua.includes('Macintosh') && navigator.maxTouchPoints > 1)) return true;\n\n // Generic touch-primary fallback for anything the checks above missed —\n // a coarse pointer with no hover capability is how a touchscreen-only\n // device (no mouse/trackpad) reports itself.\n if (typeof window !== 'undefined' && window.matchMedia?.('(pointer: coarse) and (hover: none)').matches) {\n return true;\n }\n\n return false;\n}\n","/* \n * QR Code generator library (TypeScript)\n * \n * Copyright (c) Project Nayuki. (MIT License)\n * https://www.nayuki.io/page/qr-code-generator-library\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy of\n * this software and associated documentation files (the \"Software\"), to deal in\n * the Software without restriction, including without limitation the rights to\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n * the Software, and to permit persons to whom the Software is furnished to do so,\n * subject to the following conditions:\n * - The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n * - The Software is provided \"as is\", without warranty of any kind, express or\n * implied, including but not limited to the warranties of merchantability,\n * fitness for a particular purpose and noninfringement. In no event shall the\n * authors or copyright holders be liable for any claim, damages or other\n * liability, whether in an action of contract, tort or otherwise, arising from,\n * out of or in connection with the Software or the use or other dealings in the\n * Software.\n */\n\n// Vendored verbatim from Nayuki's TypeScript QR Code generator library\n// (https://github.com/nayuki/QR-Code-generator, typescript-javascript/qrcodegen.ts)\n// for the mobile-handoff QR code — mechanically restructured only to drop the\n// dotted `namespace qrcodegen.QrCode` / `namespace qrcodegen.QrSegment` merge\n// syntax, which esbuild's declaration-file bundler (used by this package's\n// tsup build) can't emit .d.ts for. No logic was changed.\n\n\"use strict\";\n\n\n\ntype bit = number;\ntype byte = number;\ntype int = number;\n\n\n/*---- QR Code symbol class ----*/\n\n/* \n * A QR Code symbol, which is a type of two-dimension barcode.\n * Invented by Denso Wave and described in the ISO/IEC 18004 standard.\n * Instances of this class represent an immutable square grid of dark and light cells.\n * The class provides static factory functions to create a QR Code from text or binary data.\n * The class covers the QR Code Model 2 specification, supporting all versions (sizes)\n * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.\n * \n * Ways to create a QR Code object:\n * - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary().\n * - Mid level: Custom-make the list of segments and call QrCode.encodeSegments().\n * - Low level: Custom-make the array of data codeword bytes (including\n * segment headers and final padding, excluding error correction codewords),\n * supply the appropriate version number, and call the QrCode() constructor.\n * (Note that all ways require supplying the desired error correction level.)\n */\nexport class QrCode {\n\t\n\t/*-- Static factory functions (high level) --*/\n\t\n\t// Returns a QR Code representing the given Unicode text string at the given error correction level.\n\t// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer\n\t// Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible\n\t// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the\n\t// ecl argument if it can be done without increasing the version.\n\tpublic static encodeText(text: string, ecl: QrCode.Ecc): QrCode {\n\t\tconst segs: Array<QrSegment> = QrSegment.makeSegments(text);\n\t\treturn QrCode.encodeSegments(segs, ecl);\n\t}\n\t\n\t\n\t// Returns a QR Code representing the given binary data at the given error correction level.\n\t// This function always encodes using the binary segment mode, not any text mode. The maximum number of\n\t// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.\n\t// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.\n\tpublic static encodeBinary(data: Readonly<Array<byte>>, ecl: QrCode.Ecc): QrCode {\n\t\tconst seg: QrSegment = QrSegment.makeBytes(data);\n\t\treturn QrCode.encodeSegments([seg], ecl);\n\t}\n\t\n\t\n\t/*-- Static factory functions (mid level) --*/\n\t\n\t// Returns a QR Code representing the given segments with the given encoding parameters.\n\t// The smallest possible QR Code version within the given range is automatically\n\t// chosen for the output. Iff boostEcl is true, then the ECC level of the result\n\t// may be higher than the ecl argument if it can be done without increasing the\n\t// version. The mask number is either between 0 to 7 (inclusive) to force that\n\t// mask, or -1 to automatically choose an appropriate mask (which may be slow).\n\t// This function allows the user to create a custom sequence of segments that switches\n\t// between modes (such as alphanumeric and byte) to encode text in less space.\n\t// This is a mid-level API; the high-level API is encodeText() and encodeBinary().\n\tpublic static encodeSegments(segs: Readonly<Array<QrSegment>>, ecl: QrCode.Ecc,\n\t\t\tminVersion: int = 1, maxVersion: int = 40,\n\t\t\tmask: int = -1, boostEcl: boolean = true): QrCode {\n\t\t\n\t\tif (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)\n\t\t\t\t|| mask < -1 || mask > 7)\n\t\t\tthrow new RangeError(\"Invalid value\");\n\t\t\n\t\t// Find the minimal version number to use\n\t\tlet version: int;\n\t\tlet dataUsedBits: int;\n\t\tfor (version = minVersion; ; version++) {\n\t\t\tconst dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available\n\t\t\tconst usedBits: number = QrSegment.getTotalBits(segs, version);\n\t\t\tif (usedBits <= dataCapacityBits) {\n\t\t\t\tdataUsedBits = usedBits;\n\t\t\t\tbreak; // This version number is found to be suitable\n\t\t\t}\n\t\t\tif (version >= maxVersion) // All versions in the range could not fit the given data\n\t\t\t\tthrow new RangeError(\"Data too long\");\n\t\t}\n\t\t\n\t\t// Increase the error correction level while the data still fits in the current version number\n\t\tfor (const newEcl of [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]) { // From low to high\n\t\t\tif (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8)\n\t\t\t\tecl = newEcl;\n\t\t}\n\t\t\n\t\t// Concatenate all segments to create the data bit string\n\t\tlet bb: Array<bit> = []\n\t\tfor (const seg of segs) {\n\t\t\tappendBits(seg.mode.modeBits, 4, bb);\n\t\t\tappendBits(seg.numChars, seg.mode.numCharCountBits(version), bb);\n\t\t\tfor (const b of seg.getData())\n\t\t\t\tbb.push(b);\n\t\t}\n\t\tassert(bb.length == dataUsedBits);\n\t\t\n\t\t// Add terminator and pad up to a byte if applicable\n\t\tconst dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8;\n\t\tassert(bb.length <= dataCapacityBits);\n\t\tappendBits(0, Math.min(4, dataCapacityBits - bb.length), bb);\n\t\tappendBits(0, (8 - bb.length % 8) % 8, bb);\n\t\tassert(bb.length % 8 == 0);\n\t\t\n\t\t// Pad with alternating bytes until data capacity is reached\n\t\tfor (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11)\n\t\t\tappendBits(padByte, 8, bb);\n\t\t\n\t\t// Pack bits into bytes in big endian\n\t\tlet dataCodewords: Array<byte> = [];\n\t\twhile (dataCodewords.length * 8 < bb.length)\n\t\t\tdataCodewords.push(0);\n\t\tbb.forEach((b: bit, i: int) =>\n\t\t\tdataCodewords[i >>> 3] |= b << (7 - (i & 7)));\n\t\t\n\t\t// Create the QR Code object\n\t\treturn new QrCode(version, ecl, dataCodewords, mask);\n\t}\n\t\n\t\n\t/*-- Fields --*/\n\t\n\t// The width and height of this QR Code, measured in modules, between\n\t// 21 and 177 (inclusive). This is equal to version * 4 + 17.\n\tpublic readonly size: int;\n\t\n\t// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).\n\t// Even if a QR Code is created with automatic masking requested (mask = -1),\n\t// the resulting object still has a mask value between 0 and 7.\n\tpublic readonly mask: int;\n\t\n\t// The modules of this QR Code (false = light, true = dark).\n\t// Immutable after constructor finishes. Accessed through getModule().\n\tprivate readonly modules : Array<Array<boolean>> = [];\n\t\n\t// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.\n\tprivate readonly isFunction: Array<Array<boolean>> = [];\n\t\n\t\n\t/*-- Constructor (low level) and fields --*/\n\t\n\t// Creates a new QR Code with the given version number,\n\t// error correction level, data codeword bytes, and mask number.\n\t// This is a low-level API that most users should not use directly.\n\t// A mid-level API is the encodeSegments() function.\n\tpublic constructor(\n\t\t\t// The version number of this QR Code, which is between 1 and 40 (inclusive).\n\t\t\t// This determines the size of this barcode.\n\t\t\tpublic readonly version: int,\n\t\t\t\n\t\t\t// The error correction level used in this QR Code.\n\t\t\tpublic readonly errorCorrectionLevel: QrCode.Ecc,\n\t\t\t\n\t\t\tdataCodewords: Readonly<Array<byte>>,\n\t\t\t\n\t\t\tmsk: int) {\n\t\t\n\t\t// Check scalar arguments\n\t\tif (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION)\n\t\t\tthrow new RangeError(\"Version value out of range\");\n\t\tif (msk < -1 || msk > 7)\n\t\t\tthrow new RangeError(\"Mask value out of range\");\n\t\tthis.size = version * 4 + 17;\n\t\t\n\t\t// Initialize both grids to be size*size arrays of Boolean false\n\t\tlet row: Array<boolean> = [];\n\t\tfor (let i = 0; i < this.size; i++)\n\t\t\trow.push(false);\n\t\tfor (let i = 0; i < this.size; i++) {\n\t\t\tthis.modules .push(row.slice()); // Initially all light\n\t\t\tthis.isFunction.push(row.slice());\n\t\t}\n\t\t\n\t\t// Compute ECC, draw modules\n\t\tthis.drawFunctionPatterns();\n\t\tconst allCodewords: Array<byte> = this.addEccAndInterleave(dataCodewords);\n\t\tthis.drawCodewords(allCodewords);\n\t\t\n\t\t// Do masking\n\t\tif (msk == -1) { // Automatically choose best mask\n\t\t\tlet minPenalty: int = 1000000000;\n\t\t\tfor (let i = 0; i < 8; i++) {\n\t\t\t\tthis.applyMask(i);\n\t\t\t\tthis.drawFormatBits(i);\n\t\t\t\tconst penalty: int = this.getPenaltyScore();\n\t\t\t\tif (penalty < minPenalty) {\n\t\t\t\t\tmsk = i;\n\t\t\t\t\tminPenalty = penalty;\n\t\t\t\t}\n\t\t\t\tthis.applyMask(i); // Undoes the mask due to XOR\n\t\t\t}\n\t\t}\n\t\tassert(0 <= msk && msk <= 7);\n\t\tthis.mask = msk;\n\t\tthis.applyMask(msk); // Apply the final choice of mask\n\t\tthis.drawFormatBits(msk); // Overwrite old format bits\n\t\t\n\t\tthis.isFunction = [];\n\t}\n\t\n\t\n\t/*-- Accessor methods --*/\n\t\n\t// Returns the color of the module (pixel) at the given coordinates, which is false\n\t// for light or true for dark. The top left corner has the coordinates (x=0, y=0).\n\t// If the given coordinates are out of bounds, then false (light) is returned.\n\tpublic getModule(x: int, y: int): boolean {\n\t\treturn 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x];\n\t}\n\t\n\t\n\t/*-- Private helper methods for constructor: Drawing function modules --*/\n\t\n\t// Reads this object's version field, and draws and marks all function modules.\n\tprivate drawFunctionPatterns(): void {\n\t\t// Draw horizontal and vertical timing patterns\n\t\tfor (let i = 0; i < this.size; i++) {\n\t\t\tthis.setFunctionModule(6, i, i % 2 == 0);\n\t\t\tthis.setFunctionModule(i, 6, i % 2 == 0);\n\t\t}\n\t\t\n\t\t// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)\n\t\tthis.drawFinderPattern(3, 3);\n\t\tthis.drawFinderPattern(this.size - 4, 3);\n\t\tthis.drawFinderPattern(3, this.size - 4);\n\t\t\n\t\t// Draw numerous alignment patterns\n\t\tconst alignPatPos: Array<int> = this.getAlignmentPatternPositions();\n\t\tconst numAlign: int = alignPatPos.length;\n\t\tfor (let i = 0; i < numAlign; i++) {\n\t\t\tfor (let j = 0; j < numAlign; j++) {\n\t\t\t\t// Don't draw on the three finder corners\n\t\t\t\tif (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0))\n\t\t\t\t\tthis.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Draw configuration data\n\t\tthis.drawFormatBits(0); // Dummy mask value; overwritten later in the constructor\n\t\tthis.drawVersion();\n\t}\n\t\n\t\n\t// Draws two copies of the format bits (with its own error correction code)\n\t// based on the given mask and this object's error correction level field.\n\tprivate drawFormatBits(mask: int): void {\n\t\t// Calculate error correction code and pack bits\n\t\tconst data: int = this.errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3\n\t\tlet rem: int = data;\n\t\tfor (let i = 0; i < 10; i++)\n\t\t\trem = (rem << 1) ^ ((rem >>> 9) * 0x537);\n\t\tconst bits = (data << 10 | rem) ^ 0x5412; // uint15\n\t\tassert(bits >>> 15 == 0);\n\t\t\n\t\t// Draw first copy\n\t\tfor (let i = 0; i <= 5; i++)\n\t\t\tthis.setFunctionModule(8, i, getBit(bits, i));\n\t\tthis.setFunctionModule(8, 7, getBit(bits, 6));\n\t\tthis.setFunctionModule(8, 8, getBit(bits, 7));\n\t\tthis.setFunctionModule(7, 8, getBit(bits, 8));\n\t\tfor (let i = 9; i < 15; i++)\n\t\t\tthis.setFunctionModule(14 - i, 8, getBit(bits, i));\n\t\t\n\t\t// Draw second copy\n\t\tfor (let i = 0; i < 8; i++)\n\t\t\tthis.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));\n\t\tfor (let i = 8; i < 15; i++)\n\t\t\tthis.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));\n\t\tthis.setFunctionModule(8, this.size - 8, true); // Always dark\n\t}\n\t\n\t\n\t// Draws two copies of the version bits (with its own error correction code),\n\t// based on this object's version field, iff 7 <= version <= 40.\n\tprivate drawVersion(): void {\n\t\tif (this.version < 7)\n\t\t\treturn;\n\t\t\n\t\t// Calculate error correction code and pack bits\n\t\tlet rem: int = this.version; // version is uint6, in the range [7, 40]\n\t\tfor (let i = 0; i < 12; i++)\n\t\t\trem = (rem << 1) ^ ((rem >>> 11) * 0x1F25);\n\t\tconst bits: int = this.version << 12 | rem; // uint18\n\t\tassert(bits >>> 18 == 0);\n\t\t\n\t\t// Draw two copies\n\t\tfor (let i = 0; i < 18; i++) {\n\t\t\tconst color: boolean = getBit(bits, i);\n\t\t\tconst a: int = this.size - 11 + i % 3;\n\t\t\tconst b: int = Math.floor(i / 3);\n\t\t\tthis.setFunctionModule(a, b, color);\n\t\t\tthis.setFunctionModule(b, a, color);\n\t\t}\n\t}\n\t\n\t\n\t// Draws a 9*9 finder pattern including the border separator,\n\t// with the center module at (x, y). Modules can be out of bounds.\n\tprivate drawFinderPattern(x: int, y: int): void {\n\t\tfor (let dy = -4; dy <= 4; dy++) {\n\t\t\tfor (let dx = -4; dx <= 4; dx++) {\n\t\t\t\tconst dist: int = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm\n\t\t\t\tconst xx: int = x + dx;\n\t\t\t\tconst yy: int = y + dy;\n\t\t\t\tif (0 <= xx && xx < this.size && 0 <= yy && yy < this.size)\n\t\t\t\t\tthis.setFunctionModule(xx, yy, dist != 2 && dist != 4);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t// Draws a 5*5 alignment pattern, with the center module\n\t// at (x, y). All modules must be in bounds.\n\tprivate drawAlignmentPattern(x: int, y: int): void {\n\t\tfor (let dy = -2; dy <= 2; dy++) {\n\t\t\tfor (let dx = -2; dx <= 2; dx++)\n\t\t\t\tthis.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1);\n\t\t}\n\t}\n\t\n\t\n\t// Sets the color of a module and marks it as a function module.\n\t// Only used by the constructor. Coordinates must be in bounds.\n\tprivate setFunctionModule(x: int, y: int, isDark: boolean): void {\n\t\tthis.modules[y][x] = isDark;\n\t\tthis.isFunction[y][x] = true;\n\t}\n\t\n\t\n\t/*-- Private helper methods for constructor: Codewords and masking --*/\n\t\n\t// Returns a new byte string representing the given data with the appropriate error correction\n\t// codewords appended to it, based on this object's version and error correction level.\n\tprivate addEccAndInterleave(data: Readonly<Array<byte>>): Array<byte> {\n\t\tconst ver: int = this.version;\n\t\tconst ecl: QrCode.Ecc = this.errorCorrectionLevel;\n\t\tif (data.length != QrCode.getNumDataCodewords(ver, ecl))\n\t\t\tthrow new RangeError(\"Invalid argument\");\n\t\t\n\t\t// Calculate parameter numbers\n\t\tconst numBlocks: int = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];\n\t\tconst blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver];\n\t\tconst rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8);\n\t\tconst numShortBlocks: int = numBlocks - rawCodewords % numBlocks;\n\t\tconst shortBlockLen: int = Math.floor(rawCodewords / numBlocks);\n\t\t\n\t\t// Split data into blocks and append ECC to each block\n\t\tlet blocks: Array<Array<byte>> = [];\n\t\tconst rsDiv: Array<byte> = QrCode.reedSolomonComputeDivisor(blockEccLen);\n\t\tfor (let i = 0, k = 0; i < numBlocks; i++) {\n\t\t\tlet dat: Array<byte> = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));\n\t\t\tk += dat.length;\n\t\t\tconst ecc: Array<byte> = QrCode.reedSolomonComputeRemainder(dat, rsDiv);\n\t\t\tif (i < numShortBlocks)\n\t\t\t\tdat.push(0);\n\t\t\tblocks.push(dat.concat(ecc));\n\t\t}\n\t\t\n\t\t// Interleave (not concatenate) the bytes from every block into a single sequence\n\t\tlet result: Array<byte> = [];\n\t\tfor (let i = 0; i < blocks[0].length; i++) {\n\t\t\tblocks.forEach((block, j) => {\n\t\t\t\t// Skip the padding byte in short blocks\n\t\t\t\tif (i != shortBlockLen - blockEccLen || j >= numShortBlocks)\n\t\t\t\t\tresult.push(block[i]);\n\t\t\t});\n\t\t}\n\t\tassert(result.length == rawCodewords);\n\t\treturn result;\n\t}\n\t\n\t\n\t// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire\n\t// data area of this QR Code. Function modules need to be marked off before this is called.\n\tprivate drawCodewords(data: Readonly<Array<byte>>): void {\n\t\tif (data.length != Math.floor(QrCode.getNumRawDataModules(this.version) / 8))\n\t\t\tthrow new RangeError(\"Invalid argument\");\n\t\tlet i: int = 0; // Bit index into the data\n\t\t// Do the funny zigzag scan\n\t\tfor (let right = this.size - 1; right >= 1; right -= 2) { // Index of right column in each column pair\n\t\t\tif (right == 6)\n\t\t\t\tright = 5;\n\t\t\tfor (let vert = 0; vert < this.size; vert++) { // Vertical counter\n\t\t\t\tfor (let j = 0; j < 2; j++) {\n\t\t\t\t\tconst x: int = right - j; // Actual x coordinate\n\t\t\t\t\tconst upward: boolean = ((right + 1) & 2) == 0;\n\t\t\t\t\tconst y: int = upward ? this.size - 1 - vert : vert; // Actual y coordinate\n\t\t\t\t\tif (!this.isFunction[y][x] && i < data.length * 8) {\n\t\t\t\t\t\tthis.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7));\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t\t// If this QR Code has any remainder bits (0 to 7), they were assigned as\n\t\t\t\t\t// 0/false/light by the constructor and are left unchanged by this method\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tassert(i == data.length * 8);\n\t}\n\t\n\t\n\t// XORs the codeword modules in this QR Code with the given mask pattern.\n\t// The function modules must be marked and the codeword bits must be drawn\n\t// before masking. Due to the arithmetic of XOR, calling applyMask() with\n\t// the same mask value a second time will undo the mask. A final well-formed\n\t// QR Code needs exactly one (not zero, two, etc.) mask applied.\n\tprivate applyMask(mask: int): void {\n\t\tif (mask < 0 || mask > 7)\n\t\t\tthrow new RangeError(\"Mask value out of range\");\n\t\tfor (let y = 0; y < this.size; y++) {\n\t\t\tfor (let x = 0; x < this.size; x++) {\n\t\t\t\tlet invert: boolean;\n\t\t\t\tswitch (mask) {\n\t\t\t\t\tcase 0: invert = (x + y) % 2 == 0; break;\n\t\t\t\t\tcase 1: invert = y % 2 == 0; break;\n\t\t\t\t\tcase 2: invert = x % 3 == 0; break;\n\t\t\t\t\tcase 3: invert = (x + y) % 3 == 0; break;\n\t\t\t\t\tcase 4: invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 == 0; break;\n\t\t\t\t\tcase 5: invert = x * y % 2 + x * y % 3 == 0; break;\n\t\t\t\t\tcase 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;\n\t\t\t\t\tcase 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;\n\t\t\t\t\tdefault: throw new Error(\"Unreachable\");\n\t\t\t\t}\n\t\t\t\tif (!this.isFunction[y][x] && invert)\n\t\t\t\t\tthis.modules[y][x] = !this.modules[y][x];\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t// Calculates and returns the penalty score based on state of this QR Code's current modules.\n\t// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.\n\tprivate getPenaltyScore(): int {\n\t\tlet result: int = 0;\n\t\t\n\t\t// Adjacent modules in row having same color, and finder-like patterns\n\t\tfor (let y = 0; y < this.size; y++) {\n\t\t\tlet runColor = false;\n\t\t\tlet runX = 0;\n\t\t\tlet runHistory = [0,0,0,0,0,0,0];\n\t\t\tfor (let x = 0; x < this.size; x++) {\n\t\t\t\tif (this.modules[y][x] == runColor) {\n\t\t\t\t\trunX++;\n\t\t\t\t\tif (runX == 5)\n\t\t\t\t\t\tresult += QrCode.PENALTY_N1;\n\t\t\t\t\telse if (runX > 5)\n\t\t\t\t\t\tresult++;\n\t\t\t\t} else {\n\t\t\t\t\tthis.finderPenaltyAddHistory(runX, runHistory);\n\t\t\t\t\tif (!runColor)\n\t\t\t\t\t\tresult += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;\n\t\t\t\t\trunColor = this.modules[y][x];\n\t\t\t\t\trunX = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult += this.finderPenaltyTerminateAndCount(runColor, runX, runHistory) * QrCode.PENALTY_N3;\n\t\t}\n\t\t// Adjacent modules in column having same color, and finder-like patterns\n\t\tfor (let x = 0; x < this.size; x++) {\n\t\t\tlet runColor = false;\n\t\t\tlet runY = 0;\n\t\t\tlet runHistory = [0,0,0,0,0,0,0];\n\t\t\tfor (let y = 0; y < this.size; y++) {\n\t\t\t\tif (this.modules[y][x] == runColor) {\n\t\t\t\t\trunY++;\n\t\t\t\t\tif (runY == 5)\n\t\t\t\t\t\tresult += QrCode.PENALTY_N1;\n\t\t\t\t\telse if (runY > 5)\n\t\t\t\t\t\tresult++;\n\t\t\t\t} else {\n\t\t\t\t\tthis.finderPenaltyAddHistory(runY, runHistory);\n\t\t\t\t\tif (!runColor)\n\t\t\t\t\t\tresult += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;\n\t\t\t\t\trunColor = this.modules[y][x];\n\t\t\t\t\trunY = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult += this.finderPenaltyTerminateAndCount(runColor, runY, runHistory) * QrCode.PENALTY_N3;\n\t\t}\n\t\t\n\t\t// 2*2 blocks of modules having same color\n\t\tfor (let y = 0; y < this.size - 1; y++) {\n\t\t\tfor (let x = 0; x < this.size - 1; x++) {\n\t\t\t\tconst color: boolean = this.modules[y][x];\n\t\t\t\tif ( color == this.modules[y][x + 1] &&\n\t\t\t\t color == this.modules[y + 1][x] &&\n\t\t\t\t color == this.modules[y + 1][x + 1])\n\t\t\t\t\tresult += QrCode.PENALTY_N2;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Balance of dark and light modules\n\t\tlet dark: int = 0;\n\t\tfor (const row of this.modules)\n\t\t\tdark = row.reduce((sum, color) => sum + (color ? 1 : 0), dark);\n\t\tconst total: int = this.size * this.size; // Note that size is odd, so dark/total != 1/2\n\t\t// Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%\n\t\tconst k: int = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1;\n\t\tassert(0 <= k && k <= 9);\n\t\tresult += k * QrCode.PENALTY_N4;\n\t\tassert(0 <= result && result <= 2568888); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4\n\t\treturn result;\n\t}\n\t\n\t\n\t/*-- Private helper functions --*/\n\t\n\t// Returns an ascending list of positions of alignment patterns for this version number.\n\t// Each position is in the range [0,177), and are used on both the x and y axes.\n\t// This could be implemented as lookup table of 40 variable-length lists of integers.\n\tprivate getAlignmentPatternPositions(): Array<int> {\n\t\tif (this.version == 1)\n\t\t\treturn [];\n\t\telse {\n\t\t\tconst numAlign: int = Math.floor(this.version / 7) + 2;\n\t\t\tconst step: int = Math.floor((this.version * 8 + numAlign * 3 + 5) / (numAlign * 4 - 4)) * 2;\n\t\t\tlet result: Array<int> = [6];\n\t\t\tfor (let pos = this.size - 7; result.length < numAlign; pos -= step)\n\t\t\t\tresult.splice(1, 0, pos);\n\t\t\treturn result;\n\t\t}\n\t}\n\t\n\t\n\t// Returns the number of data bits that can be stored in a QR Code of the given version number, after\n\t// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.\n\t// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.\n\tprivate static getNumRawDataModules(ver: int): int {\n\t\tif (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION)\n\t\t\tthrow new RangeError(\"Version number out of range\");\n\t\tlet result: int = (16 * ver + 128) * ver + 64;\n\t\tif (ver >= 2) {\n\t\t\tconst numAlign: int = Math.floor(ver / 7) + 2;\n\t\t\tresult -= (25 * numAlign - 10) * numAlign - 55;\n\t\t\tif (ver >= 7)\n\t\t\t\tresult -= 36;\n\t\t}\n\t\tassert(208 <= result && result <= 29648);\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any\n\t// QR Code of the given version number and error correction level, with remainder bits discarded.\n\t// This stateless pure function could be implemented as a (40*4)-cell lookup table.\n\tprivate static getNumDataCodewords(ver: int, ecl: QrCode.Ecc): int {\n\t\treturn Math.floor(QrCode.getNumRawDataModules(ver) / 8) -\n\t\t\tQrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] *\n\t\t\tQrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];\n\t}\n\t\n\t\n\t// Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be\n\t// implemented as a lookup table over all possible parameter values, instead of as an algorithm.\n\tprivate static reedSolomonComputeDivisor(degree: int): Array<byte> {\n\t\tif (degree < 1 || degree > 255)\n\t\t\tthrow new RangeError(\"Degree out of range\");\n\t\t// Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.\n\t\t// For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].\n\t\tlet result: Array<byte> = [];\n\t\tfor (let i = 0; i < degree - 1; i++)\n\t\t\tresult.push(0);\n\t\tresult.push(1); // Start off with the monomial x^0\n\t\t\n\t\t// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),\n\t\t// and drop the highest monomial term which is always 1x^degree.\n\t\t// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).\n\t\tlet root = 1;\n\t\tfor (let i = 0; i < degree; i++) {\n\t\t\t// Multiply the current product by (x - r^i)\n\t\t\tfor (let j = 0; j < result.length; j++) {\n\t\t\t\tresult[j] = QrCode.reedSolomonMultiply(result[j], root);\n\t\t\t\tif (j + 1 < result.length)\n\t\t\t\t\tresult[j] ^= result[j + 1];\n\t\t\t}\n\t\t\troot = QrCode.reedSolomonMultiply(root, 0x02);\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.\n\tprivate static reedSolomonComputeRemainder(data: Readonly<Array<byte>>, divisor: Readonly<Array<byte>>): Array<byte> {\n\t\tlet result: Array<byte> = divisor.map(_ => 0);\n\t\tfor (const b of data) { // Polynomial division\n\t\t\tconst factor: byte = b ^ (result.shift() as byte);\n\t\t\tresult.push(0);\n\t\t\tdivisor.forEach((coef, i) =>\n\t\t\t\tresult[i] ^= QrCode.reedSolomonMultiply(coef, factor));\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result\n\t// are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.\n\tprivate static reedSolomonMultiply(x: byte, y: byte): byte {\n\t\tif (x >>> 8 != 0 || y >>> 8 != 0)\n\t\t\tthrow new RangeError(\"Byte out of range\");\n\t\t// Russian peasant multiplication\n\t\tlet z: int = 0;\n\t\tfor (let i = 7; i >= 0; i--) {\n\t\t\tz = (z << 1) ^ ((z >>> 7) * 0x11D);\n\t\t\tz ^= ((y >>> i) & 1) * x;\n\t\t}\n\t\tassert(z >>> 8 == 0);\n\t\treturn z as byte;\n\t}\n\t\n\t\n\t// Can only be called immediately after a light run is added, and\n\t// returns either 0, 1, or 2. A helper function for getPenaltyScore().\n\tprivate finderPenaltyCountPatterns(runHistory: Readonly<Array<int>>): int {\n\t\tconst n: int = runHistory[1];\n\t\tassert(n <= this.size * 3);\n\t\tconst core: boolean = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;\n\t\treturn (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)\n\t\t + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);\n\t}\n\t\n\t\n\t// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().\n\tprivate finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array<int>): int {\n\t\tif (currentRunColor) { // Terminate dark run\n\t\t\tthis.finderPenaltyAddHistory(currentRunLength, runHistory);\n\t\t\tcurrentRunLength = 0;\n\t\t}\n\t\tcurrentRunLength += this.size; // Add light border to final run\n\t\tthis.finderPenaltyAddHistory(currentRunLength, runHistory);\n\t\treturn this.finderPenaltyCountPatterns(runHistory);\n\t}\n\t\n\t\n\t// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().\n\tprivate finderPenaltyAddHistory(currentRunLength: int, runHistory: Array<int>): void {\n\t\tif (runHistory[0] == 0)\n\t\t\tcurrentRunLength += this.size; // Add light border to initial run\n\t\trunHistory.pop();\n\t\trunHistory.unshift(currentRunLength);\n\t}\n\t\n\t\n\t/*-- Constants and tables --*/\n\t\n\t// The minimum version number supported in the QR Code Model 2 standard.\n\tpublic static readonly MIN_VERSION: int = 1;\n\t// The maximum version number supported in the QR Code Model 2 standard.\n\tpublic static readonly MAX_VERSION: int = 40;\n\t\n\t// For use in getPenaltyScore(), when evaluating which mask is best.\n\tprivate static readonly PENALTY_N1: int = 3;\n\tprivate static readonly PENALTY_N2: int = 3;\n\tprivate static readonly PENALTY_N3: int = 40;\n\tprivate static readonly PENALTY_N4: int = 10;\n\t\n\tprivate static readonly ECC_CODEWORDS_PER_BLOCK: Array<Array<int>> = [\n\t\t// Version: (note that index 0 is for padding, and is set to an illegal value)\n\t\t//0, 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 Error correction level\n\t\t[-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low\n\t\t[-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium\n\t\t[-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile\n\t\t[-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High\n\t];\n\t\n\tprivate static readonly NUM_ERROR_CORRECTION_BLOCKS: Array<Array<int>> = [\n\t\t// Version: (note that index 0 is for padding, and is set to an illegal value)\n\t\t//0, 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 Error correction level\n\t\t[-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low\n\t\t[-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium\n\t\t[-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile\n\t\t[-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High\n\t];\n\t\n}\n\n\n// Appends the given number of low-order bits of the given value\n// to the given buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len.\nfunction appendBits(val: int, len: int, bb: Array<bit>): void {\n\tif (len < 0 || len > 31 || val >>> len != 0)\n\t\tthrow new RangeError(\"Value out of range\");\n\tfor (let i = len - 1; i >= 0; i--) // Append bit by bit\n\t\tbb.push((val >>> i) & 1);\n}\n\n\n// Returns true iff the i'th bit of x is set to 1.\nfunction getBit(x: int, i: int): boolean {\n\treturn ((x >>> i) & 1) != 0;\n}\n\n\n// Throws an exception if the given condition is false.\nfunction assert(cond: boolean): void {\n\tif (!cond)\n\t\tthrow new Error(\"Assertion error\");\n}\n\n\n\n/*---- Data segment class ----*/\n\n/* \n * A segment of character/binary/control data in a QR Code symbol.\n * Instances of this class are immutable.\n * The mid-level way to create a segment is to take the payload data\n * and call a static factory function such as QrSegment.makeNumeric().\n * The low-level way to create a segment is to custom-make the bit buffer\n * and call the QrSegment() constructor with appropriate values.\n * This segment class imposes no length restrictions, but QR Codes have restrictions.\n * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.\n * Any segment longer than this is meaningless for the purpose of generating QR Codes.\n */\nexport class QrSegment {\n\t\n\t/*-- Static factory functions (mid level) --*/\n\t\n\t// Returns a segment representing the given binary data encoded in\n\t// byte mode. All input byte arrays are acceptable. Any text string\n\t// can be converted to UTF-8 bytes and encoded as a byte mode segment.\n\tpublic static makeBytes(data: Readonly<Array<byte>>): QrSegment {\n\t\tlet bb: Array<bit> = []\n\t\tfor (const b of data)\n\t\t\tappendBits(b, 8, bb);\n\t\treturn new QrSegment(QrSegment.Mode.BYTE, data.length, bb);\n\t}\n\t\n\t\n\t// Returns a segment representing the given string of decimal digits encoded in numeric mode.\n\tpublic static makeNumeric(digits: string): QrSegment {\n\t\tif (!QrSegment.isNumeric(digits))\n\t\t\tthrow new RangeError(\"String contains non-numeric characters\");\n\t\tlet bb: Array<bit> = []\n\t\tfor (let i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration\n\t\t\tconst n: int = Math.min(digits.length - i, 3);\n\t\t\tappendBits(parseInt(digits.substring(i, i + n), 10), n * 3 + 1, bb);\n\t\t\ti += n;\n\t\t}\n\t\treturn new QrSegment(QrSegment.Mode.NUMERIC, digits.length, bb);\n\t}\n\t\n\t\n\t// Returns a segment representing the given text string encoded in alphanumeric mode.\n\t// The characters allowed are: 0 to 9, A to Z (uppercase only), space,\n\t// dollar, percent, asterisk, plus, hyphen, period, slash, colon.\n\tpublic static makeAlphanumeric(text: string): QrSegment {\n\t\tif (!QrSegment.isAlphanumeric(text))\n\t\t\tthrow new RangeError(\"String contains unencodable characters in alphanumeric mode\");\n\t\tlet bb: Array<bit> = []\n\t\tlet i: int;\n\t\tfor (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2\n\t\t\tlet temp: int = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;\n\t\t\ttemp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));\n\t\t\tappendBits(temp, 11, bb);\n\t\t}\n\t\tif (i < text.length) // 1 character remaining\n\t\t\tappendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6, bb);\n\t\treturn new QrSegment(QrSegment.Mode.ALPHANUMERIC, text.length, bb);\n\t}\n\t\n\t\n\t// Returns a new mutable list of zero or more segments to represent the given Unicode text string.\n\t// The result may use various segment modes and switch modes to optimize the length of the bit stream.\n\tpublic static makeSegments(text: string): Array<QrSegment> {\n\t\t// Select the most efficient segment encoding automatically\n\t\tif (text == \"\")\n\t\t\treturn [];\n\t\telse if (QrSegment.isNumeric(text))\n\t\t\treturn [QrSegment.makeNumeric(text)];\n\t\telse if (QrSegment.isAlphanumeric(text))\n\t\t\treturn [QrSegment.makeAlphanumeric(text)];\n\t\telse\n\t\t\treturn [QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))];\n\t}\n\t\n\t\n\t// Returns a segment representing an Extended Channel Interpretation\n\t// (ECI) designator with the given assignment value.\n\tpublic static makeEci(assignVal: int): QrSegment {\n\t\tlet bb: Array<bit> = []\n\t\tif (assignVal < 0)\n\t\t\tthrow new RangeError(\"ECI assignment value out of range\");\n\t\telse if (assignVal < (1 << 7))\n\t\t\tappendBits(assignVal, 8, bb);\n\t\telse if (assignVal < (1 << 14)) {\n\t\t\tappendBits(0b10, 2, bb);\n\t\t\tappendBits(assignVal, 14, bb);\n\t\t} else if (assignVal < 1000000) {\n\t\t\tappendBits(0b110, 3, bb);\n\t\t\tappendBits(assignVal, 21, bb);\n\t\t} else\n\t\t\tthrow new RangeError(\"ECI assignment value out of range\");\n\t\treturn new QrSegment(QrSegment.Mode.ECI, 0, bb);\n\t}\n\t\n\t\n\t// Tests whether the given string can be encoded as a segment in numeric mode.\n\t// A string is encodable iff each character is in the range 0 to 9.\n\tpublic static isNumeric(text: string): boolean {\n\t\treturn QrSegment.NUMERIC_REGEX.test(text);\n\t}\n\t\n\t\n\t// Tests whether the given string can be encoded as a segment in alphanumeric mode.\n\t// A string is encodable iff each character is in the following set: 0 to 9, A to Z\n\t// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.\n\tpublic static isAlphanumeric(text: string): boolean {\n\t\treturn QrSegment.ALPHANUMERIC_REGEX.test(text);\n\t}\n\t\n\t\n\t/*-- Constructor (low level) and fields --*/\n\t\n\t// Creates a new QR Code segment with the given attributes and data.\n\t// The character count (numChars) must agree with the mode and the bit buffer length,\n\t// but the constraint isn't checked. The given bit buffer is cloned and stored.\n\tpublic constructor(\n\t\t\t// The mode indicator of this segment.\n\t\t\tpublic readonly mode: QrSegment.Mode,\n\t\t\t\n\t\t\t// The length of this segment's unencoded data. Measured in characters for\n\t\t\t// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.\n\t\t\t// Always zero or positive. Not the same as the data's bit length.\n\t\t\tpublic readonly numChars: int,\n\t\t\t\n\t\t\t// The data bits of this segment. Accessed through getData().\n\t\t\tprivate readonly bitData: Array<bit>) {\n\t\t\n\t\tif (numChars < 0)\n\t\t\tthrow new RangeError(\"Invalid argument\");\n\t\tthis.bitData = bitData.slice(); // Make defensive copy\n\t}\n\t\n\t\n\t/*-- Methods --*/\n\t\n\t// Returns a new copy of the data bits of this segment.\n\tpublic getData(): Array<bit> {\n\t\treturn this.bitData.slice(); // Make defensive copy\n\t}\n\t\n\t\n\t// (Package-private) Calculates and returns the number of bits needed to encode the given segments at\n\t// the given version. The result is infinity if a segment has too many characters to fit its length field.\n\tpublic static getTotalBits(segs: Readonly<Array<QrSegment>>, version: int): number {\n\t\tlet result: number = 0;\n\t\tfor (const seg of segs) {\n\t\t\tconst ccbits: int = seg.mode.numCharCountBits(version);\n\t\t\tif (seg.numChars >= (1 << ccbits))\n\t\t\t\treturn Infinity; // The segment's length doesn't fit the field's bit width\n\t\t\tresult += 4 + ccbits + seg.bitData.length;\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns a new array of bytes representing the given string encoded in UTF-8.\n\tprivate static toUtf8ByteArray(str: string): Array<byte> {\n\t\tstr = encodeURI(str);\n\t\tlet result: Array<byte> = [];\n\t\tfor (let i = 0; i < str.length; i++) {\n\t\t\tif (str.charAt(i) != \"%\")\n\t\t\t\tresult.push(str.charCodeAt(i));\n\t\t\telse {\n\t\t\t\tresult.push(parseInt(str.substring(i + 1, i + 3), 16));\n\t\t\t\ti += 2;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t/*-- Constants --*/\n\t\n\t// Describes precisely all strings that are encodable in numeric mode.\n\tprivate static readonly NUMERIC_REGEX: RegExp = /^[0-9]*$/;\n\t\n\t// Describes precisely all strings that are encodable in alphanumeric mode.\n\tprivate static readonly ALPHANUMERIC_REGEX: RegExp = /^[A-Z0-9 $%*+.\\/:-]*$/;\n\t\n\t// The set of all legal characters in alphanumeric mode,\n\t// where each character value maps to the index in the string.\n\tprivate static readonly ALPHANUMERIC_CHARSET: string = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:\";\n\t\n}\n\n\n\n\n/*---- Public helper enumeration ----*/\n\nexport namespace QrCode {\n\t\n\ttype int = number;\n\t\n\t\n\t/* \n\t * The error correction level in a QR Code symbol. Immutable.\n\t */\n\texport class Ecc {\n\t\t\n\t\t/*-- Constants --*/\n\t\t\n\t\tpublic static readonly LOW = new Ecc(0, 1); // The QR Code can tolerate about 7% erroneous codewords\n\t\tpublic static readonly MEDIUM = new Ecc(1, 0); // The QR Code can tolerate about 15% erroneous codewords\n\t\tpublic static readonly QUARTILE = new Ecc(2, 3); // The QR Code can tolerate about 25% erroneous codewords\n\t\tpublic static readonly HIGH = new Ecc(3, 2); // The QR Code can tolerate about 30% erroneous codewords\n\t\t\n\t\t\n\t\t/*-- Constructor and fields --*/\n\t\t\n\t\tprivate constructor(\n\t\t\t// In the range 0 to 3 (unsigned 2-bit integer).\n\t\t\tpublic readonly ordinal: int,\n\t\t\t// (Package-private) In the range 0 to 3 (unsigned 2-bit integer).\n\t\t\tpublic readonly formatBits: int) {}\n\t\t\n\t}\n}\n\n\n\n/*---- Public helper enumeration ----*/\n\nexport namespace QrSegment {\n\t\n\ttype int = number;\n\t\n\t\n\t/* \n\t * Describes how a segment's data bits are interpreted. Immutable.\n\t */\n\texport class Mode {\n\t\t\n\t\t/*-- Constants --*/\n\t\t\n\t\tpublic static readonly NUMERIC = new Mode(0x1, [10, 12, 14]);\n\t\tpublic static readonly ALPHANUMERIC = new Mode(0x2, [ 9, 11, 13]);\n\t\tpublic static readonly BYTE = new Mode(0x4, [ 8, 16, 16]);\n\t\tpublic static readonly KANJI = new Mode(0x8, [ 8, 10, 12]);\n\t\tpublic static readonly ECI = new Mode(0x7, [ 0, 0, 0]);\n\t\t\n\t\t\n\t\t/*-- Constructor and fields --*/\n\t\t\n\t\tprivate constructor(\n\t\t\t// The mode indicator bits, which is a uint4 value (range 0 to 15).\n\t\t\tpublic readonly modeBits: int,\n\t\t\t// Number of character count bits for three different version ranges.\n\t\t\tprivate readonly numBitsCharCount: [int,int,int]) {}\n\t\t\n\t\t\n\t\t/*-- Method --*/\n\t\t\n\t\t// (Package-private) Returns the bit width of the character count field for a segment in\n\t\t// this mode in a QR Code at the given version number. The result is in the range [0, 16].\n\t\tpublic numCharCountBits(ver: int): int {\n\t\t\treturn this.numBitsCharCount[Math.floor((ver + 7) / 17)];\n\t\t}\n\t\t\n\t}\n}\n\n","import { QrCode } from './qrcodegen';\n\nexport type QrErrorCorrection = 'low' | 'medium' | 'quartile' | 'high';\n\nconst ECC: Record<QrErrorCorrection, QrCode.Ecc> = {\n low: QrCode.Ecc.LOW,\n medium: QrCode.Ecc.MEDIUM,\n quartile: QrCode.Ecc.QUARTILE,\n high: QrCode.Ecc.HIGH,\n};\n\n/**\n * Encodes `text` as a QR code and returns its module grid — `true` for a\n * dark module, indexed [y][x]. Rendering (SVG, canvas, ...) is left to the\n * caller; 'medium' error correction is plenty for a plain URL without\n * inflating the module count on longer ones.\n */\nexport function getQrMatrix(text: string, ecc: QrErrorCorrection = 'medium'): boolean[][] {\n const qr = QrCode.encodeText(text, ECC[ecc]);\n const rows: boolean[][] = [];\n for (let y = 0; y < qr.size; y++) {\n const row: boolean[] = [];\n for (let x = 0; x < qr.size; x++) row.push(qr.getModule(x, y));\n rows.push(row);\n }\n return rows;\n}\n","import type { IdType } from './types';\n\nconst PARAM_TOKEN = 'aid_token';\nconst PARAM_TS = 'aid_ts';\nconst PARAM_USER = 'aid_user';\nconst PARAM_ID_TYPE = 'aid_doc';\nconst PARAM_CONSENT = 'aid_consent';\n\nconst VALID_ID_TYPES: readonly IdType[] = ['national_id', 'passport', 'drivers_licence'];\n\n// How long a handoff link is trusted to carry consent/userId/idType forward.\n// Long enough to scan a QR code and switch devices, short enough that a\n// screenshotted or forwarded link can't be replayed much later to skip\n// consent on someone else's phone.\nconst HANDOFF_MAX_AGE_MS = 30 * 60 * 1000;\n\n/** Query string keys used to encode a mobile handoff — export so callers can strip them back out. */\nexport const HANDOFF_PARAM_KEYS = [PARAM_TOKEN, PARAM_TS, PARAM_USER, PARAM_ID_TYPE, PARAM_CONSENT] as const;\n\n/**\n * A random id unique to one QR/link render. Makes every mobile handoff URL\n * distinct instead of pointing at the site's bare domain, and gives a host\n * app something to correlate \"session started on desktop, continued on\n * phone\" in its own analytics/logs if it wants to. Not a security token —\n * nothing is looked up or authorized by it, so it needs no backend to mint.\n */\nexport function generateHandoffToken(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for older browsers without crypto.randomUUID — fine since this\n // is only ever used for link uniqueness/traceability, never as a credential.\n return Array.from({ length: 20 }, () => Math.floor(Math.random() * 36).toString(36)).join('');\n}\n\nexport interface HandoffParams {\n /** Raw token, present whenever the URL carried a handoff at all — independent of HANDOFF_MAX_AGE_MS, so a stale link still gets cleaned from the address bar. */\n token: string | null;\n userId: string | null;\n idType: IdType | null;\n /**\n * The original consentGivenAt from the device that generated this link,\n * carried forward so the phone can skip re-asking for consent — null if\n * absent OR the link is older than HANDOFF_MAX_AGE_MS.\n */\n consentGivenAt: Date | null;\n}\n\n/**\n * Appends handoff params to `baseUrl` so the phone that opens it can skip\n * re-entering what was already chosen on the desktop — including consent,\n * if it was already given there. Always stamps a generation timestamp so\n * the receiving end can refuse to honor a link that's too old.\n */\nexport function buildHandoffUrl(\n baseUrl: string,\n params: { token: string; userId?: string; idType?: IdType; consentGivenAt?: Date },\n): string {\n const url = new URL(baseUrl);\n url.searchParams.set(PARAM_TOKEN, params.token);\n url.searchParams.set(PARAM_TS, String(Date.now()));\n if (params.userId) url.searchParams.set(PARAM_USER, params.userId);\n if (params.idType) url.searchParams.set(PARAM_ID_TYPE, params.idType);\n if (params.consentGivenAt) url.searchParams.set(PARAM_CONSENT, params.consentGivenAt.toISOString());\n return url.toString();\n}\n\n/** Reads back whatever buildHandoffUrl() encoded, from the current page's URL. */\nexport function readHandoffParams(): HandoffParams {\n const empty: HandoffParams = { token: null, userId: null, idType: null, consentGivenAt: null };\n if (typeof window === 'undefined') return empty;\n\n const search = new URLSearchParams(window.location.search);\n const token = search.get(PARAM_TOKEN);\n if (!token) return empty;\n\n const ts = Number(search.get(PARAM_TS));\n const age = Date.now() - ts;\n if (!Number.isFinite(ts) || age < 0 || age > HANDOFF_MAX_AGE_MS) {\n // Too old (or malformed) to trust — still return the token so the\n // address bar gets cleaned up, but nothing else carries forward.\n return { ...empty, token };\n }\n\n const idTypeRaw = search.get(PARAM_ID_TYPE);\n const consentRaw = search.get(PARAM_CONSENT);\n const consentDate = consentRaw ? new Date(consentRaw) : null;\n\n return {\n token,\n userId: search.get(PARAM_USER),\n idType: VALID_ID_TYPES.includes(idTypeRaw as IdType) ? (idTypeRaw as IdType) : null,\n consentGivenAt: consentDate && !Number.isNaN(consentDate.getTime()) ? consentDate : null,\n };\n}\n\n/** The current page URL with any of our own handoff params removed — the safe base to re-encode a fresh link from. */\nexport function currentUrlWithoutHandoffParams(): string {\n if (typeof window === 'undefined') return '';\n const url = new URL(window.location.href);\n HANDOFF_PARAM_KEYS.forEach(key => url.searchParams.delete(key));\n return url.toString();\n}\n\n/** Strips our own handoff params from the visible address bar, without a page reload. Call once, after reading them. */\nexport function clearHandoffParamsFromAddressBar(): void {\n if (typeof window === 'undefined') return;\n const url = new URL(window.location.href);\n HANDOFF_PARAM_KEYS.forEach(key => url.searchParams.delete(key));\n window.history.replaceState(null, '', `${url.pathname}${url.search}${url.hash}`);\n}\n"],"mappings":"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","names":["header","shift","a","b","signCapture","sleep","header","dataCapacityBits","QrCode","QrSegment"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/index.ts","../../src/core/errors.ts","../../src/core/encoding.ts","../../src/core/http.ts","../../src/core/photoQuality.ts","../../src/core/api.ts","../../src/core/deviceKey.ts","../../src/core/utils.ts","../../src/core/client.ts","../../src/core/liveness.ts","../../src/core/camera.ts","../../src/core/verify.ts","../../src/core/device.ts","../../src/core/qrcodegen.ts","../../src/core/qrcode.ts","../../src/core/handoff.ts"],"sourcesContent":["export { AttestID } from './client';\nexport type { WaitForLivenessOptions } from './client';\nexport type { SubmitLivenessVideoOptions } from './api';\nexport {\n recordLiveness,\n openFrontCamera,\n livenessCaptureSupported,\n pickRecorderMimeType,\n scriptDurationMs,\n segmentAt,\n promptFor,\n LIVENESS_PROMPTS,\n FLASH_CSS,\n RECORDING_TAIL_MS,\n checkCaptureConditions,\n classifyConditions,\n meanLuma,\n CAPTURE_ISSUE_PROMPTS,\n MIN_GOOD_FPS,\n MIN_GOOD_BRIGHTNESS,\n MAX_GOOD_BRIGHTNESS,\n} from './liveness';\nexport type { LivenessRecording, RecordLivenessOptions, CaptureConditions, CaptureIssue } from './liveness';\nexport { openCameraSession, DOC_ASPECT } from './camera';\nexport { rawSessionId, faceMatchOutcome, canRetryLiveness, RETRYABLE_LIVENESS_REASONS } from './utils';\nexport { AttestIDError, AuthError, KYCError, CameraError, ValidationError } from './errors';\nexport { verifyAttestation, verifyWebhook, SignatureError } from './verify';\nexport type { VerifyAttestationOptions, VerifyWebhookOptions } from './verify';\nexport { canonicalJson } from './encoding';\nexport { checkPhotoQuality, checkBackPhotoQuality } from './photoQuality';\nexport type { BackPhotoQualityResult } from './photoQuality';\nexport { createSdkSession } from './api';\nexport { isMobileDevice } from './device';\nexport { getQrMatrix } from './qrcode';\nexport type { QrErrorCorrection } from './qrcode';\nexport {\n generateHandoffToken,\n buildHandoffUrl,\n readHandoffParams,\n currentUrlWithoutHandoffParams,\n clearHandoffParamsFromAddressBar,\n HANDOFF_PARAM_KEYS,\n} from './handoff';\nexport type { HandoffParams } from './handoff';\nexport type {\n AttestIDConfig,\n AttestIDConfigServer,\n AttestIDConfigBrowser,\n IdType,\n StartKYCOptions,\n CaptureSettings,\n DocumentBackPolicy,\n StartKYCResponse,\n LivenessCredentials,\n LivenessProvider,\n LivenessAction,\n LivenessSegment,\n LivenessChallenge,\n LivenessVideoResult,\n DocumentInfo,\n ChecksInfo,\n KYCResult,\n SdkSessionResponse,\n AttestationClaims,\n CaptureAssurance,\n} from './types';\nexport type { PhotoQualityResult } from './photoQuality';\n","export class AttestIDError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n ) {\n super(message);\n this.name = 'AttestIDError';\n }\n}\n\nexport class AuthError extends AttestIDError {\n constructor(message: string) {\n super(message, 'AUTH_ERROR', 401);\n this.name = 'AuthError';\n }\n}\n\nexport class KYCError extends AttestIDError {\n constructor(message: string, status?: number) {\n super(message, 'KYC_ERROR', status);\n this.name = 'KYCError';\n }\n}\n\nexport class CameraError extends AttestIDError {\n constructor(message: string) {\n super(message, 'CAMERA_ERROR');\n this.name = 'CameraError';\n }\n}\n\nexport class ValidationError extends AttestIDError {\n constructor(message: string) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n","/**\n * Small encoding/crypto helpers shared by device signing, attestation and\n * webhook verification. WebCrypto only (browsers, Node ≥ 19, Deno, edge\n * runtimes), with no Node-specific imports, so the package stays isomorphic.\n */\nimport { AttestIDError } from './errors';\n\nexport function subtle(): SubtleCrypto {\n const s = (globalThis as { crypto?: Crypto }).crypto?.subtle;\n if (!s) {\n throw new AttestIDError(\n 'WebCrypto (globalThis.crypto.subtle) is not available in this runtime. ' +\n 'Use a modern browser, Node.js 19+, or a runtime that provides WebCrypto.',\n 'CRYPTO_UNAVAILABLE',\n );\n }\n return s;\n}\n\nexport function hasWebCrypto(): boolean {\n return !!(globalThis as { crypto?: Crypto }).crypto?.subtle;\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let bin = '';\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n return btoa(bin);\n}\n\nexport function bytesToBase64Url(bytes: Uint8Array): string {\n return bytesToBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function base64UrlToBytes(s: string): Uint8Array {\n const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4);\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function hexToBytes(hex: string): Uint8Array | null {\n if (hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) return null;\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nexport const utf8 = (s: string): Uint8Array => new TextEncoder().encode(s);\n\n/**\n * Exactly this view's bytes as a standalone ArrayBuffer, for WebCrypto inputs.\n * TS 5.7+ types a Uint8Array's buffer as ArrayBufferLike, which BufferSource\n * rejects. `slice()` copies into a fresh ArrayBuffer and types the same on\n * every TS version, including the older ones that consumers compiling this\n * source (e.g. attestid-demo on TS 5.5) use.\n */\nexport function buf(bytes: Uint8Array): ArrayBuffer {\n return bytes.slice().buffer as ArrayBuffer;\n}\n\nexport async function sha256(data: Uint8Array): Promise<Uint8Array> {\n return new Uint8Array(await subtle().digest('SHA-256', buf(data)));\n}\n\n/**\n * Deterministic JSON matching the API's `canonical_json` (Python\n * `json.dumps(sort_keys=True, separators=(\",\", \":\"), ensure_ascii=False)`):\n * object keys sorted recursively, no whitespace. Used to recompute an\n * attestation's `document_sha256` from the document block you received.\n */\nexport function canonicalJson(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null';\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).filter(k => obj[k] !== undefined).sort();\n return `{${keys.map(k => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(',')}}`;\n}\n\n/** RFC 4122 v4 id: uses crypto.randomUUID when present, otherwise getRandomValues. */\nexport function randomId(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n const b = new Uint8Array(16);\n if (c?.getRandomValues) c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6] & 0x0f) | 0x40;\n b[8] = (b[8] & 0x3f) | 0x80;\n const h = bytesToHex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n","/**\n * POST with an `Idempotency-Key` and safe automatic retries.\n *\n * The API (SECURITY.md P3) stores the response to every keyed POST /v1/kyc/*\n * for 24 h. Retrying with the same key and the same body replays that\n * response instead of creating a second session, upload or billed completion.\n * That makes it safe to retry on a flaky mobile connection.\n *\n * Retry rules:\n * - network error (outcome unknown) → same key, same body. If the first\n * attempt actually landed, the server replays its response.\n * - 409 idempotency_request_in_progress → wait (Retry-After), same key + body.\n * This doesn't use up `attempts`: the first request is still running on\n * the server (e.g. OCR on a slow upload), so we keep asking until it\n * finishes or `inProgressWaitMs` runs out. Any other 409 is a real\n * conflict and is returned at once.\n * - 5xx / 429 → the server did NOT store anything, so the request is rebuilt\n * with a fresh key. That lets a one-time device-signing nonce be re-issued\n * instead of replayed (a replayed nonce would flag the capture as invalid).\n * - any other status → returned to the caller as-is.\n */\nimport { randomId } from './encoding';\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Base backoff in ms (doubled each retry). Default 500. */\n baseDelayMs?: number;\n /**\n * How long to keep waiting while the server says the same request is still\n * in progress (409 idempotency_request_in_progress). Default 120 000 ms.\n */\n inProgressWaitMs?: number;\n}\n\nexport interface BuiltRequest {\n body: BodyInit;\n headers?: Record<string, string>;\n}\n\nconst RETRY_FRESH_STATUSES = new Set([429, 500, 502, 503, 504]);\nconst MAX_RETRY_AFTER_MS = 10_000;\nconst DEFAULT_IN_PROGRESS_WAIT_MS = 120_000;\nconst IN_PROGRESS_ERROR = 'idempotency_request_in_progress';\n\nexport const defaultSleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));\nlet sleep = defaultSleep;\n/** @internal test hook */\nexport function _setSleep(fn: (ms: number) => Promise<void>): void {\n sleep = fn;\n}\n\nfunction retryAfterMs(res: Response, fallback: number): number {\n const raw = res.headers?.get?.('retry-after');\n const secs = raw ? Number(raw) : NaN;\n return Number.isFinite(secs) && secs >= 0 ? Math.min(secs * 1000, MAX_RETRY_AFTER_MS) : fallback;\n}\n\n/**\n * True only for the idempotency middleware's \"still running\" 409, not for a\n * route's own 409 conflict. Accepts both the `{detail: {error}}` envelope and\n * the older top-level `{error}` body. Reads a clone so the caller can still\n * read the body of a response we return.\n */\nasync function isInProgress(res: Response): Promise<boolean> {\n try {\n const body = (await (res.clone ? res.clone() : res).json()) as {\n error?: unknown;\n detail?: { error?: unknown };\n };\n return (body?.detail?.error ?? body?.error) === IN_PROGRESS_ERROR;\n } catch {\n return false;\n }\n}\n\nexport async function postIdempotent(\n fetchImpl: FetchLike,\n url: string,\n build: () => Promise<BuiltRequest> | BuiltRequest,\n options: RetryOptions = {},\n): Promise<Response> {\n const attempts = Math.max(1, options.attempts ?? 3);\n const base = options.baseDelayMs ?? 500;\n const inProgressBudget = options.inProgressWaitMs ?? DEFAULT_IN_PROGRESS_WAIT_MS;\n let inProgressWaited = 0;\n\n let key = randomId();\n let req = await build();\n\n for (let attempt = 1; ; attempt++) {\n const last = attempt >= attempts;\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: 'POST',\n headers: { ...req.headers, 'Idempotency-Key': key },\n body: req.body,\n });\n } catch (err) {\n if (last) throw err;\n await sleep(base * 2 ** (attempt - 1));\n continue; // outcome unknown → same key + body\n }\n\n if (res.ok) return res;\n if (res.status === 409) {\n if (!(await isInProgress(res))) return res; // a real conflict\n const delay = retryAfterMs(res, base);\n if (inProgressWaited + delay > inProgressBudget) return res;\n inProgressWaited += delay;\n await sleep(delay);\n attempt--; // waiting on the first request isn't a new attempt\n continue; // still running server-side → same key + body\n }\n if (last) return res;\n if (RETRY_FRESH_STATUSES.has(res.status)) {\n await sleep(retryAfterMs(res, base * 2 ** (attempt - 1)));\n key = randomId();\n req = await build();\n continue;\n }\n return res;\n }\n}\n","export interface PhotoQualityResult {\n ok: boolean;\n issues: string[];\n}\n\n// Captures come from a live getUserMedia() video frame (camera.ts), not a\n// native camera-app still — video frames are inherently softer (sensor\n// binning, continuous-autofocus hunting, slight handheld motion) even when\n// perfectly legible, so this must stay well below the value that would make\n// sense for a dedicated photo. 60 flagged plenty of genuinely fine captures.\nconst BLUR_THRESHOLD = 40;\nconst DARK_THRESHOLD = 35;\nconst BRIGHT_THRESHOLD = 225;\nconst MIN_FILE_BYTES = 30_000;\n\n// ISO 7810 ID-1 (national ID card / driver's licence) aspect ratio, ± tolerance\n// for perspective distortion in a handheld photo.\nconst ID1_ASPECT = 85.6 / 54;\nconst ID1_ASPECT_TOLERANCE = 0.15;\n\n// ── Face presence (best-effort, progressive enhancement) ────────────────────\n//\n// Scope: of every `failure_reason` the API can return, only the ones a client\n// can plausibly judge from the raw image *before* the backend's OCR/liveness/\n// fraud pipeline ever runs belong here — see docs/kyc-pipeline.md §9 in the\n// attestid_api repo for the authoritative Failed/Rejected/Fraudulent split.\n// That rules out anything requiring real OCR (document_not_recognized,\n// document_low_confidence, ocr_failed, id_number_unclear, name_unclear,\n// date_of_birth_unclear, expiry_date_unclear) or a facial-attribute model\n// (face_occluded, eyes_not_open, sunglasses_detected) — none of those are\n// worth bundling an ML/OCR library into every consumer's app for; the backend\n// classifies all of those Rejected precisely because the SDK can't check them.\n// Two exceptions don't need OCR to catch. document_is_passport /\n// document_is_not_passport is an aspect-ratio mismatch, which\n// checkDocumentIdentity() below flags client-side too (subtler cases still\n// fall through to the backend's dual-reader check). And a photo with no MRZ\n// at all (for a passport) or no printed fields at all (for a card), the\n// cases behind most mrz_failed / ocr_failed / document_not_recognized\n// results, is recognisable from text *layout* alone without reading a\n// character; see analyseTextLayout().\n//\n// face_detection_failed (0 faces) and multiple_people_detected (>1 faces) are\n// both Failed instead — a plain face *count* is exactly what the\n// browser-native Shape Detection API gives us for free, so both are flagged\n// here rather than wasting an API round trip on a mistake the client could\n// already see.\ninterface ShapeDetectionFace {\n boundingBox: { width: number; height: number };\n}\ninterface ShapeDetectionFaceDetector {\n detect(image: CanvasImageSource): Promise<ShapeDetectionFace[]>;\n}\ninterface ShapeDetectionFaceDetectorCtor {\n new (options?: { maxDetectedFaces?: number; fastMode?: boolean }): ShapeDetectionFaceDetector;\n}\n\n/**\n * Best-effort face detection via the browser-native Shape Detection API\n * (Chromium only as of writing). Returns `null` — never `[]` — when the API\n * is unsupported or the detector itself throws, so callers can tell \"could\n * not check\" apart from \"no face found\" and avoid blocking a legitimate\n * submission just because the browser lacks the API.\n *\n * maxDetectedFaces is set above 2 (rather than the minimum needed for a\n * plain presence check) because many national ID cards carry a \"ghost\n * image\" security watermark — a small duplicate of the primary photo\n * printed elsewhere on the card — which the detector happily reports as an\n * extra face. Filtering that out (see significantFaceCount() below) needs\n * to actually see it rather than have it silently crowd out real faces\n * against a cap of 2.\n */\nexport async function detectFaces(source: CanvasImageSource): Promise<ShapeDetectionFace[] | null> {\n const FaceDetectorCtor = (globalThis as { FaceDetector?: ShapeDetectionFaceDetectorCtor }).FaceDetector;\n if (!FaceDetectorCtor) return null;\n try {\n return await new FaceDetectorCtor({ maxDetectedFaces: 5, fastMode: true }).detect(source);\n } catch {\n return null;\n }\n}\n\n/**\n * Best-effort face count via the browser-native Shape Detection API. Thin\n * wrapper over detectFaces() that returns the raw, unfiltered count —\n * callers that care about ghost-image false positives should use\n * detectFaces() + significantFaceCount() instead.\n */\nexport async function detectFaceCount(source: CanvasImageSource): Promise<number | null> {\n const faces = await detectFaces(source);\n return faces === null ? null : faces.length;\n}\n\n/**\n * Counts faces that are large enough to plausibly be a real person in\n * frame, discarding faces that are much smaller than the largest one\n * detected. A ghost-image watermark is always a fraction of the size of the\n * primary ID photo, so this tells a second real person apart from that\n * watermark without needing OCR or a purpose-built model. Two people\n * actually in frame are typically at comparable distance from the camera\n * and so stay above the ratio; this is a heuristic, not a guarantee.\n */\nconst GHOST_IMAGE_AREA_RATIO = 0.4;\n\nexport function significantFaceCount(faces: ShapeDetectionFace[]): number {\n if (faces.length <= 1) return faces.length;\n const areas = faces.map(f => f.boundingBox.width * f.boundingBox.height);\n const maxArea = Math.max(...areas);\n return areas.filter(a => a >= maxArea * GHOST_IMAGE_AREA_RATIO).length;\n}\n\n// Must match the backend's magic-byte gate (api/v1/kyc.py) exactly — JPEG and\n// PNG are the only formats the API accepts, checked by signature rather than\n// file extension or the (spoofable) Content-Type header, both of which an\n// attacker fully controls (e.g. naming a script \"photo.jpg\" or setting\n// Content-Type: image/jpeg on arbitrary bytes).\nconst JPEG_MAGIC = [0xff, 0xd8, 0xff];\nconst PNG_MAGIC = [0x89, 0x50, 0x4e, 0x47];\n\n/**\n * True only for bytes that open with a genuine JPEG or PNG signature — the\n * sole two formats the API accepts. Used both by checkPhotoQuality() (the\n * advisory pre-upload UX check) and directly by startKYC() (core/api.ts) as\n * a hard gate before any bytes leave the browser, so a caller that bypasses\n * checkPhotoQuality — or a non-React consumer using the core client\n * directly — still can't hand the upload endpoint an arbitrary file.\n */\nexport async function hasSupportedMagicBytes(photo: Blob): Promise<boolean> {\n const header = new Uint8Array(await photo.slice(0, 4).arrayBuffer());\n const matches = (magic: number[]) => magic.every((byte, i) => header[i] === byte);\n return matches(JPEG_MAGIC) || matches(PNG_MAGIC);\n}\n\n/**\n * Counts rows within [x0, x0+aw) whose brightness variance falls in the\n * \"printed text\" band — high enough contrast to be ink/MRZ glyphs on a\n * background, but not so extreme it's a hard edge or sensor noise. Shared by\n * the passport data-page detector and the cross-type document/unknown-image\n * check below.\n */\nfunction countTextRows(grey: Float32Array, w: number, h: number, x0: number, aw: number): number {\n let textRows = 0;\n for (let y = 0; y < h; y++) {\n let sum = 0, sumSq = 0;\n for (let x = x0; x < x0 + aw; x++) {\n const v = grey[y * w + x];\n sum += v;\n sumSq += v * v;\n }\n const mean = sum / aw;\n const variance = sumSq / aw - mean * mean;\n if (variance > 150 && mean > 40 && mean < 215) textRows++;\n }\n return textRows;\n}\n\n// ── MRZ / printed-text structure (no OCR) ──────────────────────────────────\n//\n// The backend reads passports from the MRZ (failing with mrz_failed) and ID\n// cards / licences from their printed fields (ocr_failed /\n// document_not_recognized). Both fail late, after a full upload and round\n// trip, when the user photographed something with no MRZ or no readable\n// fields at all: the passport cover, a visa page, the back of a card, a\n// card held too far away. The checks below catch those cases from text\n// *structure* alone, without reading a single character:\n//\n// - An MRZ is two (TD3/TD2) or three (TD1) lines of fixed-pitch OCR-B glyphs\n// that run the full width of the page with no spaces (empty positions are\n// '<' fillers). Under a horizontal-edge map that reads as two or more\n// dense, unbroken, evenly spaced bands of equal height spanning the page.\n// Nothing else on a passport looks like that.\n// - A card's front carries several separate printed lines (header, surname,\n// names, date of birth, ID number). A card back, a blank surface or a photo\n// of something else doesn't.\n//\n// The image is split into vertical strips and lines are found per strip, so\n// the ID photo (a tall block of edges) can't merge the text lines beside it\n// into one blob. A small skew search (±4°) straightens a slightly tilted\n// capture first, otherwise two MRZ lines drift into each other across the\n// page width.\n//\n// Like everything in this file this is a pre-filter, not OCR: a present but\n// unreadable MRZ (glare across one character, say) still goes to the backend.\n\n/**\n * Brightness step between neighbouring pixels counted as a glyph edge, for a\n * crisp, high-contrast photo. A soft video frame of a low-contrast card (grey\n * print on a pale, patterned guilloche background) can\n * have almost no steps this large even though every field is legible (many\nnational IDs print this way), so the\n * threshold drops with the image's own contrast (see edgeThreshold()), but\n * never below EDGE_DELTA_MIN, where sensor grain and background guilloche\n * would start to count as text.\n */\nconst EDGE_DELTA = 28;\nconst EDGE_DELTA_MIN = 12;\n/** The adaptive threshold is this fraction of the 99th-percentile neighbour step. */\nconst EDGE_DELTA_P99_FRACTION = 0.8;\n/** medianStep() at or above this means a grainy frame that gets smoothed first. */\nconst NOISY_MEDIAN_STEP = 9;\n/** tan(-4°) … tan(4°) in 1° steps; 0 first so ties keep the image unrotated. */\nconst SKEW_TANS = [0, 0.0175, -0.0175, 0.0349, -0.0349, 0.0524, -0.0524, 0.0699, -0.0699];\nconst STRIPS = 8;\n/** An MRZ line must run unbroken through at least this many strips (75% of the width). */\nconst MRZ_MIN_STRIPS = Math.ceil(STRIPS * 0.75);\nconst CELL = 8;\n/** An ID card front has at least this many separate printed lines… */\nconst MIN_ID_TEXT_LINES = 4;\n/** …at least this many of them left-aligned in one column. */\nconst MIN_ID_ALIGNED_LINES = 3;\nconst TEXT_MAX_BLEED = 0.3;\nconst MRZ_MAX_BLEED = 0.25;\n/** Minimum fixed-pitch score for an MRZ line (see periodicity()). */\nconst MRZ_MIN_PERIODICITY = 0.15;\n\nexport const MRZ_NOT_FOUND_MESSAGE =\n 'We could not find the machine-readable zone (MRZ): the two lines of letters, numbers and <<< ' +\n 'at the bottom of the photo page. Photograph the page with your photo on it (not the cover or a ' +\n 'visa page), keep the passport straight, and make sure both MRZ lines are fully inside the frame, ' +\n 'sharp and free of glare.';\n\nexport function idTextNotFoundMessage(idType: string): string {\n const doc = idType === 'drivers_licence' ? \"driver's licence\" : 'ID card';\n return (\n `We could not detect readable printed details on your ${doc}. Photograph the front of the ` +\n 'card (the side with your photo, name and ID number) so it fills the frame, in good light ' +\n 'and without glare.'\n );\n}\n\nexport interface TextBand {\n top: number;\n bottom: number;\n center: number;\n height: number;\n strip: number;\n /** Index of the first cell with glyph edges, within the strip. */\n firstCell: number;\n /** Index of the last cell with glyph edges, within the strip. */\n lastCell: number;\n /** Fraction of the strip width between the band's first and last glyph cell. */\n spanFrac: number;\n /** Fraction of cells inside that span that contain glyph edges. */\n coverage: number;\n /** Glyph edges per pixel inside the band's span. */\n density: number;\n /** Edge level just above and below the band relative to inside it. Low = clean gap, like printed lines. */\n bleed: number;\n}\n\nexport interface TextLayout {\n /** Distinct printed lines found anywhere in the region. */\n textLines: number;\n /** Most printed lines sharing one left edge (a column of fields). */\n alignedLines: number;\n /** True when an MRZ-shaped block (2-3 dense, fixed-pitch, full-width lines) is present. */\n hasMrz: boolean;\n}\n\nexport interface MrzLineCandidate {\n top: number;\n bottom: number;\n center: number;\n height: number;\n /** Peak autocorrelation of the column edge profile, i.e. how fixed-pitch the glyphs are. */\n periodicity: number;\n /** Mean bleed of the member bands (see TextBand.bleed). */\n bleed: number;\n}\n\nexport interface TextMeasurements {\n bands: TextBand[];\n /** Full-width dense lines (see MRZ_MIN_STRIPS), top to bottom. */\n fullWidthLines: MrzLineCandidate[];\n}\n\n/**\n * Finds band-shaped clusters of glyph edges in the region [x0, x0+aw) of a\n * greyscale image, per vertical strip, after straightening any small tilt.\n * analyseTextLayout() turns these into a verdict. Exported for tests.\n */\nexport function measureTextBands(\n grey: Float32Array,\n w: number,\n h: number,\n x0 = 0,\n aw = w,\n /** Fixed glyph-edge threshold instead of the adaptive one (see hasMrz()). */\n edgeDeltaOverride?: number,\n): TextMeasurements {\n // 1. Horizontal edge map, kept sparse since most pixels aren't edges. A\n // grainy low-light frame is box-smoothed (3×3) first, otherwise the\n // grain fills the gaps between printed lines. Clean frames skip it:\n // smoothing would soften the small MRZ glyphs of a two-page spread.\n const src = medianStep(grey, w, h, x0, aw) >= NOISY_MEDIAN_STEP ? boxSmooth(grey, w, h, x0, aw) : null;\n const at = src ? (y: number, x: number) => src[y * aw + x] : (y: number, x: number) => grey[y * w + x0 + x];\n const edgeDelta = edgeDeltaOverride ?? edgeThreshold(at, aw, h);\n const ex: number[] = [];\n const ey: number[] = [];\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < aw - 1; x++) {\n if (Math.abs(at(y, x + 1) - at(y, x)) > edgeDelta) {\n ex.push(x);\n ey.push(y);\n }\n }\n }\n if (ex.length === 0) return { bands: [], fullWidthLines: [] };\n\n // 2. Skew: pick the shear that makes the row profile sharpest. Text lines\n // give tall narrow peaks when level and smear out when tilted.\n let bestTan = 0, bestScore = -1;\n for (const t of SKEW_TANS) {\n const shift = Math.ceil(Math.abs(t) * aw);\n const counts = new Int32Array(h + shift + 1);\n for (let i = 0; i < ex.length; i++) {\n counts[ey[i] + Math.round(ex[i] * t) + (t < 0 ? shift : 0)]++;\n }\n let score = 0;\n for (let r = 0; r < counts.length; r++) score += counts[r] * counts[r];\n if (score > bestScore) { bestScore = score; bestTan = t; }\n }\n\n // 3. Bucket the straightened edges into rows × strips × cells.\n const shift = Math.ceil(Math.abs(bestTan) * aw);\n const rows = h + shift + 1;\n const er = new Int32Array(ex.length);\n const stripW = Math.max(1, Math.floor(aw / STRIPS));\n const cps = Math.ceil(stripW / CELL);\n const cells = new Uint16Array(rows * STRIPS * cps);\n for (let i = 0; i < ex.length; i++) {\n const r = ey[i] + Math.round(ex[i] * bestTan) + (bestTan < 0 ? shift : 0);\n er[i] = r;\n const strip = Math.min(STRIPS - 1, Math.floor(ex[i] / stripW));\n const cell = Math.min(cps - 1, Math.floor((ex[i] - strip * stripW) / CELL));\n cells[(r * STRIPS + strip) * cps + cell]++;\n }\n\n // 4. Bands of consecutive \"texty\" rows per strip, split at internal\n // valleys so two tightly spaced lines aren't read as one.\n const minH = Math.max(3, Math.round(h * 0.012));\n const maxH = Math.max(minH + 2, Math.round(h * 0.12));\n const rowMin = Math.max(4, Math.round(stripW * 0.03));\n const bands: TextBand[] = [];\n\n for (let s = 0; s < STRIPS; s++) {\n const rowCount = new Int32Array(rows);\n for (let r = 0; r < rows; r++) {\n let n = 0;\n const base = (r * STRIPS + s) * cps;\n for (let c = 0; c < cps; c++) n += cells[base + c];\n rowCount[r] = n;\n }\n\n const runs: Array<[number, number]> = [];\n let start = -1, gap = 0;\n for (let r = 0; r <= rows; r++) {\n const texty = r < rows && rowCount[r] >= rowMin;\n if (texty) {\n if (start < 0) start = r;\n gap = 0;\n } else if (start >= 0 && (++gap > 1 || r === rows)) {\n runs.push([start, r - gap]);\n start = -1;\n gap = 0;\n }\n }\n\n const split = (a: number, b: number): Array<[number, number]> => {\n if (b - a + 1 < 2 * minH + 1) return [[a, b]];\n let peak = 0;\n for (let r = a; r <= b; r++) peak = Math.max(peak, rowCount[r]);\n let valley = -1, valleyVal = Infinity;\n for (let r = a + minH; r <= b - minH; r++) {\n if (rowCount[r] < valleyVal) { valleyVal = rowCount[r]; valley = r; }\n }\n if (valley < 0 || valleyVal >= peak * 0.4) return [[a, b]];\n return [...split(a, valley - 1), ...split(valley + 1, b)];\n };\n\n for (const [a, b] of runs.flatMap(([a, b]) => split(a, b))) {\n const height = b - a + 1;\n if (height < minH || height > maxH) continue;\n let first = -1, last = -1, active = 0, inside = 0;\n for (let c = 0; c < cps; c++) {\n let n = 0;\n for (let r = a; r <= b; r++) n += cells[(r * STRIPS + s) * cps + c];\n if (n >= 2) {\n if (first < 0) first = c;\n last = c;\n active++;\n }\n inside += n;\n }\n if (first < 0) continue;\n const span = last - first + 1;\n // Edges in the two rows either side, over the same span, vs inside.\n let outside = 0, outsideRows = 0;\n for (const r of [a - 2, a - 1, b + 1, b + 2]) {\n if (r < 0 || r >= rows) continue;\n outsideRows++;\n for (let c = first; c <= last; c++) outside += cells[(r * STRIPS + s) * cps + c];\n }\n const insidePerRow = inside / height;\n bands.push({\n top: a,\n bottom: b,\n center: (a + b) / 2,\n height,\n strip: s,\n firstCell: first,\n lastCell: last,\n spanFrac: span / cps,\n coverage: active / span,\n density: inside / (span * CELL * height),\n bleed: outsideRows > 0 && insidePerRow > 0 ? outside / outsideRows / insidePerRow : 0,\n });\n }\n }\n\n // 5. Full-width dense lines: bands on the same row across most strips.\n const fullWidthLines = clusterBands(bands.filter(b => b.spanFrac >= 0.75 && b.coverage >= 0.85))\n .filter(g => new Set(g.map(b => b.strip)).size >= MRZ_MIN_STRIPS)\n .map(g => {\n const top = Math.min(...g.map(b => b.top));\n const bottom = Math.max(...g.map(b => b.bottom));\n const height = g.reduce((sum, b) => sum + b.height, 0) / g.length;\n // Column edge profile across the whole line; fixed-pitch OCR-B glyphs\n // repeat at one character pitch, proportional print and texture don't.\n const profile = new Float64Array(aw);\n for (let i = 0; i < ex.length; i++) if (er[i] >= top && er[i] <= bottom) profile[ex[i]]++;\n const bleed = g.reduce((sum, b) => sum + b.bleed, 0) / g.length;\n return { top, bottom, center: (top + bottom) / 2, height, periodicity: periodicity(profile, height), bleed };\n });\n\n return { bands, fullWidthLines };\n}\n\n/** Median brightness step between horizontal neighbours: a cheap sensor-noise estimate. */\nfunction medianStep(grey: Float32Array, w: number, h: number, x0: number, aw: number): number {\n const hist = new Uint32Array(256);\n let n = 0;\n for (let y = 0; y < h; y++) {\n const row = y * w + x0;\n for (let x = 0; x < aw - 1; x++) {\n hist[Math.min(255, Math.round(Math.abs(grey[row + x + 1] - grey[row + x])))]++;\n n++;\n }\n }\n let acc = 0;\n for (let v = 0; v < 256; v++) {\n acc += hist[v];\n if (acc >= n / 2) return v;\n }\n return 255;\n}\n\n/**\n * Glyph-edge threshold for this image: EDGE_DELTA when the photo is crisp,\n * lower when its strongest edges (99th-percentile neighbour step) are faint.\n */\nfunction edgeThreshold(at: (y: number, x: number) => number, aw: number, h: number): number {\n const hist = new Uint32Array(256);\n let n = 0;\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < aw - 1; x++) {\n hist[Math.min(255, Math.round(Math.abs(at(y, x + 1) - at(y, x))))]++;\n n++;\n }\n }\n let acc = 0, p99 = 255;\n for (let v = 0; v < 256; v++) {\n acc += hist[v];\n if (acc >= n * 0.99) { p99 = v; break; }\n }\n return Math.max(EDGE_DELTA_MIN, Math.min(EDGE_DELTA, Math.round(p99 * EDGE_DELTA_P99_FRACTION)));\n}\n\n/** 3×3 box blur of the region, returned as an aw-wide array. */\nfunction boxSmooth(grey: Float32Array, w: number, h: number, x0: number, aw: number): Float32Array {\n const out = new Float32Array(aw * h);\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < aw; x++) {\n let sum = 0, n = 0;\n for (let yy = Math.max(0, y - 1); yy <= Math.min(h - 1, y + 1); yy++) {\n for (let xx = Math.max(0, x - 1); xx <= Math.min(aw - 1, x + 1); xx++) {\n sum += grey[yy * w + x0 + xx];\n n++;\n }\n }\n out[y * aw + x] = sum / n;\n }\n }\n return out;\n}\n\nfunction clusterBands(items: TextBand[]): TextBand[][] {\n const sorted = [...items].sort((p, q) => p.center - q.center);\n const groups: TextBand[][] = [];\n for (const band of sorted) {\n const g = groups[groups.length - 1];\n const ref = g?.[g.length - 1];\n if (ref && Math.abs(band.center - ref.center) <= 0.6 * Math.max(band.height, ref.height)) g.push(band);\n else groups.push([band]);\n }\n return groups;\n}\n\n/** Highest normalised autocorrelation of `profile` at lags of 0.4-1.6 × glyph height. */\nfunction periodicity(profile: Float64Array, glyphHeight: number): number {\n const n = profile.length;\n let mean = 0;\n for (let i = 0; i < n; i++) mean += profile[i];\n mean /= n;\n let denom = 0;\n for (let i = 0; i < n; i++) denom += (profile[i] - mean) ** 2;\n if (denom === 0) return 0;\n let best = 0;\n const lo = Math.max(2, Math.round(glyphHeight * 0.4));\n const hi = Math.min(n - 1, Math.round(glyphHeight * 1.6));\n for (let lag = lo; lag <= hi; lag++) {\n let num = 0;\n for (let i = 0; i + lag < n; i++) num += (profile[i] - mean) * (profile[i + lag] - mean);\n best = Math.max(best, num / denom);\n }\n return best;\n}\n\n/**\n * Finds printed text lines in the region [x0, x0+aw) of a greyscale image\n * and reports whether they include an MRZ. Pure pixel arithmetic, about\n * 5-20 ms on a 640 px image.\n */\nexport function analyseTextLayout(\n grey: Float32Array,\n w: number,\n h: number,\n x0 = 0,\n aw = w,\n edgeDeltaOverride?: number,\n): TextLayout {\n const { bands, fullWidthLines } = measureTextBands(grey, w, h, x0, aw, edgeDeltaOverride);\n\n // Printed lines: wider than tall, with a clean gap above and below.\n // Texture and photo detail make bands too, but they bleed into the rows\n // around them.\n const stripCells = Math.ceil(Math.max(1, Math.floor(aw / STRIPS)) / CELL);\n const clean = bands.filter(b =>\n b.spanFrac >= 0.2 && b.coverage >= 0.35 && b.bleed <= TEXT_MAX_BLEED\n && (b.spanFrac * stripCells * CELL) / b.height >= 3);\n const textLines = clusterBands(clean).length;\n // Fields on a card sit in a column, so their lines share an edge: the\n // left one for left-to-right scripts, the right one for right-to-left\n // scripts (Arabic, Hebrew, Persian, Urdu …) where fields are right-aligned.\n let alignedLines = 0;\n for (const b of clean) {\n const sameStrip = clean.filter(o => o.strip === b.strip);\n const left = sameStrip.filter(o => Math.abs(o.firstCell - b.firstCell) <= 1).length;\n const right = sameStrip.filter(o => Math.abs(o.lastCell - b.lastCell) <= 1).length;\n alignedLines = Math.max(alignedLines, left, right);\n }\n\n // MRZ: two clean, fixed-pitch, full-width lines of equal glyph height,\n // one line pitch (about twice the glyph height) apart. Runs are found over\n // *all* full-width lines first, so that a paragraph of print (four or\n // more evenly spaced lines) is never mistaken for one even if some of its\n // lines score like an MRZ. A run of three still passes, allowing for a\n // printed rule or field line right beside the MRZ.\n const isMrzLine = (l: MrzLineCandidate) => l.bleed <= MRZ_MAX_BLEED && l.periodicity >= MRZ_MIN_PERIODICITY;\n const stacked = (p: MrzLineCandidate, q: MrzLineCandidate) => {\n const avgH = (p.height + q.height) / 2;\n const pitch = q.center - p.center;\n return pitch >= 1.3 * avgH && pitch <= 2.8 * avgH\n && Math.max(p.height, q.height) / Math.min(p.height, q.height) <= 1.4;\n };\n let hasMrz = false;\n let runStart = 0;\n for (let i = 1; i <= fullWidthLines.length; i++) {\n if (i < fullWidthLines.length && stacked(fullWidthLines[i - 1], fullWidthLines[i])) continue;\n const run = fullWidthLines.slice(runStart, i);\n if (run.length >= 2 && run.length <= 3) {\n for (let j = 1; j < run.length; j++) {\n if (isMrzLine(run[j - 1]) && isMrzLine(run[j])) hasMrz = true;\n }\n }\n runStart = i;\n }\n\n return { textLines, alignedLines, hasMrz };\n}\n\n/**\n * Glyph-edge threshold for the second MRZ attempt in hasMrz(). The adaptive\n * threshold follows the image's strongest edges, so a crisp barcode or photo\n * on the same page keeps it at EDGE_DELTA even when the MRZ print itself is\n * softer, and the MRZ lines then break into fragments. Measured on real card\n * backs: an MRZ the server reads is found at 14, not at 18 or above.\n */\nconst MRZ_RETRY_EDGE_DELTA = 14;\n\n/**\n * True when the region [x0, x0+aw) shows an MRZ. Tries the adaptive edge\n * threshold first, then once more at MRZ_RETRY_EDGE_DELTA. The retry can\n * only turn \"not found\" into \"found\" — it never rejects anything — and the\n * MRZ test itself (2-3 stacked, fixed-pitch, full-width lines) still has to\n * pass, so plain print, barcodes and card fronts aren't let through by it.\n */\nexport function hasMrz(grey: Float32Array, w: number, h: number, x0 = 0, aw = w): boolean {\n return (\n analyseTextLayout(grey, w, h, x0, aw).hasMrz ||\n analyseTextLayout(grey, w, h, x0, aw, MRZ_RETRY_EDGE_DELTA).hasMrz\n );\n}\n\n// Same progressive-enhancement pattern as FaceDetector above: Chromium's\n// Shape Detection TextDetector, where present, actually reads characters.\n// Only ever used to *pass* a photo the structural check above doubted, never\n// to reject one, so its uneven availability and accuracy can't add false\n// rejections.\ninterface ShapeDetectionText {\n rawValue?: string;\n}\ninterface ShapeDetectionTextDetectorCtor {\n new (): { detect(image: CanvasImageSource): Promise<ShapeDetectionText[]> };\n}\n\n/** Returns detected text blocks, or `null` when the API is unsupported or throws. */\nexport async function detectText(source: CanvasImageSource): Promise<ShapeDetectionText[] | null> {\n const TextDetectorCtor = (globalThis as { TextDetector?: ShapeDetectionTextDetectorCtor }).TextDetector;\n if (!TextDetectorCtor) return null;\n try {\n return await new TextDetectorCtor().detect(source);\n } catch {\n return null;\n }\n}\n\n/** True for a string that reads like an MRZ line: ≥25 chars of A-Z, 0-9 and at least one '<' filler. */\nexport function looksLikeMrzLine(text: string): boolean {\n const compact = text.replace(/\\s+/g, '').toUpperCase();\n return compact.length >= 25 && /^[A-Z0-9<]+$/.test(compact) && compact.includes('<');\n}\n\nfunction laplacianVariance(grey: Float32Array, w: number, h: number, x0: number, aw: number): number {\n let lapSum = 0, lapCount = 0;\n for (let y = 1; y < h - 1; y++) {\n for (let x = x0 + 1; x < x0 + aw - 1; x++) {\n const lap =\n 4 * grey[y * w + x]\n - grey[(y - 1) * w + x]\n - grey[(y + 1) * w + x]\n - grey[y * w + (x - 1)]\n - grey[y * w + (x + 1)];\n lapSum += lap * lap;\n lapCount++;\n }\n }\n return lapCount > 0 ? lapSum / lapCount : 0;\n}\n\n/**\n * Passport-specific checks: the data page must show an MRZ, and must be sharp.\n *\n * Two-page open spread (ratio > 2.0): both halves are scanned independently,\n * and the half with the MRZ is the data page. Only that half's blur is\n * evaluated, so a blurry opposite page never triggers a rejection.\n *\n * Single-page photo: the full image is treated as one region.\n *\n * `browserFoundMrz` is the TextDetector's verdict (see detectText()). When it\n * read an MRZ line the structural check missed, the photo passes on to the\n * blur check, using the region with the most text as the data page.\n */\nexport function checkPassportDocument(\n grey: Float32Array,\n w: number,\n h: number,\n browserFoundMrz = false,\n): string[] {\n const ratio = w / h;\n const regions: Array<{ x0: number; aw: number }> =\n ratio > 2.0\n ? [{ x0: 0, aw: Math.floor(w / 2) }, { x0: Math.floor(w / 2), aw: w - Math.floor(w / 2) }]\n : [{ x0: 0, aw: w }];\n\n let dataPage = regions.find(({ x0, aw }) => hasMrz(grey, w, h, x0, aw));\n if (!dataPage && browserFoundMrz) {\n dataPage = regions.reduce((best, r) =>\n countTextRows(grey, w, h, r.x0, r.aw) > countTextRows(grey, w, h, best.x0, best.aw) ? r : best);\n }\n if (!dataPage) return [MRZ_NOT_FOUND_MESSAGE];\n\n if (laplacianVariance(grey, w, h, dataPage.x0, dataPage.aw) < BLUR_THRESHOLD) {\n return [\n 'The data page is too blurry. Hold the camera steady and make sure the ' +\n 'MRZ text at the bottom of the data page is in sharp focus.',\n ];\n }\n return [];\n}\n\n/**\n * National ID / driver's licence check: the backend reads these by OCR of\n * the printed fields, so the photo must show several separate lines of\n * print. Nothing here is specific to one country's layout: any script,\n * fields aligned left or right, and ICAO 9303 TD1 cards whose side in frame\n * carries an MRZ (which passes on its own). `browserTextBlocks` is the\n * TextDetector's block count (see detectText()); three or more blocks read\n * by the browser also passes.\n */\nexport function checkReadableText(\n grey: Float32Array,\n w: number,\n h: number,\n idType: string,\n browserTextBlocks: number | null = null,\n): string[] {\n if ((browserTextBlocks ?? 0) >= 3) return [];\n const layout = analyseTextLayout(grey, w, h);\n if (layout.hasMrz) return [];\n if (layout.textLines >= MIN_ID_TEXT_LINES && layout.alignedLines >= MIN_ID_ALIGNED_LINES) return [];\n return [idTextNotFoundMessage(idType)];\n}\n\n/**\n * Cross-document-type checks, run for every idType before the type-specific\n * ones above.\n *\n * 1. Unknown-image gate: every genuine ID document — card or passport —\n * has printed text somewhere in frame (name, ID number, dates, MRZ), which\n * shows up as high-contrast rows under countTextRows(). A photo of\n * something else entirely (a selfie, a random object, a blank surface, a\n * screenshot) has essentially none. This is a coarse heuristic, not OCR,\n * so the bar is set low — it only catches images with no document-like\n * structure at all, not blurry-but-genuine documents (blur is handled\n * separately above).\n *\n * 2. Type-mismatch gate: an open passport spread is unmistakably wide\n * (ratio > 2.0) and an ID-1 card has a fixed, distinctive ratio (~1.585,\n * ISO 7810). Uploading the wrong type against the caller-declared idType\n * is flagged in both directions, mirroring the backend's own\n * document_is_passport / document_is_not_passport check (see\n * docs/kyc-pipeline.md §9 in attestid_api) so the user gets the same\n * guidance without waiting on a round trip. This only catches the obvious\n * aspect-ratio case — subtler mismatches still fall through to the\n * backend's dual-OCR-reader check.\n */\nexport function checkDocumentIdentity(\n grey: Float32Array,\n w: number,\n h: number,\n idType: string,\n // Lets checkPhotoQuality() pass in a scan it already ran, instead of this\n // function repeating it. Optional so direct/test callers behave as before.\n precomputedTotalTextRows?: number,\n): string[] {\n const ratio = w / h;\n\n const totalTextRows = precomputedTotalTextRows ?? countTextRows(grey, w, h, 0, w);\n if (totalTextRows < Math.max(2, h * 0.02)) {\n return [\n 'This does not look like an identity document: no printed text was detected. ' +\n 'Please photograph your ID card or passport data page on a plain, well-lit background.',\n ];\n }\n\n if (idType === 'passport') {\n if (ratio < 2.0 && Math.abs(ratio - ID1_ASPECT) < ID1_ASPECT_TOLERANCE) {\n return [\n \"You selected Passport as the document type, but this looks like a National ID or Driver's Licence. \" +\n \"Please go back and select the correct document type.\",\n ];\n }\n } else if (ratio > 2.0) {\n return [\n \"You selected National ID or Driver's Licence, but this looks like a Passport. \" +\n \"Please go back and select 'Passport' as the document type.\",\n ];\n }\n\n return [];\n}\n\nexport interface BackPhotoQualityResult extends PhotoQualityResult {\n /** True when an MRZ (the lines of letters, numbers and <<<) was found. */\n hasMrz: boolean;\n}\n\n// When no MRZ is found the pixels can't tell a blurry MRZ from a missing one\n// (a sharp barcode or background keeps any whole-image sharpness score high\n// while the MRZ text itself is smeared), so the message names the usual\n// causes rather than guessing one.\nexport const BACK_MRZ_NOT_FOUND_MESSAGE =\n 'We could not read the machine-readable zone (MRZ) on the back of your card: the 3 lines of ' +\n 'letters, numbers and <<< along the bottom. This usually means the photo is blurry or the card ' +\n 'is too small in the frame. Fill the frame with the back of the card, hold the phone steady, ' +\n 'and wait for the <<< lines to come into focus before capturing. Avoid glare on the card.';\n\n/**\n * Pure-pixel part of checkBackPhotoQuality(): exposure, blur and MRZ presence\n * on a greyscale image. `browserFoundMrz` is the TextDetector's verdict, and\n * only ever adds a pass. Exported for tests.\n */\nexport function checkBackOfCard(\n grey: Float32Array,\n w: number,\n h: number,\n browserFoundMrz = false,\n): BackPhotoQualityResult {\n let total = 0;\n for (let i = 0; i < grey.length; i++) total += grey[i];\n const brightness = total / grey.length;\n\n const issues: string[] = [];\n if (brightness < DARK_THRESHOLD) issues.push('Image is too dark. Move to a well-lit area.');\n if (brightness > BRIGHT_THRESHOLD)\n issues.push('Image is overexposed. Avoid direct light or glare shining onto the card.');\n\n const mrzFound = browserFoundMrz || hasMrz(grey, w, h);\n if (mrzFound && laplacianVariance(grey, w, h, 0, w) < BLUR_THRESHOLD) {\n issues.push('The back of the card is too blurry. Hold the camera steady so the lines of <<< are sharp.');\n }\n // A dark or overexposed photo hides the MRZ too; show only that root cause.\n if (!mrzFound && issues.length === 0) issues.push(BACK_MRZ_NOT_FOUND_MESSAGE);\n return { ok: issues.length === 0, issues, hasMrz: mrzFound };\n}\n\n/**\n * Checks a photo of the BACK of an ID card / driver's licence (the optional\n * StartKYCOptions.idPhotoBack). The back is only useful to the API when it\n * carries an ICAO 9303 MRZ, so `ok` means \"an MRZ was found and is legible\n * enough to send\". When `hasMrz` is false the caller should offer a retake\n * or let the user skip the back: many cards have no MRZ at all, and the\n * verification never needs one.\n */\nexport async function checkBackPhotoQuality(photo: Blob): Promise<BackPhotoQualityResult> {\n if (photo.size < MIN_FILE_BYTES) {\n return { ok: false, hasMrz: false, issues: ['File is too small. This does not appear to be a real photo.'] };\n }\n if (!(await hasSupportedMagicBytes(photo))) {\n return { ok: false, hasMrz: false, issues: ['Unsupported file type. Please upload a JPEG or PNG photo.'] };\n }\n\n return new Promise(resolve => {\n const img = new Image();\n const url = URL.createObjectURL(photo);\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve({ ok: false, hasMrz: false, issues: ['Could not read the image. Please try a different photo.'] });\n };\n img.onload = async () => {\n URL.revokeObjectURL(url);\n // Same 640 px nearest-neighbour downscale as checkPhotoQuality().\n const scale = Math.min(1, 640 / Math.max(img.width, img.height));\n const w = Math.round(img.width * scale);\n const h = Math.round(img.height * scale);\n const canvas = document.createElement('canvas');\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext('2d')!;\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(img, 0, 0, w, h);\n const textPromise = detectText(canvas);\n\n const { data } = ctx.getImageData(0, 0, w, h);\n const grey = new Float32Array(w * h);\n for (let i = 0; i < grey.length; i++) {\n grey[i] = 0.299 * data[i * 4] + 0.587 * data[i * 4 + 1] + 0.114 * data[i * 4 + 2];\n }\n const texts = await textPromise;\n const browserFoundMrz = texts?.some(t => looksLikeMrzLine(t.rawValue ?? '')) ?? false;\n resolve(checkBackOfCard(grey, w, h, browserFoundMrz));\n };\n img.src = url;\n });\n}\n\n/**\n * Analyses a photo for blur, exposure, and (where the browser supports it)\n * face presence before it is submitted to the AttestID API. Returns a list\n * of human-readable issues; `ok` is true only when the list is empty.\n *\n * Run this before calling startVerification() to avoid unnecessary API calls\n * and to give users clear guidance on retaking a poor-quality photo. This is\n * intentionally not exhaustive — reasons that require real OCR or a facial-\n * attribute model (blurry field reads, sunglasses, closed eyes, …) can only\n * be determined by the backend pipeline; see the scope note above\n * detectFaceCount(). Obviously-wrong-document-type and unrecognizable images\n * *are* caught here (see checkDocumentIdentity()), and so are photos the\n * backend couldn't read at all: a passport with no visible MRZ, or a card\n * with no printed fields (see checkPassportDocument() / checkReadableText()).\n *\n * @param photo - The image to check (File or Blob)\n * @param idType - Document type; passports must show an MRZ, cards must show printed fields\n */\nexport async function checkPhotoQuality(\n photo: Blob,\n idType = 'national_id',\n): Promise<PhotoQualityResult> {\n const issues: string[] = [];\n\n if (photo.size < MIN_FILE_BYTES) {\n issues.push('File is too small. This does not appear to be a real photo.');\n return { ok: false, issues };\n }\n\n if (!(await hasSupportedMagicBytes(photo))) {\n issues.push('Unsupported file type. Please upload a JPEG or PNG photo.');\n return { ok: false, issues };\n }\n\n return new Promise(resolve => {\n const img = new Image();\n const url = URL.createObjectURL(photo);\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve({ ok: false, issues: ['Could not read the image. Please try a different photo.'] });\n };\n\n img.onload = async () => {\n URL.revokeObjectURL(url);\n\n // Downscale to 640 px on the longest side — enough for all checks\n const scale = Math.min(1, 640 / Math.max(img.width, img.height));\n const w = Math.round(img.width * scale);\n const h = Math.round(img.height * scale);\n\n const canvas = document.createElement('canvas');\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext('2d')!;\n // The bilinear filtering the canvas would otherwise apply while\n // downscaling is itself a low-pass blur, stacked on top of whatever\n // real sharpness the source frame has — exactly the confound that\n // makes the Laplacian check below mistake a resized-down but genuinely\n // sharp capture for a blurry one. Nearest-neighbour sampling leaves\n // the source's actual edge contrast alone.\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(img, 0, 0, w, h);\n\n // Fire this off now — it's a separate (often off-thread) browser API\n // call, so let it run concurrently with the pixel analysis below\n // instead of waiting until that's done to even start it.\n const facesPromise = detectFaces(canvas);\n const textPromise = detectText(canvas);\n\n const { data } = ctx.getImageData(0, 0, w, h);\n const len = w * h;\n\n // Greyscale + average brightness\n const grey = new Float32Array(len);\n let totalLum = 0;\n for (let i = 0; i < len; i++) {\n const lum = 0.299 * data[i * 4] + 0.587 * data[i * 4 + 1] + 0.114 * data[i * 4 + 2];\n grey[i] = lum;\n totalLum += lum;\n }\n const brightness = totalLum / len;\n\n // Laplacian variance — measures edge sharpness; low = blurry. Skipped\n // entirely for passports: checkPassportDocument() below computes blur\n // per-region instead (so a blurry non-data page is never penalised),\n // which makes this full-image pass wasted work in that case.\n if (idType !== 'passport' && laplacianVariance(grey, w, h, 0, w) < BLUR_THRESHOLD)\n issues.push('Photo is too blurry. Hold the camera steady and make sure the document is in sharp focus.');\n if (brightness < DARK_THRESHOLD)\n issues.push('Image is too dark. Move to a well-lit area.');\n if (brightness > BRIGHT_THRESHOLD)\n issues.push('Image is overexposed. Avoid direct light or glare shining onto the document.');\n\n // Unknown-image and wrong-document-type gates run first — if the image\n // isn't a document at all, or is clearly the other document type, the\n // passport data-page check below would just add a confusing second\n // message about the same underlying problem.\n const totalTextRows = countTextRows(grey, w, h, 0, w);\n const identityIssues = checkDocumentIdentity(grey, w, h, idType, totalTextRows);\n issues.push(...identityIssues);\n\n // The backend reads a passport from its MRZ and a card from its printed\n // fields, so a photo with neither is rejected here instead of failing\n // after the upload. A blurry, dark or glary photo hides both, so when\n // one of those issues is already listed only that root cause is shown.\n if (identityIssues.length === 0) {\n const texts = await textPromise;\n if (idType === 'passport') {\n const browserFoundMrz = texts?.some(t => looksLikeMrzLine(t.rawValue ?? '')) ?? false;\n const passportIssues = checkPassportDocument(grey, w, h, browserFoundMrz);\n if (!(issues.length > 0 && passportIssues[0] === MRZ_NOT_FOUND_MESSAGE)) issues.push(...passportIssues);\n } else {\n if (issues.length === 0) issues.push(...checkReadableText(grey, w, h, idType, texts?.length ?? null));\n }\n }\n\n // Zero faces maps to face_detection_failed, more than one to\n // multiple_people_detected — both Failed (see the comment on\n // detectFaces() above). `null` means the browser doesn't support the\n // Shape Detection API; never block on that, just skip the check.\n // significantFaceCount() discards faces much smaller than the\n // largest one detected, so an ID card's \"ghost image\" watermark\n // doesn't get mistaken for a second person.\n const faces = await facesPromise;\n const faceCount = faces === null ? null : significantFaceCount(faces);\n if (faceCount === 0) {\n issues.push('No face was detected on your document. Please ensure the photo on your ID is clearly visible and try again.');\n } else if (faceCount !== null && faceCount > 1) {\n issues.push('More than one face was detected in the photo. Please upload a photo showing only your ID document.');\n }\n\n resolve({ ok: issues.length === 0, issues });\n };\n\n img.src = url;\n });\n}\n","import { AuthError, KYCError, ValidationError } from './errors';\nimport { postIdempotent, type FetchLike, type RetryOptions } from './http';\nimport { hasSupportedMagicBytes } from './photoQuality';\nimport type { CaptureSignature } from './deviceKey';\nimport type {\n CaptureSettings,\n SdkSessionResponse,\n StartKYCOptions,\n StartKYCResponse,\n KYCResult,\n LivenessCredentials,\n LivenessChallenge,\n LivenessVideoResult,\n LivenessProvider,\n} from './types';\n\n/** Optional transport settings: custom fetch (e.g. mTLS) and POST retry policy. */\nexport interface Transport {\n fetch?: FetchLike;\n retry?: RetryOptions;\n}\n\n// Resolved per call (not captured at import), so a test or polyfill that\n// replaces globalThis.fetch later is still honoured.\nconst fetchOf = (t?: Transport): FetchLike => t?.fetch ?? ((input, init) => fetch(input, init));\n\n/**\n * Extracts the API's own error message verbatim, instead of the raw response\n * body. The API always errors with FastAPI's `{\"detail\": ...}` envelope,\n * where `detail` is either a plain string or an object carrying a\n * human-readable `message` (see api/v1/kyc.py) — either way we surface that\n * exact string, unmodified, so the SDK never paraphrases or drops detail the\n * backend intentionally chose to send.\n *\n * Sanitized against anything that isn't that expected shape: unparsable\n * bodies, or a `detail` that's neither a string nor an object with a string\n * `message`, fall back to the raw response text rather than surfacing\n * `[object Object]`, `undefined`, or throwing while handling an error.\n */\nasync function extractErrorMessage(res: Response): Promise<string> {\n const text = await res.text();\n try {\n const parsed = JSON.parse(text) as { detail?: unknown; message?: unknown };\n const detail = parsed.detail;\n if (typeof detail === 'string') return detail;\n if (detail !== null && typeof detail === 'object') {\n const message = (detail as { message?: unknown }).message;\n if (typeof message === 'string') return message;\n }\n // Older API middleware errors (idempotency) had a top-level `message`.\n if (detail === undefined && typeof parsed.message === 'string') return parsed.message;\n } catch {\n // Not JSON (or not the { detail } shape) — fall through to the raw text.\n }\n return text;\n}\n\nasync function handleResponse<T>(res: Response): Promise<T> {\n if (res.ok) return res.json() as Promise<T>;\n const message = await extractErrorMessage(res);\n if (res.status === 401 || res.status === 403) throw new AuthError(message);\n throw new KYCError(message, res.status);\n}\n\n/**\n * Exchange the API key (+ its paired access key ID) for a short-lived SDK\n * token bound to userId. Every other KYC call presents this token instead\n * of the raw API key.\n */\nexport async function createSdkSession(\n apiUrl: string,\n apiKey: string,\n accessKeyId: string,\n userId: string,\n transport?: Transport,\n): Promise<SdkSessionResponse> {\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/session`,\n () => ({\n headers: {\n 'X-API-Key': apiKey,\n 'X-Access-Key-Id': accessKeyId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ user_id: userId }),\n }),\n transport?.retry,\n );\n return handleResponse<SdkSessionResponse>(res);\n}\n\n/**\n * Upload the ID photo. `signCapture`, when given, is called once per attempt\n * that needs a fresh request (see http.ts). Each signature uses a single-use\n * nonce, so a rebuilt retry must re-sign instead of replaying the old\n * signature. A null result uploads the photo unsigned.\n */\n/** Tells the API which liveness capture flows this app can run. */\nexport const LIVENESS_FLOWS_HEADER = 'X-AttestID-Liveness-Flows';\n\nfunction sdkHeaders(sdkToken: string, livenessFlows?: readonly LivenessProvider[]): Record<string, string> {\n const headers: Record<string, string> = { 'Authorization': `Bearer ${sdkToken}` };\n if (livenessFlows?.length) headers[LIVENESS_FLOWS_HEADER] = livenessFlows.join(',');\n return headers;\n}\n\nexport async function startKYC(\n apiUrl: string,\n sdkToken: string,\n options: StartKYCOptions,\n transport?: Transport,\n signCapture?: () => Promise<CaptureSignature | null>,\n livenessFlows?: readonly LivenessProvider[],\n): Promise<StartKYCResponse> {\n // Hard gate on file format, independent of checkPhotoQuality() — a caller\n // using the core client directly (no React hook) never runs that advisory\n // check, so this is the one place every upload is guaranteed to pass\n // through. Only a genuine JPEG/PNG signature is accepted; extension and\n // Content-Type are both attacker-controlled and never trusted.\n if (!(await hasSupportedMagicBytes(options.idPhoto))) {\n throw new ValidationError('Unsupported image format. Only JPEG and PNG photos are accepted.');\n }\n if (options.idPhotoBack) {\n if (options.idType === 'passport') {\n throw new ValidationError(\"idPhotoBack is only accepted for national IDs and driver's licences.\");\n }\n if (!(await hasSupportedMagicBytes(options.idPhotoBack))) {\n throw new ValidationError('Unsupported image format for the back of the card. Only JPEG and PNG photos are accepted.');\n }\n }\n\n const build = async () => {\n const form = new FormData();\n form.append('user_id', options.userId);\n form.append('id_type', options.idType);\n form.append('id_photo', options.idPhoto);\n if (options.idPhotoBack) form.append('id_photo_back', options.idPhotoBack);\n form.append('consent_given_at', options.consentGivenAt.toISOString());\n form.append('processing_purpose', options.processingPurpose ?? 'identity_verification');\n const signature = signCapture ? await signCapture() : null;\n if (signature) {\n // All four or none: a partial set is treated as a failed signature.\n for (const [k, v] of Object.entries(signature)) form.append(k, v);\n }\n return { headers: sdkHeaders(sdkToken, livenessFlows), body: form };\n };\n\n const res = await postIdempotent(fetchOf(transport), `${apiUrl}/v1/kyc/start`, build, transport?.retry);\n return handleResponse<StartKYCResponse>(res);\n}\n\n/**\n * The company's capture settings (currently: whether to ask for the back of\n * an ID card). An API from before these settings existed answers 404, which\n * means the same as 'off'.\n */\nexport async function getCaptureSettings(\n apiUrl: string,\n sdkToken: string,\n transport?: Transport,\n): Promise<CaptureSettings> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/capture-settings`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n if (res.status === 404) return { document_back: 'off' };\n return handleResponse<CaptureSettings>(res);\n}\n\nexport async function getLivenessCredentials(\n apiUrl: string,\n sdkToken: string,\n transport?: Transport,\n): Promise<LivenessCredentials> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/liveness-credentials`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n return handleResponse<LivenessCredentials>(res);\n}\n\nconst enc = encodeURIComponent;\n\n/** The random liveness script + nonce for an AttestID Vision session. */\nexport async function getLivenessChallenge(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n transport?: Transport,\n): Promise<LivenessChallenge> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-challenge`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n return handleResponse<LivenessChallenge>(res);\n}\n\nexport interface SubmitLivenessVideoOptions {\n /** From getLivenessChallenge(). */\n nonce: string;\n /** True if the recorded frames are mirrored. A browser's front camera records un-mirrored frames (only the preview is mirrored), so the default false is right for recordLiveness(). */\n mirrored?: boolean;\n}\n\n/**\n * Upload the liveness video. Like startKYC(), `signCapture` is called once\n * per attempt that needs a fresh request, so a retried upload is re-signed\n * with a fresh single-use nonce. A null result uploads the video unsigned.\n */\nexport async function submitLivenessVideo(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n video: Blob,\n options: SubmitLivenessVideoOptions,\n transport?: Transport,\n signCapture?: () => Promise<CaptureSignature | null>,\n): Promise<LivenessVideoResult> {\n const type = (video.type || 'video/webm').split(';')[0];\n const ext = type === 'video/mp4' ? 'mp4' : type === 'video/quicktime' ? 'mov' : 'webm';\n const build = async () => {\n const form = new FormData();\n // Re-wrap so the part's Content-Type is the bare media type the API\n // accepts ('video/webm', not 'video/webm;codecs=vp8').\n form.append('video', new File([video], `liveness.${ext}`, { type }));\n form.append('nonce', options.nonce);\n form.append('mirrored', options.mirrored ? 'true' : 'false');\n const signature = signCapture ? await signCapture() : null;\n if (signature) {\n for (const [k, v] of Object.entries(signature)) form.append(k, v);\n }\n return { headers: { 'Authorization': `Bearer ${sdkToken}` }, body: form };\n };\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-video`,\n build,\n transport?.retry,\n );\n return handleResponse<LivenessVideoResult>(res);\n}\n\n/** Poll target after submitLivenessVideo() answered status 'in_progress'. */\nexport async function getLivenessStatus(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n transport?: Transport,\n): Promise<LivenessVideoResult> {\n const res = await fetchOf(transport)(`${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-status`, {\n headers: { 'Authorization': `Bearer ${sdkToken}` },\n });\n return handleResponse<LivenessVideoResult>(res);\n}\n\n/**\n * Reopen a verification that failed only at liveness with a fresh liveness\n * session, reusing the ID photo already uploaded by startKYC(). Same response\n * shape as startKYC(): run the liveness flow it names, then completeKYC().\n */\nexport async function retryLiveness(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n transport?: Transport,\n livenessFlows?: readonly LivenessProvider[],\n): Promise<StartKYCResponse> {\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/${enc(verificationId)}/liveness-retry`,\n () => ({ headers: sdkHeaders(sdkToken, livenessFlows), body: new FormData() }),\n transport?.retry,\n );\n return handleResponse<StartKYCResponse>(res);\n}\n\nexport async function completeKYC(\n apiUrl: string,\n sdkToken: string,\n verificationId: string,\n deviceId?: string,\n transport?: Transport,\n): Promise<KYCResult> {\n const res = await postIdempotent(\n fetchOf(transport),\n `${apiUrl}/v1/kyc/complete`,\n () => {\n const form = new FormData();\n form.append('verification_id', verificationId);\n if (deviceId) form.append('device_id', deviceId);\n return { headers: { 'Authorization': `Bearer ${sdkToken}` }, body: form };\n },\n transport?.retry,\n );\n return handleResponse<KYCResult>(res);\n}\n","/**\n * Device-bound capture signing (AttestID SECURITY.md P6), browser side.\n *\n * The SDK keeps one ECDSA P-256 key pair per browser profile. The private\n * key is created **non-extractable** with WebCrypto and persisted in\n * IndexedDB as a CryptoKey object: JavaScript, including this SDK, can use\n * it to sign but can never read its bytes. Before each upload:\n *\n * 1. POST /v1/kyc/device/challenge → nonce, then POST /v1/kyc/device/register\n * with the public key. Registration runs every session: the server\n * de-duplicates the same key, so a server-side erasure of old device\n * keys can never leave a stale id behind.\n * 2. A fresh challenge, then sign\n * \"attestid-capture-v1|<sha256 hex of the exact uploaded bytes>|<user_id>|<nonce>|<unix ts>\"\n * and send the four capture_* fields with POST /v1/kyc/start. With the\n * optional back of a card, the v2 message covers both images:\n * \"attestid-capture-v2|<sha256 of front>|<sha256 of back>|<user_id>|<nonce>|<unix ts>\"\n *\n * What this proves: the photo was submitted from the same device that holds\n * the registered key, within a few minutes, through this SDK flow, so\n * images replayed or injected straight into the API (no key, wrong key,\n * reused nonce) are caught. What it doesn't prove (web only): that the bytes\n * came from the physical camera. Browsers have no hardware attestation, so\n * web keys stay at the server's \"key_bound\" level, never \"hardware_attested\".\n *\n * Best-effort by design: if anything fails (no WebCrypto/IndexedDB, network,\n * an older API without these endpoints) the SDK sends **no** capture fields\n * at all. That's assurance \"none\", which isn't penalised. A partial or broken\n * signature would make the server hold the verification for fraud review, so\n * one is never sent.\n */\nimport { buf, bytesToBase64, bytesToHex, hasWebCrypto, sha256, subtle, utf8 } from './encoding';\nimport type { FetchLike } from './http';\n\nexport interface CaptureSignature {\n device_key_id: string;\n capture_nonce: string;\n capture_timestamp: string;\n capture_signature: string;\n}\n\nconst DB_NAME = 'attestid-sdk';\nconst STORE = 'device-keys';\nconst KEY_ID = 'capture-signing-v1';\n\nlet memoryKey: Promise<CryptoKeyPair> | null = null;\n\nfunction idb(): IDBFactory | undefined {\n return (globalThis as { indexedDB?: IDBFactory }).indexedDB;\n}\n\nfunction openDb(factory: IDBFactory): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = factory.open(DB_NAME, 1);\n req.onupgradeneeded = () => req.result.createObjectStore(STORE);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction idbRequest<T>(db: IDBDatabase, mode: IDBTransactionMode, op: (s: IDBObjectStore) => IDBRequest): Promise<T> {\n return new Promise((resolve, reject) => {\n const req = op(db.transaction(STORE, mode).objectStore(STORE));\n req.onsuccess = () => resolve(req.result as T);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction generate(): Promise<CryptoKeyPair> {\n return subtle().generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify']);\n}\n\n/**\n * The device's key pair: loaded from IndexedDB, or created and stored there.\n * Falls back to a per-page-load in-memory key when IndexedDB is unavailable\n * (private mode, some webviews, tests). That's still a valid registered key, just not persistent.\n */\nexport function getDeviceKeyPair(): Promise<CryptoKeyPair> {\n const factory = idb();\n if (!factory) return (memoryKey ??= generate());\n return (async () => {\n try {\n const db = await openDb(factory);\n try {\n const existing = await idbRequest<CryptoKeyPair | undefined>(db, 'readonly', s => s.get(KEY_ID));\n if (existing?.privateKey && existing.publicKey) return existing;\n const pair = await generate();\n await idbRequest(db, 'readwrite', s => s.put(pair, KEY_ID));\n return pair;\n } finally {\n db.close();\n }\n } catch {\n return (memoryKey ??= generate());\n }\n })();\n}\n\nexport function deviceBindingSupported(): boolean {\n return hasWebCrypto();\n}\n\nasync function postJson<T>(fetchImpl: FetchLike, url: string, token: string, body?: unknown): Promise<T> {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) throw new Error(`${url} → HTTP ${res.status}`);\n return res.json() as Promise<T>;\n}\n\nasync function challenge(fetchImpl: FetchLike, apiUrl: string, token: string): Promise<string> {\n return (await postJson<{ nonce: string }>(fetchImpl, `${apiUrl}/v1/kyc/device/challenge`, token)).nonce;\n}\n\n/** Register this device's public key for the SDK token's end user. Returns device_key_id. */\nexport async function registerDeviceKey(\n fetchImpl: FetchLike,\n apiUrl: string,\n token: string,\n pair: CryptoKeyPair,\n): Promise<string> {\n const spki = new Uint8Array(await subtle().exportKey('spki', pair.publicKey));\n const nonce = await challenge(fetchImpl, apiUrl, token);\n const res = await postJson<{ device_key_id: string }>(fetchImpl, `${apiUrl}/v1/kyc/device/register`, token, {\n platform: 'web',\n public_key: bytesToBase64(spki),\n nonce,\n });\n return res.device_key_id;\n}\n\n/**\n * The signed capture message. With a back-of-card photo, one v2 signature\n * covers both images, so a signed front can't be paired with a swapped-in back.\n */\nexport function captureMessage(\n imageSha256Hex: string,\n userId: string,\n nonce: string,\n ts: number,\n backImageSha256Hex?: string,\n): string {\n if (backImageSha256Hex !== undefined) {\n return `attestid-capture-v2|${imageSha256Hex}|${backImageSha256Hex}|${userId}|${nonce}|${ts}`;\n }\n return `attestid-capture-v1|${imageSha256Hex}|${userId}|${nonce}|${ts}`;\n}\n\n/**\n * Register (idempotently) and sign one capture. Returns null instead of\n * throwing on any failure, so the upload goes out unsigned rather than with\n * a broken signature.\n */\nexport async function signCapture(\n fetchImpl: FetchLike,\n apiUrl: string,\n token: string,\n userId: string,\n photo: Blob,\n backPhoto?: Blob,\n): Promise<CaptureSignature | null> {\n try {\n if (!deviceBindingSupported()) return null;\n const pair = await getDeviceKeyPair();\n const deviceKeyId = await registerDeviceKey(fetchImpl, apiUrl, token, pair);\n const imageHash = bytesToHex(await sha256(new Uint8Array(await photo.arrayBuffer())));\n const backHash = backPhoto\n ? bytesToHex(await sha256(new Uint8Array(await backPhoto.arrayBuffer())))\n : undefined;\n const nonce = await challenge(fetchImpl, apiUrl, token);\n const ts = Math.floor(Date.now() / 1000);\n // WebCrypto ECDSA returns raw r||s (64 bytes), which the API accepts as-is.\n const sig = new Uint8Array(\n await subtle().sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n pair.privateKey,\n buf(utf8(captureMessage(imageHash, userId, nonce, ts, backHash))),\n ),\n );\n return {\n device_key_id: deviceKeyId,\n capture_nonce: nonce,\n capture_timestamp: String(ts),\n capture_signature: bytesToBase64(sig),\n };\n } catch {\n return null;\n }\n}\n\n/** @internal test hook */\nexport function _resetDeviceKeyCache(): void {\n memoryKey = null;\n}\n","import type { IdType, KYCResult } from './types';\n\n/** Strips provider prefix (\"aws:\" / \"azure:\") from a liveness session token */\nexport function rawSessionId(token: string): string {\n const idx = token.indexOf(':');\n return idx !== -1 ? token.slice(idx + 1) : token;\n}\n\n/** ISO 7810 document aspect ratios (width / height) */\nexport const DOC_ASPECT: Record<IdType, number> = {\n national_id: 85.6 / 54, // ID-1 — all national ID cards regardless of country\n drivers_licence: 85.6 / 54, // ID-1\n passport: 125 / 88, // ID-3 booklet data page\n};\n\n/**\n * How the API ruled on the face match. Derived from the API's own decision\n * rather than re-applying a threshold here: the gate is server config and\n * differs per matcher (Rekognition vs attestid_vision).\n */\nexport function faceMatchOutcome(result: KYCResult): 'pass' | 'review' | 'fail' {\n const flags = result.checks?.fraud_flags ?? [];\n if (flags.includes('face_match_borderline')) return 'review';\n if (result.failure_reason === 'face_match_failed' || flags.includes('repeated_face_match_failure')) {\n return 'fail';\n }\n return 'pass';\n}\n\n/**\n * Face-check failures that look like a bad capture rather than an attack. A\n * verification that failed with one of these can retry the face check with\n * AttestID.retryLiveness(), without uploading the ID photo again. Includes a\n * scored face-match miss (face_match_failed): the ID photo was read and\n * compared, so only the selfie is retaken. Mirrors the API's\n * RETRYABLE_LIVENESS_REASONS. The API has the final say (it also caps\n * retries and limits how long after upload they're allowed).\n */\nexport const RETRYABLE_LIVENESS_REASONS: ReadonlySet<string> = new Set([\n 'liveness_failed',\n 'liveness_confidence_too_low',\n 'liveness_session_missing',\n 'liveness_reference_image_missing',\n 'liveness_in_progress',\n 'face_match_failed',\n]);\n\n/** True if this result failed only at the face check, so retryLiveness() can be offered. */\nexport function canRetryLiveness(result: KYCResult): boolean {\n if (result.verified || !result.failure_reason) return false;\n return RETRYABLE_LIVENESS_REASONS.has(result.failure_reason.split(':', 1)[0]);\n}\n","import {\n createSdkSession,\n startKYC,\n getCaptureSettings,\n getLivenessCredentials,\n getLivenessChallenge,\n submitLivenessVideo,\n getLivenessStatus,\n retryLiveness,\n completeKYC,\n type SubmitLivenessVideoOptions,\n type Transport,\n} from './api';\nimport { signCapture } from './deviceKey';\nimport { rawSessionId } from './utils';\nimport { AttestIDError, AuthError } from './errors';\nimport type {\n AttestIDConfig,\n AttestIDConfigServer,\n CaptureSettings,\n StartKYCOptions,\n StartKYCResponse,\n LivenessCredentials,\n LivenessChallenge,\n LivenessProvider,\n LivenessVideoResult,\n KYCResult,\n} from './types';\n\nconst sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));\n\n/** The flows this SDK ships components for: LivenessDetector and VisionLivenessCapture. */\nconst DEFAULT_LIVENESS_FLOWS: readonly LivenessProvider[] = ['aws', 'vision'];\n\nexport interface WaitForLivenessOptions {\n /** Poll interval. Default 1500 ms. */\n intervalMs?: number;\n /** Give up after this long. Default 120 s. */\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\n// Refresh a bit before actual expiry so an in-flight request never races the\n// backend's own clock.\nconst TOKEN_REFRESH_MARGIN_MS = 15_000;\n\nfunction isServerConfig(config: AttestIDConfig): config is AttestIDConfigServer {\n return 'apiKey' in config;\n}\n\n// A bundler shipping code to a browser, React Native, or an edge runtime never\n// exposes a real Node.js process — only an actual Node backend process does.\n// This is what we gate apiKey/accessKeyId on: those are long-lived secrets that\n// must never end up in code a bundler ships to a client. Accessed via\n// globalThis (rather than the bare `process` identifier) so this isomorphic\n// package doesn't need a hard @types/node dependency.\nfunction isTrustedServerEnvironment(): boolean {\n const proc = (globalThis as { process?: { versions?: { node?: string } } }).process;\n return !!proc?.versions?.node;\n}\n\n// http:// is only tolerated against a loopback backend — i.e. a developer\n// running the API locally without TLS. Anything else (a real host, an IP,\n// a tunnel/staging domain) must be https, since apiKey/accessKeyId, the\n// bearer sdk_token, and the ID photo itself all travel over this URL.\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);\n\nfunction assertSecureApiUrl(apiUrl: string): void {\n let url: URL;\n try {\n url = new URL(apiUrl);\n } catch {\n throw new AttestIDError(`apiUrl must be a valid absolute URL, got \"${apiUrl}\".`, 'INSECURE_CONFIG');\n }\n if (url.protocol === 'https:') return;\n if (url.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname)) return;\n throw new AttestIDError(\n `apiUrl must use https://, got \"${apiUrl}\". Sending it over plain http exposes your API key, ` +\n 'SDK token, and uploaded ID photos to anyone on the network path. http://localhost (or ' +\n '127.0.0.1/::1) is allowed for local development against a backend running without TLS.',\n 'INSECURE_CONFIG',\n );\n}\n\nexport class AttestID {\n private readonly config: AttestIDConfig & { region: string };\n private readonly transport: Transport;\n private readonly deviceBinding: boolean;\n private readonly livenessFlows: readonly LivenessProvider[];\n\n // The backend never sees anything but this short-lived token past session\n // creation — the SDK exchanges the API key (server mode) or calls your\n // getSdkToken callback (browser mode) once per user/expiry and reuses the\n // result for every subsequent KYC call.\n private tokenPromise: Promise<string> | null = null;\n private tokenUserId: string | null = null;\n private tokenExpiresAt = 0;\n private tokenInFlight = false;\n\n constructor(config: AttestIDConfig) {\n assertSecureApiUrl(config.apiUrl);\n if (isServerConfig(config) && !isTrustedServerEnvironment()) {\n throw new AttestIDError(\n 'apiKey/accessKeyId must only be used in a trusted Node.js backend process. A bundler ' +\n 'would ship this long-lived secret to every client, letting anyone extract it. ' +\n 'In browser or mobile app code, configure `getSdkToken` instead: a callback that fetches ' +\n 'an already-minted sdk_token from your own backend, which holds apiKey server-side and ' +\n 'calls POST /kyc/session. See the integration guide for the backend-proxy pattern.',\n 'INSECURE_CONFIG',\n );\n }\n this.config = { region: 'us-east-1', ...config };\n this.transport = { fetch: config.fetch, retry: config.retry };\n this.livenessFlows = config.livenessFlows?.length ? [...config.livenessFlows] : DEFAULT_LIVENESS_FLOWS;\n // Device binding signs with a key that lives on the end user's device.\n // A backend process holding apiKey is never that device, so it's off in\n // server mode whatever the config says.\n this.deviceBinding = !isServerConfig(config) && (config.deviceBinding ?? true);\n }\n\n /**\n * Obtain a short-lived SDK token bound to userId, reusing an in-flight or\n * still-valid token for the same user instead of minting a new one on every\n * call. Server mode exchanges apiKey/accessKeyId itself; browser mode\n * delegates to your getSdkToken callback.\n */\n private getSdkToken(userId: string): Promise<string> {\n const sameUser = this.tokenUserId === userId;\n const stillValid = Date.now() < this.tokenExpiresAt - TOKEN_REFRESH_MARGIN_MS;\n // Reuse the cached token when it's still valid, OR when a mint for this same\n // user is already in flight. The in-flight case matters: startVerification()\n // and getLivenessCredentials() are fired together (Promise.all in\n // useAttestID), both call this synchronously before the first resolves, and\n // tokenExpiresAt isn't set yet — without the tokenInFlight check the second\n // call mints its own token, doubling the round-trips to your backend and to\n // POST /kyc/session.\n if (this.tokenPromise && sameUser && (stillValid || this.tokenInFlight)) {\n return this.tokenPromise;\n }\n\n this.tokenUserId = userId;\n this.tokenInFlight = true;\n this.tokenExpiresAt = 0;\n const config = this.config;\n const sessionPromise = isServerConfig(config)\n ? createSdkSession(config.apiUrl, config.apiKey, config.accessKeyId, userId, this.transport)\n : config.getSdkToken(userId);\n const minted = sessionPromise.then(session => {\n // In server mode, createSdkSession() already throws on a non-2xx\n // response. In browser mode, `session` comes from the consumer's own\n // getSdkToken callback — e.g. a backend proxy response parsed without\n // checking res.ok — so an auth failure upstream can resolve here as a\n // plain object with no sdk_token instead of rejecting. Catch that now\n // rather than silently sending \"Authorization: Bearer undefined\" on\n // the next call, which fails with a confusing, unrelated 401.\n if (!session?.sdk_token) {\n throw new AuthError(\n 'getSdkToken() did not return a valid sdk_token. Check that your backend\\'s ' +\n '/api/attestid-session endpoint (or equivalent) returned a successful response.',\n );\n }\n this.tokenExpiresAt = Date.now() + session.expires_in * 1000;\n this.tokenInFlight = false;\n return session.sdk_token;\n });\n // A failed mint must not stay cached — the next call should retry instead of\n // replaying the same rejection. Only clear if this is still the current\n // attempt (a later getSdkToken() may already have replaced it).\n minted.catch(() => {\n if (this.tokenPromise === minted) {\n this.tokenPromise = null;\n this.tokenUserId = null;\n this.tokenExpiresAt = 0;\n this.tokenInFlight = false;\n }\n });\n this.tokenPromise = minted;\n return minted;\n }\n\n /**\n * Start a KYC verification — uploads the ID photo and returns a liveness\n * session token to pass to the FaceLivenessDetector component.\n *\n * `options.consentGivenAt` is mandatory and must be the timestamp of an\n * actual user action (e.g. ticking a consent checkbox), not the moment\n * this method happens to be called. Plain JavaScript callers bypassing\n * TypeScript's type check are still rejected here at runtime.\n */\n async startVerification(options: StartKYCOptions): Promise<StartKYCResponse> {\n if (!options.consentGivenAt) {\n throw new AttestIDError(\n 'consentGivenAt is required: capture the timestamp of an explicit user consent action before starting verification',\n 'CONSENT_REQUIRED',\n );\n }\n const token = await this.getSdkToken(options.userId);\n const { apiUrl } = this.config;\n const fetchImpl = this.transport.fetch ?? ((input: string, init?: RequestInit) => fetch(input, init));\n const signer = this.deviceBinding\n ? () => signCapture(fetchImpl, apiUrl, token, options.userId, options.idPhoto, options.idPhotoBack)\n : undefined;\n return startKYC(apiUrl, token, options, this.transport, signer, this.livenessFlows);\n }\n\n /**\n * The company's capture settings, set on the AttestID dashboard. Read\n * `document_back` before the document step to know whether to ask for the\n * back of a national ID / driver's licence (see DocumentBackPolicy).\n */\n async getCaptureSettings(userId: string): Promise<CaptureSettings> {\n const token = await this.getSdkToken(userId);\n return getCaptureSettings(this.config.apiUrl, token, this.transport);\n }\n\n /**\n * Fetch temporary AWS credentials and the Cognito identity pool ID from the\n * backend. Call this alongside startVerification() for the same userId to\n * configure Amplify before mounting FaceLivenessDetector.\n */\n async getLivenessCredentials(userId: string): Promise<LivenessCredentials> {\n const token = await this.getSdkToken(userId);\n return getLivenessCredentials(this.config.apiUrl, token, this.transport);\n }\n\n /**\n * Returns the raw liveness session ID (strips provider prefix) ready to\n * pass directly to the FaceLivenessDetector `sessionId` prop.\n */\n getLivenessSessionId(token: string): string {\n return rawSessionId(token);\n }\n\n /**\n * Which face-check flow this verification uses. The API picks it per\n * session (a super admin can switch between AWS Rekognition and AttestID\n * Vision, with automatic fallback), so branch on this rather than assuming\n * the Amplify flow. Responses from older API versions carry no provider:\n * that means 'aws'.\n */\n livenessProvider(start: StartKYCResponse): LivenessProvider {\n return start.liveness_provider ?? 'aws';\n }\n\n private async currentToken(method: string): Promise<string> {\n if (!this.tokenPromise) {\n throw new AttestIDError(`${method}() must be called after startVerification()`, 'AUTH_ERROR');\n }\n return this.tokenPromise;\n }\n\n /**\n * AttestID Vision only: the random, single-use liveness script and nonce.\n * Play `script` while recording (recordLiveness / VisionLivenessCapture) and\n * upload with submitLivenessVideo() before `expires_at`.\n */\n async getLivenessChallenge(verificationId: string): Promise<LivenessChallenge> {\n const token = await this.currentToken('getLivenessChallenge');\n return getLivenessChallenge(this.config.apiUrl, token, verificationId, this.transport);\n }\n\n /**\n * AttestID Vision only: upload the recorded liveness video. With device\n * binding on, the video is signed with the same device key as the ID photo.\n * If the result's status is 'in_progress', call waitForLivenessResult()\n * next. Either way, finish with completeVerification(), which turns the\n * liveness outcome into the final result.\n */\n async submitLivenessVideo(\n verificationId: string,\n video: Blob,\n options: SubmitLivenessVideoOptions,\n ): Promise<LivenessVideoResult> {\n const token = await this.currentToken('submitLivenessVideo');\n const { apiUrl } = this.config;\n const fetchImpl = this.transport.fetch ?? ((input: string, init?: RequestInit) => fetch(input, init));\n const userId = this.tokenUserId ?? '';\n const signer = this.deviceBinding\n ? () => signCapture(fetchImpl, apiUrl, token, userId, video)\n : undefined;\n return submitLivenessVideo(apiUrl, token, verificationId, video, options, this.transport, signer);\n }\n\n /**\n * AttestID Vision only: poll until the liveness decision is ready\n * (status 'done'). Don't call completeVerification() before this resolves,\n * because completing while liveness is still in progress fails the\n * verification.\n */\n async waitForLivenessResult(\n verificationId: string,\n options: WaitForLivenessOptions = {},\n ): Promise<LivenessVideoResult> {\n const token = await this.currentToken('waitForLivenessResult');\n const interval = options.intervalMs ?? 1500;\n const deadline = Date.now() + (options.timeoutMs ?? 120_000);\n for (;;) {\n if (options.signal?.aborted) {\n throw new AttestIDError('Stopped waiting for the liveness result.', 'LIVENESS_ABORTED');\n }\n const r = await getLivenessStatus(this.config.apiUrl, token, verificationId, this.transport);\n if (r.status !== 'in_progress') return r;\n if (Date.now() + interval > deadline) {\n throw new AttestIDError('Timed out waiting for the liveness result.', 'LIVENESS_TIMEOUT');\n }\n await sleep(interval);\n }\n }\n\n /**\n * Retry the face check on a verification that failed only at liveness\n * (see canRetryLiveness()), without uploading the ID photo again. Returns a\n * fresh liveness session in the same shape as startVerification(): branch on\n * livenessProvider() and run that flow again, then completeVerification().\n *\n * The API refuses (KYCError, status 409/410) for spoof, face-match or fraud\n * failures, once the retry cap is used up, or too long after the upload.\n * Start a new verification in that case.\n */\n async retryLiveness(verificationId: string): Promise<StartKYCResponse> {\n if (this.tokenUserId === null) {\n throw new AttestIDError('retryLiveness() must be called after startVerification()', 'AUTH_ERROR');\n }\n // getSdkToken rather than currentToken: a retry can come minutes after\n // startVerification(), so refresh the token if it's about to expire.\n const token = await this.getSdkToken(this.tokenUserId);\n return retryLiveness(this.config.apiUrl, token, verificationId, this.transport, this.livenessFlows);\n }\n\n /**\n * Complete the KYC verification after the liveness check passes.\n * Returns the final result with document info, face-match score, and fraud flags.\n * Must be called after startVerification() in the same client instance —\n * it reuses that call's SDK token rather than minting a new one.\n */\n async completeVerification(verificationId: string, deviceId?: string): Promise<KYCResult> {\n const token = await this.currentToken('completeVerification');\n return completeKYC(this.config.apiUrl, token, verificationId, deviceId, this.transport);\n }\n\n /** The Cognito region. */\n get region(): string {\n return this.config.region;\n }\n\n /**\n * The Cognito identity pool ID if provided in config.\n * Prefer getLivenessCredentials() to fetch it dynamically from the backend.\n */\n get identityPoolId(): string | undefined {\n return this.config.identityPoolId;\n }\n}\n","/**\n * AttestID Vision liveness capture, browser side.\n *\n * The API hands out a random, single-use script per attempt (see\n * getLivenessChallenge): hold still → a sequence of full-screen colour\n * flashes → a few actions (turn, blink, smile, open mouth). This module\n * records the front camera with MediaRecorder while the UI plays that script.\n * The server then checks that the video really follows it: the right actions\n * at the right times, the screen colours reflected on real skin in the right\n * order, a pulse, no print/screen/mask, the same face throughout. A\n * pre-recorded or generated video can't know the script in advance.\n *\n * Timing matters. The server reads the script against the video's own\n * timestamps, so the script clock starts on MediaRecorder's `start` event,\n * the moment frame 0 is captured, and the UI must show each segment exactly\n * when onSegment says so. Browsers keep front-camera *recordings*\n * un-mirrored (only a preview is mirrored, via CSS), so uploads use\n * mirrored=false.\n */\nimport { AttestIDError, CameraError } from './errors';\nimport type { LivenessAction, LivenessSegment } from './types';\n\n/** Default user-facing prompts. Override per language in your own UI. */\nexport const LIVENESS_PROMPTS: Record<LivenessAction | 'steady' | 'flash', string> = {\n steady: 'Look straight at the camera and hold still',\n flash: 'Keep looking at the screen',\n turn_left: 'Slowly turn your head to your left',\n turn_right: 'Slowly turn your head to your right',\n blink: 'Blink',\n smile: 'Smile',\n open_mouth: 'Open your mouth',\n};\n\n/** CSS colours for flash segments: pure, full-brightness primaries. */\nexport const FLASH_CSS: Record<NonNullable<LivenessSegment['color']>, string> = {\n red: '#ff0000',\n green: '#00ff00',\n blue: '#0000ff',\n};\n\n/** Recording keeps running this long past the script, so the last action isn't cut off. */\nexport const RECORDING_TAIL_MS = 500;\n\nexport function scriptDurationMs(script: LivenessSegment[]): number {\n return script.reduce((max, s) => Math.max(max, s.end_ms), 0);\n}\n\nexport function segmentAt(script: LivenessSegment[], elapsedMs: number): LivenessSegment | undefined {\n return script.find(s => elapsedMs >= s.start_ms && elapsedMs < s.end_ms);\n}\n\nexport function promptFor(segment: LivenessSegment | undefined): string {\n if (!segment) return '';\n if (segment.kind === 'challenge' && segment.action) return LIVENESS_PROMPTS[segment.action];\n return LIVENESS_PROMPTS[segment.kind === 'flash' ? 'flash' : 'steady'];\n}\n\n// MP4 first: Safari (incl. every iOS browser) only records MP4, and Chrome's\n// MP4 is the most widely decodable. WebM for Firefox and older Chromium.\nconst MIME_CANDIDATES = [\n 'video/mp4;codecs=avc1',\n 'video/mp4',\n 'video/webm;codecs=vp8',\n 'video/webm;codecs=vp9',\n 'video/webm',\n];\n\nexport function pickRecorderMimeType(): string | undefined {\n const MR = (globalThis as { MediaRecorder?: typeof MediaRecorder }).MediaRecorder;\n if (!MR) return undefined;\n if (typeof MR.isTypeSupported !== 'function') return '';\n return MIME_CANDIDATES.find(m => MR.isTypeSupported(m));\n}\n\nexport function livenessCaptureSupported(): boolean {\n const md = (globalThis as { navigator?: Navigator }).navigator?.mediaDevices;\n return !!md?.getUserMedia && pickRecorderMimeType() !== undefined;\n}\n\n/** Front camera at a size the server analyses well (≥ 360p short side, ~30 fps), video only. */\nexport async function openFrontCamera(): Promise<MediaStream> {\n try {\n return await navigator.mediaDevices.getUserMedia({\n audio: false,\n video: {\n facingMode: 'user',\n width: { ideal: 640 },\n height: { ideal: 480 },\n frameRate: { ideal: 30, min: 15 },\n },\n });\n } catch {\n throw new CameraError('Camera access denied. Grant permission and try again.');\n }\n}\n\n// ── Pre-capture lighting check ──────────────────────────────────────────────\n//\n// The same idea as AWS Face Liveness, Onfido and Smile ID: check the light\n// before recording and coach the user, instead of failing after the whole\n// script. In dim light a webcam lengthens its exposure and drops to 10–15 fps\n// whatever getUserMedia asked for, and the server's liveness layers all\n// degrade with it. Thresholds mirror the server (low_light_max_fps,\n// low_light_face_luma in attestid_vision's config).\nexport const MIN_GOOD_FPS = 20;\nexport const MIN_GOOD_BRIGHTNESS = 60;\nexport const MAX_GOOD_BRIGHTNESS = 230;\n\nexport type CaptureIssue = 'too_dark' | 'too_bright' | 'low_frame_rate';\n\nexport interface CaptureConditions {\n /** Frames per second the camera actually delivered, or null if the browser can't tell. */\n fps: number | null;\n /** Mean luminance (0–255) of the centre of the frame, where the face sits. */\n brightness: number | null;\n issue: CaptureIssue | null;\n}\n\nexport const CAPTURE_ISSUE_PROMPTS: Record<CaptureIssue, string> = {\n too_dark: 'It’s too dark. Move somewhere brighter or face a light.',\n low_frame_rate: 'It’s too dark. Move somewhere brighter or face a light.',\n too_bright: 'It’s too bright. Move out of direct sunlight.',\n};\n\n/** Mean BT.601 luminance of RGBA pixel data. */\nexport function meanLuma(rgba: ArrayLike<number>): number {\n let sum = 0;\n const n = Math.floor(rgba.length / 4);\n for (let i = 0; i < n * 4; i += 4) sum += 0.299 * rgba[i] + 0.587 * rgba[i + 1] + 0.114 * rgba[i + 2];\n return n ? sum / n : 0;\n}\n\nexport function classifyConditions(fps: number | null, brightness: number | null): CaptureIssue | null {\n if (brightness !== null && brightness < MIN_GOOD_BRIGHTNESS) return 'too_dark';\n if (brightness !== null && brightness > MAX_GOOD_BRIGHTNESS) return 'too_bright';\n if (fps !== null && fps < MIN_GOOD_FPS) return 'low_frame_rate';\n return null;\n}\n\ntype FrameCallbackVideo = HTMLVideoElement & {\n requestVideoFrameCallback?: (cb: (now: number, meta: { presentedFrames: number }) => void) => number;\n cancelVideoFrameCallback?: (handle: number) => void;\n};\n\nfunction measureFps(video: FrameCallbackVideo, sampleMs: number): Promise<number | null> {\n if (typeof video.requestVideoFrameCallback !== 'function') return Promise.resolve(null);\n return new Promise(resolve => {\n let first: { t: number; frames: number } | null = null;\n let last: { t: number; frames: number } | null = null;\n let handle = 0;\n const onFrame = (now: number, meta: { presentedFrames: number }) => {\n if (!first) first = { t: now, frames: meta.presentedFrames };\n last = { t: now, frames: meta.presentedFrames };\n handle = video.requestVideoFrameCallback!(onFrame);\n };\n handle = video.requestVideoFrameCallback!(onFrame);\n setTimeout(() => {\n video.cancelVideoFrameCallback?.(handle);\n if (!first || !last || last.t <= first.t) return resolve(null);\n resolve((1000 * (last.frames - first.frames)) / (last.t - first.t));\n }, sampleMs);\n });\n}\n\nfunction measureBrightness(video: HTMLVideoElement): number | null {\n const w = video.videoWidth;\n const h = video.videoHeight;\n if (!w || !h || typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = 64;\n canvas.height = 48;\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) return null;\n // Centre half of the frame: roughly the oval the face is asked to fill.\n ctx.drawImage(video, w / 4, h / 4, w / 2, h / 2, 0, 0, 64, 48);\n try {\n return meanLuma(ctx.getImageData(0, 0, 64, 48).data);\n } catch {\n return null;\n }\n}\n\n/**\n * Measure the live preview for `sampleMs`: delivered frame rate and the\n * brightness where the face sits. `video` must be playing the camera stream.\n */\nexport async function checkCaptureConditions(video: HTMLVideoElement, sampleMs = 1000): Promise<CaptureConditions> {\n const fps = await measureFps(video, sampleMs);\n const brightness = measureBrightness(video);\n return { fps, brightness, issue: classifyConditions(fps, brightness) };\n}\n\nexport interface LivenessRecording {\n video: Blob;\n mimeType: string;\n durationMs: number;\n}\n\nexport interface RecordLivenessOptions {\n /** Called whenever the active segment changes (undefined in the trailing tail). Drive your UI from this. */\n onSegment?: (segment: LivenessSegment | undefined, elapsedMs: number) => void;\n /** Called on every animation frame with the elapsed time, e.g. for a progress bar. */\n onTick?: (elapsedMs: number, totalMs: number) => void;\n /** Abort recording; the promise rejects with code LIVENESS_ABORTED. */\n signal?: AbortSignal;\n}\n\n/**\n * Record `stream` while playing `script`. Resolves with the video once the\n * script (plus a short tail) has finished. Does not stop the stream's tracks;\n * the caller owns the camera.\n */\nexport function recordLiveness(\n stream: MediaStream,\n script: LivenessSegment[],\n options: RecordLivenessOptions = {},\n): Promise<LivenessRecording> {\n const mimeType = pickRecorderMimeType();\n if (mimeType === undefined) {\n return Promise.reject(\n new AttestIDError('This browser cannot record video (MediaRecorder unavailable).', 'LIVENESS_UNSUPPORTED'),\n );\n }\n const total = scriptDurationMs(script);\n const stopAt = total + RECORDING_TAIL_MS;\n\n return new Promise((resolve, reject) => {\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(stream, {\n ...(mimeType ? { mimeType } : {}),\n videoBitsPerSecond: 2_500_000,\n });\n } catch (e) {\n reject(new AttestIDError(`Could not start recording: ${String(e)}`, 'LIVENESS_UNSUPPORTED'));\n return;\n }\n\n const chunks: Blob[] = [];\n let t0 = 0;\n let current: LivenessSegment | undefined | null = null; // null = not started yet\n let raf = 0;\n let aborted = false;\n\n const now = () => (globalThis.performance?.now?.() ?? Date.now());\n const schedule = (fn: () => void) =>\n typeof requestAnimationFrame === 'function' ? requestAnimationFrame(fn) : (setTimeout(fn, 16) as unknown as number);\n const cancel = (id: number) =>\n typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame(id) : clearTimeout(id);\n\n const tick = () => {\n const elapsed = now() - t0;\n const seg = segmentAt(script, elapsed);\n if (seg !== current) {\n current = seg;\n options.onSegment?.(seg, elapsed);\n }\n options.onTick?.(Math.min(elapsed, total), total);\n if (elapsed >= stopAt) {\n if (recorder.state !== 'inactive') recorder.stop();\n return;\n }\n raf = schedule(tick);\n };\n\n const onAbort = () => {\n aborted = true;\n cancel(raf);\n if (recorder.state !== 'inactive') recorder.stop();\n reject(new AttestIDError('Liveness recording was cancelled.', 'LIVENESS_ABORTED'));\n };\n if (options.signal?.aborted) {\n onAbort();\n return;\n }\n options.signal?.addEventListener('abort', onAbort, { once: true });\n\n recorder.ondataavailable = e => {\n if (e.data && e.data.size > 0) chunks.push(e.data);\n };\n recorder.onerror = () => {\n cancel(raf);\n reject(new AttestIDError('Recording failed.', 'LIVENESS_RECORDING_FAILED'));\n };\n recorder.onstart = () => {\n t0 = now();\n raf = schedule(tick);\n };\n recorder.onstop = () => {\n cancel(raf);\n options.signal?.removeEventListener('abort', onAbort);\n if (aborted) return;\n const type = recorder.mimeType || mimeType || 'video/webm';\n const video = new Blob(chunks, { type });\n if (video.size === 0) {\n reject(new AttestIDError('Recording produced no video.', 'LIVENESS_RECORDING_FAILED'));\n return;\n }\n resolve({ video, mimeType: type, durationMs: Math.round(now() - t0) });\n };\n\n // One chunk per second keeps memory flat on long-ish recordings and means\n // a mid-recording tab freeze still leaves usable data.\n recorder.start(1000);\n });\n}\n","import { CameraError } from './errors';\nimport type { IdType } from './types';\nimport { DOC_ASPECT } from './utils';\n\nexport interface CameraSession {\n stream: MediaStream;\n captureFrame: (frameEl: HTMLElement, videoEl: HTMLVideoElement) => Promise<File>;\n stop: () => void;\n}\n\nexport async function openCameraSession(idType: IdType): Promise<CameraSession> {\n let stream: MediaStream;\n try {\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n facingMode: 'environment',\n // 1080p is already well above what OCR needs for a card-sized\n // document — asking for 4K (the previous ideal) made the browser\n // drive a much heavier live-preview + capture pipeline than\n // necessary, which is a real crash risk on lower-end/older phones.\n width: { ideal: 1920 },\n height: { ideal: 1080 },\n // @ts-expect-error — focusMode not in all TS lib versions\n focusMode: 'continuous',\n },\n });\n } catch (e) {\n throw new CameraError('Camera access denied. Grant permission and try again.');\n }\n\n function captureFrame(frameEl: HTMLElement, videoEl: HTMLVideoElement): Promise<File> {\n return new Promise((resolve, reject) => {\n const vRect = videoEl.getBoundingClientRect();\n const fRect = frameEl.getBoundingClientRect();\n\n // `.camera-preview` renders with `object-fit: cover`: the video is\n // scaled *uniformly* to fill its box and the overflow is cropped\n // centered — it is not stretched independently on x/y. Map the\n // on-screen focus-frame rect back to native video pixels through that\n // same scale + centered-crop transform so the capture matches exactly\n // what's visible inside the frame.\n const scale = Math.max(vRect.width / videoEl.videoWidth, vRect.height / videoEl.videoHeight);\n const offsetX = (videoEl.videoWidth * scale - vRect.width) / 2;\n const offsetY = (videoEl.videoHeight * scale - vRect.height) / 2;\n\n const sx = (offsetX + (fRect.left - vRect.left)) / scale;\n const sy = (offsetY + (fRect.top - vRect.top)) / scale;\n const sw = fRect.width / scale;\n const sh = fRect.height / scale;\n\n const canvas = document.createElement('canvas');\n canvas.width = sw;\n canvas.height = sh;\n canvas.getContext('2d')!.drawImage(videoEl, sx, sy, sw, sh, 0, 0, sw, sh);\n canvas.toBlob(blob => {\n if (!blob) { reject(new CameraError('Failed to capture frame')); return; }\n resolve(new File([blob], 'id-photo.jpg', { type: 'image/jpeg' }));\n }, 'image/jpeg', 0.95);\n });\n }\n\n function stop() {\n stream.getTracks().forEach(t => t.stop());\n }\n\n void idType; // aspect ratio is consumed by the UI layer via DOC_ASPECT\n return { stream, captureFrame, stop };\n}\n\nexport { DOC_ASPECT };\n","/**\n * Verify what AttestID signs. Meant for **your backend**, though it runs\n * anywhere WebCrypto exists (Node 19+, browsers, Deno, edge runtimes).\n *\n * verifyAttestation() the `attestation` on a KYC result (ES256 JWS, SECURITY.md P1)\n * verifyWebhook() an incoming webhook: the HMAC `AttestID-Signature`\n * (any of several secrets, for no-downtime rotation, P4)\n * and/or the asymmetric `AttestID-JWS` header (P1)\n *\n * Public keys come from `${apiUrl}/.well-known/jwks.json` and are cached for\n * 10 minutes, refetched once when an unknown `kid` shows up (key rotation).\n */\nimport { AttestIDError } from './errors';\nimport {\n base64UrlToBytes,\n buf,\n bytesToBase64Url,\n canonicalJson,\n hexToBytes,\n sha256,\n subtle,\n utf8,\n} from './encoding';\nimport type { FetchLike } from './http';\nimport type { AttestationClaims, DocumentInfo } from './types';\n\nexport class SignatureError extends AttestIDError {\n constructor(message: string) {\n super(message, 'SIGNATURE_INVALID');\n this.name = 'SignatureError';\n }\n}\n\ninterface Jwk {\n kty: string;\n crv: string;\n x: string;\n y: string;\n kid: string;\n alg?: string;\n}\n\nconst JWKS_TTL_MS = 10 * 60_000;\nconst jwksCache = new Map<string, { at: number; keys: Map<string, CryptoKey> }>();\n\n/** @internal test hook */\nexport function _clearJwksCache(): void {\n jwksCache.clear();\n}\n\nasync function loadJwks(apiUrl: string, fetchImpl: FetchLike): Promise<Map<string, CryptoKey>> {\n const res = await fetchImpl(`${apiUrl.replace(/\\/$/, '')}/.well-known/jwks.json`);\n if (!res.ok) throw new AttestIDError(`Could not fetch AttestID JWKS (HTTP ${res.status})`, 'JWKS_UNAVAILABLE');\n const { keys } = (await res.json()) as { keys: Jwk[] };\n const out = new Map<string, CryptoKey>();\n for (const k of keys ?? []) {\n if (k.kty !== 'EC' || k.crv !== 'P-256') continue;\n out.set(\n k.kid,\n await subtle().importKey(\n 'jwk',\n { kty: 'EC', crv: 'P-256', x: k.x, y: k.y, ext: true },\n { name: 'ECDSA', namedCurve: 'P-256' },\n false,\n ['verify'],\n ),\n );\n }\n jwksCache.set(apiUrl, { at: Date.now(), keys: out });\n return out;\n}\n\nasync function keyFor(apiUrl: string, kid: string, fetchImpl: FetchLike): Promise<CryptoKey> {\n const cached = jwksCache.get(apiUrl);\n const fresh = !cached || Date.now() - cached.at >= JWKS_TTL_MS;\n let keys = fresh ? await loadJwks(apiUrl, fetchImpl) : cached.keys;\n if (!keys.has(kid) && !fresh) keys = await loadJwks(apiUrl, fetchImpl); // key rotated since caching\n const key = keys.get(kid);\n if (!key) throw new SignatureError(`Unknown AttestID signing key id \"${kid}\"`);\n return key;\n}\n\nfunction parseJson<T>(bytes: Uint8Array, what: string): T {\n try {\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n } catch {\n throw new SignatureError(`Malformed ${what}`);\n }\n}\n\nasync function verifyCompact(\n headerB64: string,\n payloadB64: string,\n sigB64: string,\n opts: { apiUrl: string; fetch?: FetchLike },\n): Promise<Record<string, unknown>> {\n const header = parseJson<Record<string, unknown>>(base64UrlToBytes(headerB64), 'JWS header');\n // Pin the algorithm: never let the token choose (blocks alg=none / HS256 confusion).\n if (header.alg !== 'ES256' || typeof header.kid !== 'string') {\n throw new SignatureError('Unexpected JWS algorithm or missing kid');\n }\n const key = await keyFor(opts.apiUrl, header.kid, opts.fetch ?? fetch);\n const ok = await subtle().verify(\n { name: 'ECDSA', hash: 'SHA-256' },\n key,\n buf(base64UrlToBytes(sigB64)),\n buf(utf8(`${headerB64}.${payloadB64}`)),\n );\n if (!ok) throw new SignatureError('Signature does not verify');\n return header;\n}\n\nexport interface VerifyAttestationOptions {\n /** Your AttestID API base URL. The JWKS is fetched from here. */\n apiUrl: string;\n /** Require the attestation to be for this verification id. */\n verificationId?: string;\n /** Require it to belong to this company id. */\n companyId?: string;\n /**\n * The `document` block you received alongside the attestation. When given,\n * its canonical SHA-256 must match `document_sha256`, which proves the\n * identity data wasn't altered after AttestID issued the result.\n */\n document?: DocumentInfo | Record<string, unknown> | null;\n /** Custom fetch (e.g. one configured for mTLS). */\n fetch?: FetchLike;\n}\n\n/**\n * Verify a KYC result's `attestation` and return its signed claims. Throws\n * `SignatureError` if the signature, the expected ids or the document hash\n * don't match. Trust `claims.verified` / `claims.status`, which are signed,\n * over the unsigned response fields.\n */\nexport async function verifyAttestation(\n attestation: string,\n options: VerifyAttestationOptions,\n): Promise<AttestationClaims> {\n const parts = attestation.split('.');\n if (parts.length !== 3 || !parts[1]) throw new SignatureError('Malformed attestation');\n const header = await verifyCompact(parts[0], parts[1], parts[2], options);\n if (header.typ !== 'attestid-verification+jws') throw new SignatureError('Not an AttestID verification attestation');\n const claims = parseJson<AttestationClaims>(base64UrlToBytes(parts[1]), 'attestation payload');\n\n if (options.verificationId && claims.sub !== options.verificationId) {\n throw new SignatureError('Attestation is for a different verification');\n }\n if (options.companyId && claims.company_id !== options.companyId) {\n throw new SignatureError('Attestation is for a different company');\n }\n if (options.document !== undefined) {\n const expected = options.document\n ? bytesToBase64Url(await sha256(utf8(canonicalJson(options.document))))\n : null;\n if (claims.document_sha256 !== expected) {\n throw new SignatureError('Document data does not match the signed attestation');\n }\n }\n return claims;\n}\n\nexport interface VerifyWebhookOptions {\n /**\n * Your webhook secret(s). Pass both the new and the old secret during a\n * rotation; the delivery verifies if any one of them matches.\n */\n secrets?: string | string[];\n /** Your AttestID API base URL. Enables `AttestID-JWS` verification. */\n apiUrl?: string;\n /** Max age of the signed timestamp in seconds (replay window). Default 300. */\n toleranceSeconds?: number;\n fetch?: FetchLike;\n}\n\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\nfunction header(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === 'function') return (headers as Headers).get(name) ?? undefined;\n const rec = headers as Record<string, string | string[] | undefined>;\n const hit = Object.keys(rec).find(k => k.toLowerCase() === name.toLowerCase());\n const v = hit ? rec[hit] : undefined;\n return Array.isArray(v) ? v[0] : v;\n}\n\nasync function verifyHmac(body: Uint8Array, sigHeader: string, secrets: string[], tolerance: number): Promise<void> {\n const pairs = sigHeader.split(',').map(p => p.split('=', 2) as [string, string]);\n const t = pairs.find(([k]) => k === 't')?.[1];\n const sigs = pairs.filter(([k]) => k === 'v1').map(([, v]) => v);\n if (!t || !sigs.length) throw new SignatureError('Malformed AttestID-Signature header');\n if (Math.abs(Date.now() / 1000 - Number(t)) > tolerance) throw new SignatureError('Webhook timestamp outside tolerance (possible replay)');\n\n const message = new Uint8Array([...utf8(`${t}.`), ...body]);\n for (const secret of secrets) {\n const key = await subtle().importKey('raw', buf(utf8(secret)), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);\n for (const hex of sigs) {\n const sig = hexToBytes(hex);\n // subtle.verify is a constant-time comparison.\n if (sig && (await subtle().verify('HMAC', key, buf(sig), buf(message)))) return;\n }\n }\n throw new SignatureError('AttestID-Signature does not match any configured secret');\n}\n\n/**\n * Verify an incoming AttestID webhook. Pass the **raw** request body\n * (exactly the bytes received, before JSON parsing) and the request headers.\n * Checks the HMAC signature when `secrets` is given and the JWS signature\n * when `apiUrl` is given. At least one is required, and both is best.\n * Returns the parsed payload.\n */\nexport async function verifyWebhook<T = Record<string, unknown>>(\n rawBody: string | Uint8Array | ArrayBuffer,\n headers: HeaderSource,\n options: VerifyWebhookOptions,\n): Promise<T> {\n const body =\n typeof rawBody === 'string' ? utf8(rawBody) : rawBody instanceof Uint8Array ? rawBody : new Uint8Array(rawBody);\n const tolerance = options.toleranceSeconds ?? 300;\n const secrets = options.secrets === undefined ? [] : ([] as string[]).concat(options.secrets);\n if (!secrets.length && !options.apiUrl) {\n throw new AttestIDError('verifyWebhook needs `secrets`, `apiUrl`, or both', 'INVALID_ARGUMENT');\n }\n\n if (secrets.length) {\n const sig = header(headers, 'AttestID-Signature');\n if (!sig) throw new SignatureError('Missing AttestID-Signature header');\n await verifyHmac(body, sig, secrets, tolerance);\n }\n\n if (options.apiUrl) {\n const jws = header(headers, 'AttestID-JWS');\n if (!jws) throw new SignatureError('Missing AttestID-JWS header');\n const parts = jws.split('.');\n if (parts.length !== 3 || parts[1] !== '') throw new SignatureError('Malformed AttestID-JWS (expected detached JWS)');\n const h = await verifyCompact(parts[0], bytesToBase64Url(body), parts[2], { apiUrl: options.apiUrl, fetch: options.fetch });\n if (h.typ !== 'attestid-webhook+jws') throw new SignatureError('Not an AttestID webhook signature');\n if (typeof h.iat !== 'number' || Math.abs(Date.now() / 1000 - h.iat) > tolerance) {\n throw new SignatureError('Webhook JWS iat outside tolerance (possible replay)');\n }\n }\n\n return parseJson<T>(body, 'webhook body');\n}\n","/**\n * Best-effort phone/tablet detection. AttestIDVerifier gates ID document\n * capture on this: a desktop/laptop never gets a \"Take Photo\" button — it's\n * shown a QR/link handoff to continue on a phone instead. A phone's rear\n * camera focuses at card distance and is much harder to spoof than pointing\n * a document at a webcam, so this is a deliberate restriction, not just a\n * UX nicety.\n */\nexport function isMobileDevice(): boolean {\n if (typeof navigator === 'undefined') return false;\n\n // Chromium's User-Agent Client Hints: the most reliable signal when\n // present, since — unlike the UA string — it isn't routinely spoofed by\n // browser extensions or embedded webviews.\n const uaData = (navigator as Navigator & { userAgentData?: { mobile?: boolean } }).userAgentData;\n if (typeof uaData?.mobile === 'boolean') return uaData.mobile;\n\n const ua = navigator.userAgent || '';\n if (/Android|iPhone|iPod|IEMobile|BlackBerry|Opera Mini|Windows Phone/i.test(ua)) return true;\n\n // iPadOS 13+ identifies as \"Macintosh\" with no \"iPad\" token in the UA, but\n // — unlike an actual Mac — reports multi-touch support. That combination\n // is the standard way to tell the two apart.\n if (/iPad/i.test(ua) || (ua.includes('Macintosh') && navigator.maxTouchPoints > 1)) return true;\n\n // Generic touch-primary fallback for anything the checks above missed —\n // a coarse pointer with no hover capability is how a touchscreen-only\n // device (no mouse/trackpad) reports itself.\n if (typeof window !== 'undefined' && window.matchMedia?.('(pointer: coarse) and (hover: none)').matches) {\n return true;\n }\n\n return false;\n}\n","/* \n * QR Code generator library (TypeScript)\n * \n * Copyright (c) Project Nayuki. (MIT License)\n * https://www.nayuki.io/page/qr-code-generator-library\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy of\n * this software and associated documentation files (the \"Software\"), to deal in\n * the Software without restriction, including without limitation the rights to\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n * the Software, and to permit persons to whom the Software is furnished to do so,\n * subject to the following conditions:\n * - The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n * - The Software is provided \"as is\", without warranty of any kind, express or\n * implied, including but not limited to the warranties of merchantability,\n * fitness for a particular purpose and noninfringement. In no event shall the\n * authors or copyright holders be liable for any claim, damages or other\n * liability, whether in an action of contract, tort or otherwise, arising from,\n * out of or in connection with the Software or the use or other dealings in the\n * Software.\n */\n\n// Vendored verbatim from Nayuki's TypeScript QR Code generator library\n// (https://github.com/nayuki/QR-Code-generator, typescript-javascript/qrcodegen.ts)\n// for the mobile-handoff QR code — mechanically restructured only to drop the\n// dotted `namespace qrcodegen.QrCode` / `namespace qrcodegen.QrSegment` merge\n// syntax, which esbuild's declaration-file bundler (used by this package's\n// tsup build) can't emit .d.ts for. No logic was changed.\n\n\"use strict\";\n\n\n\ntype bit = number;\ntype byte = number;\ntype int = number;\n\n\n/*---- QR Code symbol class ----*/\n\n/* \n * A QR Code symbol, which is a type of two-dimension barcode.\n * Invented by Denso Wave and described in the ISO/IEC 18004 standard.\n * Instances of this class represent an immutable square grid of dark and light cells.\n * The class provides static factory functions to create a QR Code from text or binary data.\n * The class covers the QR Code Model 2 specification, supporting all versions (sizes)\n * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.\n * \n * Ways to create a QR Code object:\n * - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary().\n * - Mid level: Custom-make the list of segments and call QrCode.encodeSegments().\n * - Low level: Custom-make the array of data codeword bytes (including\n * segment headers and final padding, excluding error correction codewords),\n * supply the appropriate version number, and call the QrCode() constructor.\n * (Note that all ways require supplying the desired error correction level.)\n */\nexport class QrCode {\n\t\n\t/*-- Static factory functions (high level) --*/\n\t\n\t// Returns a QR Code representing the given Unicode text string at the given error correction level.\n\t// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer\n\t// Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible\n\t// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the\n\t// ecl argument if it can be done without increasing the version.\n\tpublic static encodeText(text: string, ecl: QrCode.Ecc): QrCode {\n\t\tconst segs: Array<QrSegment> = QrSegment.makeSegments(text);\n\t\treturn QrCode.encodeSegments(segs, ecl);\n\t}\n\t\n\t\n\t// Returns a QR Code representing the given binary data at the given error correction level.\n\t// This function always encodes using the binary segment mode, not any text mode. The maximum number of\n\t// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.\n\t// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.\n\tpublic static encodeBinary(data: Readonly<Array<byte>>, ecl: QrCode.Ecc): QrCode {\n\t\tconst seg: QrSegment = QrSegment.makeBytes(data);\n\t\treturn QrCode.encodeSegments([seg], ecl);\n\t}\n\t\n\t\n\t/*-- Static factory functions (mid level) --*/\n\t\n\t// Returns a QR Code representing the given segments with the given encoding parameters.\n\t// The smallest possible QR Code version within the given range is automatically\n\t// chosen for the output. Iff boostEcl is true, then the ECC level of the result\n\t// may be higher than the ecl argument if it can be done without increasing the\n\t// version. The mask number is either between 0 to 7 (inclusive) to force that\n\t// mask, or -1 to automatically choose an appropriate mask (which may be slow).\n\t// This function allows the user to create a custom sequence of segments that switches\n\t// between modes (such as alphanumeric and byte) to encode text in less space.\n\t// This is a mid-level API; the high-level API is encodeText() and encodeBinary().\n\tpublic static encodeSegments(segs: Readonly<Array<QrSegment>>, ecl: QrCode.Ecc,\n\t\t\tminVersion: int = 1, maxVersion: int = 40,\n\t\t\tmask: int = -1, boostEcl: boolean = true): QrCode {\n\t\t\n\t\tif (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)\n\t\t\t\t|| mask < -1 || mask > 7)\n\t\t\tthrow new RangeError(\"Invalid value\");\n\t\t\n\t\t// Find the minimal version number to use\n\t\tlet version: int;\n\t\tlet dataUsedBits: int;\n\t\tfor (version = minVersion; ; version++) {\n\t\t\tconst dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available\n\t\t\tconst usedBits: number = QrSegment.getTotalBits(segs, version);\n\t\t\tif (usedBits <= dataCapacityBits) {\n\t\t\t\tdataUsedBits = usedBits;\n\t\t\t\tbreak; // This version number is found to be suitable\n\t\t\t}\n\t\t\tif (version >= maxVersion) // All versions in the range could not fit the given data\n\t\t\t\tthrow new RangeError(\"Data too long\");\n\t\t}\n\t\t\n\t\t// Increase the error correction level while the data still fits in the current version number\n\t\tfor (const newEcl of [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]) { // From low to high\n\t\t\tif (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8)\n\t\t\t\tecl = newEcl;\n\t\t}\n\t\t\n\t\t// Concatenate all segments to create the data bit string\n\t\tlet bb: Array<bit> = []\n\t\tfor (const seg of segs) {\n\t\t\tappendBits(seg.mode.modeBits, 4, bb);\n\t\t\tappendBits(seg.numChars, seg.mode.numCharCountBits(version), bb);\n\t\t\tfor (const b of seg.getData())\n\t\t\t\tbb.push(b);\n\t\t}\n\t\tassert(bb.length == dataUsedBits);\n\t\t\n\t\t// Add terminator and pad up to a byte if applicable\n\t\tconst dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8;\n\t\tassert(bb.length <= dataCapacityBits);\n\t\tappendBits(0, Math.min(4, dataCapacityBits - bb.length), bb);\n\t\tappendBits(0, (8 - bb.length % 8) % 8, bb);\n\t\tassert(bb.length % 8 == 0);\n\t\t\n\t\t// Pad with alternating bytes until data capacity is reached\n\t\tfor (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11)\n\t\t\tappendBits(padByte, 8, bb);\n\t\t\n\t\t// Pack bits into bytes in big endian\n\t\tlet dataCodewords: Array<byte> = [];\n\t\twhile (dataCodewords.length * 8 < bb.length)\n\t\t\tdataCodewords.push(0);\n\t\tbb.forEach((b: bit, i: int) =>\n\t\t\tdataCodewords[i >>> 3] |= b << (7 - (i & 7)));\n\t\t\n\t\t// Create the QR Code object\n\t\treturn new QrCode(version, ecl, dataCodewords, mask);\n\t}\n\t\n\t\n\t/*-- Fields --*/\n\t\n\t// The width and height of this QR Code, measured in modules, between\n\t// 21 and 177 (inclusive). This is equal to version * 4 + 17.\n\tpublic readonly size: int;\n\t\n\t// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).\n\t// Even if a QR Code is created with automatic masking requested (mask = -1),\n\t// the resulting object still has a mask value between 0 and 7.\n\tpublic readonly mask: int;\n\t\n\t// The modules of this QR Code (false = light, true = dark).\n\t// Immutable after constructor finishes. Accessed through getModule().\n\tprivate readonly modules : Array<Array<boolean>> = [];\n\t\n\t// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.\n\tprivate readonly isFunction: Array<Array<boolean>> = [];\n\t\n\t\n\t/*-- Constructor (low level) and fields --*/\n\t\n\t// Creates a new QR Code with the given version number,\n\t// error correction level, data codeword bytes, and mask number.\n\t// This is a low-level API that most users should not use directly.\n\t// A mid-level API is the encodeSegments() function.\n\tpublic constructor(\n\t\t\t// The version number of this QR Code, which is between 1 and 40 (inclusive).\n\t\t\t// This determines the size of this barcode.\n\t\t\tpublic readonly version: int,\n\t\t\t\n\t\t\t// The error correction level used in this QR Code.\n\t\t\tpublic readonly errorCorrectionLevel: QrCode.Ecc,\n\t\t\t\n\t\t\tdataCodewords: Readonly<Array<byte>>,\n\t\t\t\n\t\t\tmsk: int) {\n\t\t\n\t\t// Check scalar arguments\n\t\tif (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION)\n\t\t\tthrow new RangeError(\"Version value out of range\");\n\t\tif (msk < -1 || msk > 7)\n\t\t\tthrow new RangeError(\"Mask value out of range\");\n\t\tthis.size = version * 4 + 17;\n\t\t\n\t\t// Initialize both grids to be size*size arrays of Boolean false\n\t\tlet row: Array<boolean> = [];\n\t\tfor (let i = 0; i < this.size; i++)\n\t\t\trow.push(false);\n\t\tfor (let i = 0; i < this.size; i++) {\n\t\t\tthis.modules .push(row.slice()); // Initially all light\n\t\t\tthis.isFunction.push(row.slice());\n\t\t}\n\t\t\n\t\t// Compute ECC, draw modules\n\t\tthis.drawFunctionPatterns();\n\t\tconst allCodewords: Array<byte> = this.addEccAndInterleave(dataCodewords);\n\t\tthis.drawCodewords(allCodewords);\n\t\t\n\t\t// Do masking\n\t\tif (msk == -1) { // Automatically choose best mask\n\t\t\tlet minPenalty: int = 1000000000;\n\t\t\tfor (let i = 0; i < 8; i++) {\n\t\t\t\tthis.applyMask(i);\n\t\t\t\tthis.drawFormatBits(i);\n\t\t\t\tconst penalty: int = this.getPenaltyScore();\n\t\t\t\tif (penalty < minPenalty) {\n\t\t\t\t\tmsk = i;\n\t\t\t\t\tminPenalty = penalty;\n\t\t\t\t}\n\t\t\t\tthis.applyMask(i); // Undoes the mask due to XOR\n\t\t\t}\n\t\t}\n\t\tassert(0 <= msk && msk <= 7);\n\t\tthis.mask = msk;\n\t\tthis.applyMask(msk); // Apply the final choice of mask\n\t\tthis.drawFormatBits(msk); // Overwrite old format bits\n\t\t\n\t\tthis.isFunction = [];\n\t}\n\t\n\t\n\t/*-- Accessor methods --*/\n\t\n\t// Returns the color of the module (pixel) at the given coordinates, which is false\n\t// for light or true for dark. The top left corner has the coordinates (x=0, y=0).\n\t// If the given coordinates are out of bounds, then false (light) is returned.\n\tpublic getModule(x: int, y: int): boolean {\n\t\treturn 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x];\n\t}\n\t\n\t\n\t/*-- Private helper methods for constructor: Drawing function modules --*/\n\t\n\t// Reads this object's version field, and draws and marks all function modules.\n\tprivate drawFunctionPatterns(): void {\n\t\t// Draw horizontal and vertical timing patterns\n\t\tfor (let i = 0; i < this.size; i++) {\n\t\t\tthis.setFunctionModule(6, i, i % 2 == 0);\n\t\t\tthis.setFunctionModule(i, 6, i % 2 == 0);\n\t\t}\n\t\t\n\t\t// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)\n\t\tthis.drawFinderPattern(3, 3);\n\t\tthis.drawFinderPattern(this.size - 4, 3);\n\t\tthis.drawFinderPattern(3, this.size - 4);\n\t\t\n\t\t// Draw numerous alignment patterns\n\t\tconst alignPatPos: Array<int> = this.getAlignmentPatternPositions();\n\t\tconst numAlign: int = alignPatPos.length;\n\t\tfor (let i = 0; i < numAlign; i++) {\n\t\t\tfor (let j = 0; j < numAlign; j++) {\n\t\t\t\t// Don't draw on the three finder corners\n\t\t\t\tif (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0))\n\t\t\t\t\tthis.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Draw configuration data\n\t\tthis.drawFormatBits(0); // Dummy mask value; overwritten later in the constructor\n\t\tthis.drawVersion();\n\t}\n\t\n\t\n\t// Draws two copies of the format bits (with its own error correction code)\n\t// based on the given mask and this object's error correction level field.\n\tprivate drawFormatBits(mask: int): void {\n\t\t// Calculate error correction code and pack bits\n\t\tconst data: int = this.errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3\n\t\tlet rem: int = data;\n\t\tfor (let i = 0; i < 10; i++)\n\t\t\trem = (rem << 1) ^ ((rem >>> 9) * 0x537);\n\t\tconst bits = (data << 10 | rem) ^ 0x5412; // uint15\n\t\tassert(bits >>> 15 == 0);\n\t\t\n\t\t// Draw first copy\n\t\tfor (let i = 0; i <= 5; i++)\n\t\t\tthis.setFunctionModule(8, i, getBit(bits, i));\n\t\tthis.setFunctionModule(8, 7, getBit(bits, 6));\n\t\tthis.setFunctionModule(8, 8, getBit(bits, 7));\n\t\tthis.setFunctionModule(7, 8, getBit(bits, 8));\n\t\tfor (let i = 9; i < 15; i++)\n\t\t\tthis.setFunctionModule(14 - i, 8, getBit(bits, i));\n\t\t\n\t\t// Draw second copy\n\t\tfor (let i = 0; i < 8; i++)\n\t\t\tthis.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));\n\t\tfor (let i = 8; i < 15; i++)\n\t\t\tthis.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));\n\t\tthis.setFunctionModule(8, this.size - 8, true); // Always dark\n\t}\n\t\n\t\n\t// Draws two copies of the version bits (with its own error correction code),\n\t// based on this object's version field, iff 7 <= version <= 40.\n\tprivate drawVersion(): void {\n\t\tif (this.version < 7)\n\t\t\treturn;\n\t\t\n\t\t// Calculate error correction code and pack bits\n\t\tlet rem: int = this.version; // version is uint6, in the range [7, 40]\n\t\tfor (let i = 0; i < 12; i++)\n\t\t\trem = (rem << 1) ^ ((rem >>> 11) * 0x1F25);\n\t\tconst bits: int = this.version << 12 | rem; // uint18\n\t\tassert(bits >>> 18 == 0);\n\t\t\n\t\t// Draw two copies\n\t\tfor (let i = 0; i < 18; i++) {\n\t\t\tconst color: boolean = getBit(bits, i);\n\t\t\tconst a: int = this.size - 11 + i % 3;\n\t\t\tconst b: int = Math.floor(i / 3);\n\t\t\tthis.setFunctionModule(a, b, color);\n\t\t\tthis.setFunctionModule(b, a, color);\n\t\t}\n\t}\n\t\n\t\n\t// Draws a 9*9 finder pattern including the border separator,\n\t// with the center module at (x, y). Modules can be out of bounds.\n\tprivate drawFinderPattern(x: int, y: int): void {\n\t\tfor (let dy = -4; dy <= 4; dy++) {\n\t\t\tfor (let dx = -4; dx <= 4; dx++) {\n\t\t\t\tconst dist: int = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm\n\t\t\t\tconst xx: int = x + dx;\n\t\t\t\tconst yy: int = y + dy;\n\t\t\t\tif (0 <= xx && xx < this.size && 0 <= yy && yy < this.size)\n\t\t\t\t\tthis.setFunctionModule(xx, yy, dist != 2 && dist != 4);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t// Draws a 5*5 alignment pattern, with the center module\n\t// at (x, y). All modules must be in bounds.\n\tprivate drawAlignmentPattern(x: int, y: int): void {\n\t\tfor (let dy = -2; dy <= 2; dy++) {\n\t\t\tfor (let dx = -2; dx <= 2; dx++)\n\t\t\t\tthis.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1);\n\t\t}\n\t}\n\t\n\t\n\t// Sets the color of a module and marks it as a function module.\n\t// Only used by the constructor. Coordinates must be in bounds.\n\tprivate setFunctionModule(x: int, y: int, isDark: boolean): void {\n\t\tthis.modules[y][x] = isDark;\n\t\tthis.isFunction[y][x] = true;\n\t}\n\t\n\t\n\t/*-- Private helper methods for constructor: Codewords and masking --*/\n\t\n\t// Returns a new byte string representing the given data with the appropriate error correction\n\t// codewords appended to it, based on this object's version and error correction level.\n\tprivate addEccAndInterleave(data: Readonly<Array<byte>>): Array<byte> {\n\t\tconst ver: int = this.version;\n\t\tconst ecl: QrCode.Ecc = this.errorCorrectionLevel;\n\t\tif (data.length != QrCode.getNumDataCodewords(ver, ecl))\n\t\t\tthrow new RangeError(\"Invalid argument\");\n\t\t\n\t\t// Calculate parameter numbers\n\t\tconst numBlocks: int = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];\n\t\tconst blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver];\n\t\tconst rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8);\n\t\tconst numShortBlocks: int = numBlocks - rawCodewords % numBlocks;\n\t\tconst shortBlockLen: int = Math.floor(rawCodewords / numBlocks);\n\t\t\n\t\t// Split data into blocks and append ECC to each block\n\t\tlet blocks: Array<Array<byte>> = [];\n\t\tconst rsDiv: Array<byte> = QrCode.reedSolomonComputeDivisor(blockEccLen);\n\t\tfor (let i = 0, k = 0; i < numBlocks; i++) {\n\t\t\tlet dat: Array<byte> = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));\n\t\t\tk += dat.length;\n\t\t\tconst ecc: Array<byte> = QrCode.reedSolomonComputeRemainder(dat, rsDiv);\n\t\t\tif (i < numShortBlocks)\n\t\t\t\tdat.push(0);\n\t\t\tblocks.push(dat.concat(ecc));\n\t\t}\n\t\t\n\t\t// Interleave (not concatenate) the bytes from every block into a single sequence\n\t\tlet result: Array<byte> = [];\n\t\tfor (let i = 0; i < blocks[0].length; i++) {\n\t\t\tblocks.forEach((block, j) => {\n\t\t\t\t// Skip the padding byte in short blocks\n\t\t\t\tif (i != shortBlockLen - blockEccLen || j >= numShortBlocks)\n\t\t\t\t\tresult.push(block[i]);\n\t\t\t});\n\t\t}\n\t\tassert(result.length == rawCodewords);\n\t\treturn result;\n\t}\n\t\n\t\n\t// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire\n\t// data area of this QR Code. Function modules need to be marked off before this is called.\n\tprivate drawCodewords(data: Readonly<Array<byte>>): void {\n\t\tif (data.length != Math.floor(QrCode.getNumRawDataModules(this.version) / 8))\n\t\t\tthrow new RangeError(\"Invalid argument\");\n\t\tlet i: int = 0; // Bit index into the data\n\t\t// Do the funny zigzag scan\n\t\tfor (let right = this.size - 1; right >= 1; right -= 2) { // Index of right column in each column pair\n\t\t\tif (right == 6)\n\t\t\t\tright = 5;\n\t\t\tfor (let vert = 0; vert < this.size; vert++) { // Vertical counter\n\t\t\t\tfor (let j = 0; j < 2; j++) {\n\t\t\t\t\tconst x: int = right - j; // Actual x coordinate\n\t\t\t\t\tconst upward: boolean = ((right + 1) & 2) == 0;\n\t\t\t\t\tconst y: int = upward ? this.size - 1 - vert : vert; // Actual y coordinate\n\t\t\t\t\tif (!this.isFunction[y][x] && i < data.length * 8) {\n\t\t\t\t\t\tthis.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7));\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t\t// If this QR Code has any remainder bits (0 to 7), they were assigned as\n\t\t\t\t\t// 0/false/light by the constructor and are left unchanged by this method\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tassert(i == data.length * 8);\n\t}\n\t\n\t\n\t// XORs the codeword modules in this QR Code with the given mask pattern.\n\t// The function modules must be marked and the codeword bits must be drawn\n\t// before masking. Due to the arithmetic of XOR, calling applyMask() with\n\t// the same mask value a second time will undo the mask. A final well-formed\n\t// QR Code needs exactly one (not zero, two, etc.) mask applied.\n\tprivate applyMask(mask: int): void {\n\t\tif (mask < 0 || mask > 7)\n\t\t\tthrow new RangeError(\"Mask value out of range\");\n\t\tfor (let y = 0; y < this.size; y++) {\n\t\t\tfor (let x = 0; x < this.size; x++) {\n\t\t\t\tlet invert: boolean;\n\t\t\t\tswitch (mask) {\n\t\t\t\t\tcase 0: invert = (x + y) % 2 == 0; break;\n\t\t\t\t\tcase 1: invert = y % 2 == 0; break;\n\t\t\t\t\tcase 2: invert = x % 3 == 0; break;\n\t\t\t\t\tcase 3: invert = (x + y) % 3 == 0; break;\n\t\t\t\t\tcase 4: invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 == 0; break;\n\t\t\t\t\tcase 5: invert = x * y % 2 + x * y % 3 == 0; break;\n\t\t\t\t\tcase 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;\n\t\t\t\t\tcase 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;\n\t\t\t\t\tdefault: throw new Error(\"Unreachable\");\n\t\t\t\t}\n\t\t\t\tif (!this.isFunction[y][x] && invert)\n\t\t\t\t\tthis.modules[y][x] = !this.modules[y][x];\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t// Calculates and returns the penalty score based on state of this QR Code's current modules.\n\t// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.\n\tprivate getPenaltyScore(): int {\n\t\tlet result: int = 0;\n\t\t\n\t\t// Adjacent modules in row having same color, and finder-like patterns\n\t\tfor (let y = 0; y < this.size; y++) {\n\t\t\tlet runColor = false;\n\t\t\tlet runX = 0;\n\t\t\tlet runHistory = [0,0,0,0,0,0,0];\n\t\t\tfor (let x = 0; x < this.size; x++) {\n\t\t\t\tif (this.modules[y][x] == runColor) {\n\t\t\t\t\trunX++;\n\t\t\t\t\tif (runX == 5)\n\t\t\t\t\t\tresult += QrCode.PENALTY_N1;\n\t\t\t\t\telse if (runX > 5)\n\t\t\t\t\t\tresult++;\n\t\t\t\t} else {\n\t\t\t\t\tthis.finderPenaltyAddHistory(runX, runHistory);\n\t\t\t\t\tif (!runColor)\n\t\t\t\t\t\tresult += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;\n\t\t\t\t\trunColor = this.modules[y][x];\n\t\t\t\t\trunX = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult += this.finderPenaltyTerminateAndCount(runColor, runX, runHistory) * QrCode.PENALTY_N3;\n\t\t}\n\t\t// Adjacent modules in column having same color, and finder-like patterns\n\t\tfor (let x = 0; x < this.size; x++) {\n\t\t\tlet runColor = false;\n\t\t\tlet runY = 0;\n\t\t\tlet runHistory = [0,0,0,0,0,0,0];\n\t\t\tfor (let y = 0; y < this.size; y++) {\n\t\t\t\tif (this.modules[y][x] == runColor) {\n\t\t\t\t\trunY++;\n\t\t\t\t\tif (runY == 5)\n\t\t\t\t\t\tresult += QrCode.PENALTY_N1;\n\t\t\t\t\telse if (runY > 5)\n\t\t\t\t\t\tresult++;\n\t\t\t\t} else {\n\t\t\t\t\tthis.finderPenaltyAddHistory(runY, runHistory);\n\t\t\t\t\tif (!runColor)\n\t\t\t\t\t\tresult += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;\n\t\t\t\t\trunColor = this.modules[y][x];\n\t\t\t\t\trunY = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult += this.finderPenaltyTerminateAndCount(runColor, runY, runHistory) * QrCode.PENALTY_N3;\n\t\t}\n\t\t\n\t\t// 2*2 blocks of modules having same color\n\t\tfor (let y = 0; y < this.size - 1; y++) {\n\t\t\tfor (let x = 0; x < this.size - 1; x++) {\n\t\t\t\tconst color: boolean = this.modules[y][x];\n\t\t\t\tif ( color == this.modules[y][x + 1] &&\n\t\t\t\t color == this.modules[y + 1][x] &&\n\t\t\t\t color == this.modules[y + 1][x + 1])\n\t\t\t\t\tresult += QrCode.PENALTY_N2;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Balance of dark and light modules\n\t\tlet dark: int = 0;\n\t\tfor (const row of this.modules)\n\t\t\tdark = row.reduce((sum, color) => sum + (color ? 1 : 0), dark);\n\t\tconst total: int = this.size * this.size; // Note that size is odd, so dark/total != 1/2\n\t\t// Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%\n\t\tconst k: int = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1;\n\t\tassert(0 <= k && k <= 9);\n\t\tresult += k * QrCode.PENALTY_N4;\n\t\tassert(0 <= result && result <= 2568888); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4\n\t\treturn result;\n\t}\n\t\n\t\n\t/*-- Private helper functions --*/\n\t\n\t// Returns an ascending list of positions of alignment patterns for this version number.\n\t// Each position is in the range [0,177), and are used on both the x and y axes.\n\t// This could be implemented as lookup table of 40 variable-length lists of integers.\n\tprivate getAlignmentPatternPositions(): Array<int> {\n\t\tif (this.version == 1)\n\t\t\treturn [];\n\t\telse {\n\t\t\tconst numAlign: int = Math.floor(this.version / 7) + 2;\n\t\t\tconst step: int = Math.floor((this.version * 8 + numAlign * 3 + 5) / (numAlign * 4 - 4)) * 2;\n\t\t\tlet result: Array<int> = [6];\n\t\t\tfor (let pos = this.size - 7; result.length < numAlign; pos -= step)\n\t\t\t\tresult.splice(1, 0, pos);\n\t\t\treturn result;\n\t\t}\n\t}\n\t\n\t\n\t// Returns the number of data bits that can be stored in a QR Code of the given version number, after\n\t// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.\n\t// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.\n\tprivate static getNumRawDataModules(ver: int): int {\n\t\tif (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION)\n\t\t\tthrow new RangeError(\"Version number out of range\");\n\t\tlet result: int = (16 * ver + 128) * ver + 64;\n\t\tif (ver >= 2) {\n\t\t\tconst numAlign: int = Math.floor(ver / 7) + 2;\n\t\t\tresult -= (25 * numAlign - 10) * numAlign - 55;\n\t\t\tif (ver >= 7)\n\t\t\t\tresult -= 36;\n\t\t}\n\t\tassert(208 <= result && result <= 29648);\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any\n\t// QR Code of the given version number and error correction level, with remainder bits discarded.\n\t// This stateless pure function could be implemented as a (40*4)-cell lookup table.\n\tprivate static getNumDataCodewords(ver: int, ecl: QrCode.Ecc): int {\n\t\treturn Math.floor(QrCode.getNumRawDataModules(ver) / 8) -\n\t\t\tQrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] *\n\t\t\tQrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];\n\t}\n\t\n\t\n\t// Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be\n\t// implemented as a lookup table over all possible parameter values, instead of as an algorithm.\n\tprivate static reedSolomonComputeDivisor(degree: int): Array<byte> {\n\t\tif (degree < 1 || degree > 255)\n\t\t\tthrow new RangeError(\"Degree out of range\");\n\t\t// Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.\n\t\t// For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].\n\t\tlet result: Array<byte> = [];\n\t\tfor (let i = 0; i < degree - 1; i++)\n\t\t\tresult.push(0);\n\t\tresult.push(1); // Start off with the monomial x^0\n\t\t\n\t\t// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),\n\t\t// and drop the highest monomial term which is always 1x^degree.\n\t\t// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).\n\t\tlet root = 1;\n\t\tfor (let i = 0; i < degree; i++) {\n\t\t\t// Multiply the current product by (x - r^i)\n\t\t\tfor (let j = 0; j < result.length; j++) {\n\t\t\t\tresult[j] = QrCode.reedSolomonMultiply(result[j], root);\n\t\t\t\tif (j + 1 < result.length)\n\t\t\t\t\tresult[j] ^= result[j + 1];\n\t\t\t}\n\t\t\troot = QrCode.reedSolomonMultiply(root, 0x02);\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.\n\tprivate static reedSolomonComputeRemainder(data: Readonly<Array<byte>>, divisor: Readonly<Array<byte>>): Array<byte> {\n\t\tlet result: Array<byte> = divisor.map(_ => 0);\n\t\tfor (const b of data) { // Polynomial division\n\t\t\tconst factor: byte = b ^ (result.shift() as byte);\n\t\t\tresult.push(0);\n\t\t\tdivisor.forEach((coef, i) =>\n\t\t\t\tresult[i] ^= QrCode.reedSolomonMultiply(coef, factor));\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result\n\t// are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.\n\tprivate static reedSolomonMultiply(x: byte, y: byte): byte {\n\t\tif (x >>> 8 != 0 || y >>> 8 != 0)\n\t\t\tthrow new RangeError(\"Byte out of range\");\n\t\t// Russian peasant multiplication\n\t\tlet z: int = 0;\n\t\tfor (let i = 7; i >= 0; i--) {\n\t\t\tz = (z << 1) ^ ((z >>> 7) * 0x11D);\n\t\t\tz ^= ((y >>> i) & 1) * x;\n\t\t}\n\t\tassert(z >>> 8 == 0);\n\t\treturn z as byte;\n\t}\n\t\n\t\n\t// Can only be called immediately after a light run is added, and\n\t// returns either 0, 1, or 2. A helper function for getPenaltyScore().\n\tprivate finderPenaltyCountPatterns(runHistory: Readonly<Array<int>>): int {\n\t\tconst n: int = runHistory[1];\n\t\tassert(n <= this.size * 3);\n\t\tconst core: boolean = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;\n\t\treturn (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)\n\t\t + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);\n\t}\n\t\n\t\n\t// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().\n\tprivate finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array<int>): int {\n\t\tif (currentRunColor) { // Terminate dark run\n\t\t\tthis.finderPenaltyAddHistory(currentRunLength, runHistory);\n\t\t\tcurrentRunLength = 0;\n\t\t}\n\t\tcurrentRunLength += this.size; // Add light border to final run\n\t\tthis.finderPenaltyAddHistory(currentRunLength, runHistory);\n\t\treturn this.finderPenaltyCountPatterns(runHistory);\n\t}\n\t\n\t\n\t// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().\n\tprivate finderPenaltyAddHistory(currentRunLength: int, runHistory: Array<int>): void {\n\t\tif (runHistory[0] == 0)\n\t\t\tcurrentRunLength += this.size; // Add light border to initial run\n\t\trunHistory.pop();\n\t\trunHistory.unshift(currentRunLength);\n\t}\n\t\n\t\n\t/*-- Constants and tables --*/\n\t\n\t// The minimum version number supported in the QR Code Model 2 standard.\n\tpublic static readonly MIN_VERSION: int = 1;\n\t// The maximum version number supported in the QR Code Model 2 standard.\n\tpublic static readonly MAX_VERSION: int = 40;\n\t\n\t// For use in getPenaltyScore(), when evaluating which mask is best.\n\tprivate static readonly PENALTY_N1: int = 3;\n\tprivate static readonly PENALTY_N2: int = 3;\n\tprivate static readonly PENALTY_N3: int = 40;\n\tprivate static readonly PENALTY_N4: int = 10;\n\t\n\tprivate static readonly ECC_CODEWORDS_PER_BLOCK: Array<Array<int>> = [\n\t\t// Version: (note that index 0 is for padding, and is set to an illegal value)\n\t\t//0, 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 Error correction level\n\t\t[-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low\n\t\t[-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium\n\t\t[-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile\n\t\t[-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High\n\t];\n\t\n\tprivate static readonly NUM_ERROR_CORRECTION_BLOCKS: Array<Array<int>> = [\n\t\t// Version: (note that index 0 is for padding, and is set to an illegal value)\n\t\t//0, 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 Error correction level\n\t\t[-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low\n\t\t[-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium\n\t\t[-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile\n\t\t[-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High\n\t];\n\t\n}\n\n\n// Appends the given number of low-order bits of the given value\n// to the given buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len.\nfunction appendBits(val: int, len: int, bb: Array<bit>): void {\n\tif (len < 0 || len > 31 || val >>> len != 0)\n\t\tthrow new RangeError(\"Value out of range\");\n\tfor (let i = len - 1; i >= 0; i--) // Append bit by bit\n\t\tbb.push((val >>> i) & 1);\n}\n\n\n// Returns true iff the i'th bit of x is set to 1.\nfunction getBit(x: int, i: int): boolean {\n\treturn ((x >>> i) & 1) != 0;\n}\n\n\n// Throws an exception if the given condition is false.\nfunction assert(cond: boolean): void {\n\tif (!cond)\n\t\tthrow new Error(\"Assertion error\");\n}\n\n\n\n/*---- Data segment class ----*/\n\n/* \n * A segment of character/binary/control data in a QR Code symbol.\n * Instances of this class are immutable.\n * The mid-level way to create a segment is to take the payload data\n * and call a static factory function such as QrSegment.makeNumeric().\n * The low-level way to create a segment is to custom-make the bit buffer\n * and call the QrSegment() constructor with appropriate values.\n * This segment class imposes no length restrictions, but QR Codes have restrictions.\n * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.\n * Any segment longer than this is meaningless for the purpose of generating QR Codes.\n */\nexport class QrSegment {\n\t\n\t/*-- Static factory functions (mid level) --*/\n\t\n\t// Returns a segment representing the given binary data encoded in\n\t// byte mode. All input byte arrays are acceptable. Any text string\n\t// can be converted to UTF-8 bytes and encoded as a byte mode segment.\n\tpublic static makeBytes(data: Readonly<Array<byte>>): QrSegment {\n\t\tlet bb: Array<bit> = []\n\t\tfor (const b of data)\n\t\t\tappendBits(b, 8, bb);\n\t\treturn new QrSegment(QrSegment.Mode.BYTE, data.length, bb);\n\t}\n\t\n\t\n\t// Returns a segment representing the given string of decimal digits encoded in numeric mode.\n\tpublic static makeNumeric(digits: string): QrSegment {\n\t\tif (!QrSegment.isNumeric(digits))\n\t\t\tthrow new RangeError(\"String contains non-numeric characters\");\n\t\tlet bb: Array<bit> = []\n\t\tfor (let i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration\n\t\t\tconst n: int = Math.min(digits.length - i, 3);\n\t\t\tappendBits(parseInt(digits.substring(i, i + n), 10), n * 3 + 1, bb);\n\t\t\ti += n;\n\t\t}\n\t\treturn new QrSegment(QrSegment.Mode.NUMERIC, digits.length, bb);\n\t}\n\t\n\t\n\t// Returns a segment representing the given text string encoded in alphanumeric mode.\n\t// The characters allowed are: 0 to 9, A to Z (uppercase only), space,\n\t// dollar, percent, asterisk, plus, hyphen, period, slash, colon.\n\tpublic static makeAlphanumeric(text: string): QrSegment {\n\t\tif (!QrSegment.isAlphanumeric(text))\n\t\t\tthrow new RangeError(\"String contains unencodable characters in alphanumeric mode\");\n\t\tlet bb: Array<bit> = []\n\t\tlet i: int;\n\t\tfor (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2\n\t\t\tlet temp: int = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;\n\t\t\ttemp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));\n\t\t\tappendBits(temp, 11, bb);\n\t\t}\n\t\tif (i < text.length) // 1 character remaining\n\t\t\tappendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6, bb);\n\t\treturn new QrSegment(QrSegment.Mode.ALPHANUMERIC, text.length, bb);\n\t}\n\t\n\t\n\t// Returns a new mutable list of zero or more segments to represent the given Unicode text string.\n\t// The result may use various segment modes and switch modes to optimize the length of the bit stream.\n\tpublic static makeSegments(text: string): Array<QrSegment> {\n\t\t// Select the most efficient segment encoding automatically\n\t\tif (text == \"\")\n\t\t\treturn [];\n\t\telse if (QrSegment.isNumeric(text))\n\t\t\treturn [QrSegment.makeNumeric(text)];\n\t\telse if (QrSegment.isAlphanumeric(text))\n\t\t\treturn [QrSegment.makeAlphanumeric(text)];\n\t\telse\n\t\t\treturn [QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))];\n\t}\n\t\n\t\n\t// Returns a segment representing an Extended Channel Interpretation\n\t// (ECI) designator with the given assignment value.\n\tpublic static makeEci(assignVal: int): QrSegment {\n\t\tlet bb: Array<bit> = []\n\t\tif (assignVal < 0)\n\t\t\tthrow new RangeError(\"ECI assignment value out of range\");\n\t\telse if (assignVal < (1 << 7))\n\t\t\tappendBits(assignVal, 8, bb);\n\t\telse if (assignVal < (1 << 14)) {\n\t\t\tappendBits(0b10, 2, bb);\n\t\t\tappendBits(assignVal, 14, bb);\n\t\t} else if (assignVal < 1000000) {\n\t\t\tappendBits(0b110, 3, bb);\n\t\t\tappendBits(assignVal, 21, bb);\n\t\t} else\n\t\t\tthrow new RangeError(\"ECI assignment value out of range\");\n\t\treturn new QrSegment(QrSegment.Mode.ECI, 0, bb);\n\t}\n\t\n\t\n\t// Tests whether the given string can be encoded as a segment in numeric mode.\n\t// A string is encodable iff each character is in the range 0 to 9.\n\tpublic static isNumeric(text: string): boolean {\n\t\treturn QrSegment.NUMERIC_REGEX.test(text);\n\t}\n\t\n\t\n\t// Tests whether the given string can be encoded as a segment in alphanumeric mode.\n\t// A string is encodable iff each character is in the following set: 0 to 9, A to Z\n\t// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.\n\tpublic static isAlphanumeric(text: string): boolean {\n\t\treturn QrSegment.ALPHANUMERIC_REGEX.test(text);\n\t}\n\t\n\t\n\t/*-- Constructor (low level) and fields --*/\n\t\n\t// Creates a new QR Code segment with the given attributes and data.\n\t// The character count (numChars) must agree with the mode and the bit buffer length,\n\t// but the constraint isn't checked. The given bit buffer is cloned and stored.\n\tpublic constructor(\n\t\t\t// The mode indicator of this segment.\n\t\t\tpublic readonly mode: QrSegment.Mode,\n\t\t\t\n\t\t\t// The length of this segment's unencoded data. Measured in characters for\n\t\t\t// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.\n\t\t\t// Always zero or positive. Not the same as the data's bit length.\n\t\t\tpublic readonly numChars: int,\n\t\t\t\n\t\t\t// The data bits of this segment. Accessed through getData().\n\t\t\tprivate readonly bitData: Array<bit>) {\n\t\t\n\t\tif (numChars < 0)\n\t\t\tthrow new RangeError(\"Invalid argument\");\n\t\tthis.bitData = bitData.slice(); // Make defensive copy\n\t}\n\t\n\t\n\t/*-- Methods --*/\n\t\n\t// Returns a new copy of the data bits of this segment.\n\tpublic getData(): Array<bit> {\n\t\treturn this.bitData.slice(); // Make defensive copy\n\t}\n\t\n\t\n\t// (Package-private) Calculates and returns the number of bits needed to encode the given segments at\n\t// the given version. The result is infinity if a segment has too many characters to fit its length field.\n\tpublic static getTotalBits(segs: Readonly<Array<QrSegment>>, version: int): number {\n\t\tlet result: number = 0;\n\t\tfor (const seg of segs) {\n\t\t\tconst ccbits: int = seg.mode.numCharCountBits(version);\n\t\t\tif (seg.numChars >= (1 << ccbits))\n\t\t\t\treturn Infinity; // The segment's length doesn't fit the field's bit width\n\t\t\tresult += 4 + ccbits + seg.bitData.length;\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t// Returns a new array of bytes representing the given string encoded in UTF-8.\n\tprivate static toUtf8ByteArray(str: string): Array<byte> {\n\t\tstr = encodeURI(str);\n\t\tlet result: Array<byte> = [];\n\t\tfor (let i = 0; i < str.length; i++) {\n\t\t\tif (str.charAt(i) != \"%\")\n\t\t\t\tresult.push(str.charCodeAt(i));\n\t\t\telse {\n\t\t\t\tresult.push(parseInt(str.substring(i + 1, i + 3), 16));\n\t\t\t\ti += 2;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\t\n\t\n\t/*-- Constants --*/\n\t\n\t// Describes precisely all strings that are encodable in numeric mode.\n\tprivate static readonly NUMERIC_REGEX: RegExp = /^[0-9]*$/;\n\t\n\t// Describes precisely all strings that are encodable in alphanumeric mode.\n\tprivate static readonly ALPHANUMERIC_REGEX: RegExp = /^[A-Z0-9 $%*+.\\/:-]*$/;\n\t\n\t// The set of all legal characters in alphanumeric mode,\n\t// where each character value maps to the index in the string.\n\tprivate static readonly ALPHANUMERIC_CHARSET: string = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:\";\n\t\n}\n\n\n\n\n/*---- Public helper enumeration ----*/\n\nexport namespace QrCode {\n\t\n\ttype int = number;\n\t\n\t\n\t/* \n\t * The error correction level in a QR Code symbol. Immutable.\n\t */\n\texport class Ecc {\n\t\t\n\t\t/*-- Constants --*/\n\t\t\n\t\tpublic static readonly LOW = new Ecc(0, 1); // The QR Code can tolerate about 7% erroneous codewords\n\t\tpublic static readonly MEDIUM = new Ecc(1, 0); // The QR Code can tolerate about 15% erroneous codewords\n\t\tpublic static readonly QUARTILE = new Ecc(2, 3); // The QR Code can tolerate about 25% erroneous codewords\n\t\tpublic static readonly HIGH = new Ecc(3, 2); // The QR Code can tolerate about 30% erroneous codewords\n\t\t\n\t\t\n\t\t/*-- Constructor and fields --*/\n\t\t\n\t\tprivate constructor(\n\t\t\t// In the range 0 to 3 (unsigned 2-bit integer).\n\t\t\tpublic readonly ordinal: int,\n\t\t\t// (Package-private) In the range 0 to 3 (unsigned 2-bit integer).\n\t\t\tpublic readonly formatBits: int) {}\n\t\t\n\t}\n}\n\n\n\n/*---- Public helper enumeration ----*/\n\nexport namespace QrSegment {\n\t\n\ttype int = number;\n\t\n\t\n\t/* \n\t * Describes how a segment's data bits are interpreted. Immutable.\n\t */\n\texport class Mode {\n\t\t\n\t\t/*-- Constants --*/\n\t\t\n\t\tpublic static readonly NUMERIC = new Mode(0x1, [10, 12, 14]);\n\t\tpublic static readonly ALPHANUMERIC = new Mode(0x2, [ 9, 11, 13]);\n\t\tpublic static readonly BYTE = new Mode(0x4, [ 8, 16, 16]);\n\t\tpublic static readonly KANJI = new Mode(0x8, [ 8, 10, 12]);\n\t\tpublic static readonly ECI = new Mode(0x7, [ 0, 0, 0]);\n\t\t\n\t\t\n\t\t/*-- Constructor and fields --*/\n\t\t\n\t\tprivate constructor(\n\t\t\t// The mode indicator bits, which is a uint4 value (range 0 to 15).\n\t\t\tpublic readonly modeBits: int,\n\t\t\t// Number of character count bits for three different version ranges.\n\t\t\tprivate readonly numBitsCharCount: [int,int,int]) {}\n\t\t\n\t\t\n\t\t/*-- Method --*/\n\t\t\n\t\t// (Package-private) Returns the bit width of the character count field for a segment in\n\t\t// this mode in a QR Code at the given version number. The result is in the range [0, 16].\n\t\tpublic numCharCountBits(ver: int): int {\n\t\t\treturn this.numBitsCharCount[Math.floor((ver + 7) / 17)];\n\t\t}\n\t\t\n\t}\n}\n\n","import { QrCode } from './qrcodegen';\n\nexport type QrErrorCorrection = 'low' | 'medium' | 'quartile' | 'high';\n\nconst ECC: Record<QrErrorCorrection, QrCode.Ecc> = {\n low: QrCode.Ecc.LOW,\n medium: QrCode.Ecc.MEDIUM,\n quartile: QrCode.Ecc.QUARTILE,\n high: QrCode.Ecc.HIGH,\n};\n\n/**\n * Encodes `text` as a QR code and returns its module grid — `true` for a\n * dark module, indexed [y][x]. Rendering (SVG, canvas, ...) is left to the\n * caller; 'medium' error correction is plenty for a plain URL without\n * inflating the module count on longer ones.\n */\nexport function getQrMatrix(text: string, ecc: QrErrorCorrection = 'medium'): boolean[][] {\n const qr = QrCode.encodeText(text, ECC[ecc]);\n const rows: boolean[][] = [];\n for (let y = 0; y < qr.size; y++) {\n const row: boolean[] = [];\n for (let x = 0; x < qr.size; x++) row.push(qr.getModule(x, y));\n rows.push(row);\n }\n return rows;\n}\n","import type { IdType } from './types';\n\nconst PARAM_TOKEN = 'aid_token';\nconst PARAM_TS = 'aid_ts';\nconst PARAM_USER = 'aid_user';\nconst PARAM_ID_TYPE = 'aid_doc';\nconst PARAM_CONSENT = 'aid_consent';\n\nconst VALID_ID_TYPES: readonly IdType[] = ['national_id', 'passport', 'drivers_licence'];\n\n// How long a handoff link is trusted to carry consent/userId/idType forward.\n// Long enough to scan a QR code and switch devices, short enough that a\n// screenshotted or forwarded link can't be replayed much later to skip\n// consent on someone else's phone.\nconst HANDOFF_MAX_AGE_MS = 30 * 60 * 1000;\n\n/** Query string keys used to encode a mobile handoff — export so callers can strip them back out. */\nexport const HANDOFF_PARAM_KEYS = [PARAM_TOKEN, PARAM_TS, PARAM_USER, PARAM_ID_TYPE, PARAM_CONSENT] as const;\n\n/**\n * A random id unique to one QR/link render. Makes every mobile handoff URL\n * distinct instead of pointing at the site's bare domain, and gives a host\n * app something to correlate \"session started on desktop, continued on\n * phone\" in its own analytics/logs if it wants to. Not a security token —\n * nothing is looked up or authorized by it, so it needs no backend to mint.\n */\nexport function generateHandoffToken(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for older browsers without crypto.randomUUID — fine since this\n // is only ever used for link uniqueness/traceability, never as a credential.\n return Array.from({ length: 20 }, () => Math.floor(Math.random() * 36).toString(36)).join('');\n}\n\nexport interface HandoffParams {\n /** Raw token, present whenever the URL carried a handoff at all — independent of HANDOFF_MAX_AGE_MS, so a stale link still gets cleaned from the address bar. */\n token: string | null;\n userId: string | null;\n idType: IdType | null;\n /**\n * The original consentGivenAt from the device that generated this link,\n * carried forward so the phone can skip re-asking for consent — null if\n * absent OR the link is older than HANDOFF_MAX_AGE_MS.\n */\n consentGivenAt: Date | null;\n}\n\n/**\n * Appends handoff params to `baseUrl` so the phone that opens it can skip\n * re-entering what was already chosen on the desktop — including consent,\n * if it was already given there. Always stamps a generation timestamp so\n * the receiving end can refuse to honor a link that's too old.\n */\nexport function buildHandoffUrl(\n baseUrl: string,\n params: { token: string; userId?: string; idType?: IdType; consentGivenAt?: Date },\n): string {\n const url = new URL(baseUrl);\n url.searchParams.set(PARAM_TOKEN, params.token);\n url.searchParams.set(PARAM_TS, String(Date.now()));\n if (params.userId) url.searchParams.set(PARAM_USER, params.userId);\n if (params.idType) url.searchParams.set(PARAM_ID_TYPE, params.idType);\n if (params.consentGivenAt) url.searchParams.set(PARAM_CONSENT, params.consentGivenAt.toISOString());\n return url.toString();\n}\n\n/** Reads back whatever buildHandoffUrl() encoded, from the current page's URL. */\nexport function readHandoffParams(): HandoffParams {\n const empty: HandoffParams = { token: null, userId: null, idType: null, consentGivenAt: null };\n if (typeof window === 'undefined') return empty;\n\n const search = new URLSearchParams(window.location.search);\n const token = search.get(PARAM_TOKEN);\n if (!token) return empty;\n\n const ts = Number(search.get(PARAM_TS));\n const age = Date.now() - ts;\n if (!Number.isFinite(ts) || age < 0 || age > HANDOFF_MAX_AGE_MS) {\n // Too old (or malformed) to trust — still return the token so the\n // address bar gets cleaned up, but nothing else carries forward.\n return { ...empty, token };\n }\n\n const idTypeRaw = search.get(PARAM_ID_TYPE);\n const consentRaw = search.get(PARAM_CONSENT);\n const consentDate = consentRaw ? new Date(consentRaw) : null;\n\n return {\n token,\n userId: search.get(PARAM_USER),\n idType: VALID_ID_TYPES.includes(idTypeRaw as IdType) ? (idTypeRaw as IdType) : null,\n consentGivenAt: consentDate && !Number.isNaN(consentDate.getTime()) ? consentDate : null,\n };\n}\n\n/** The current page URL with any of our own handoff params removed — the safe base to re-encode a fresh link from. */\nexport function currentUrlWithoutHandoffParams(): string {\n if (typeof window === 'undefined') return '';\n const url = new URL(window.location.href);\n HANDOFF_PARAM_KEYS.forEach(key => url.searchParams.delete(key));\n return url.toString();\n}\n\n/** Strips our own handoff params from the visible address bar, without a page reload. Call once, after reading them. */\nexport function clearHandoffParamsFromAddressBar(): void {\n if (typeof window === 'undefined') return;\n const url = new URL(window.location.href);\n HANDOFF_PARAM_KEYS.forEach(key => url.searchParams.delete(key));\n window.history.replaceState(null, '', `${url.pathname}${url.search}${url.hash}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YACE,SACgB,MACA,QAChB;AACA,UAAM,OAAO;AAHG;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,cAAc;AAAA,EAC3C,YAAY,SAAiB;AAC3B,UAAM,SAAS,cAAc,GAAG;AAChC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,WAAN,cAAuB,cAAc;AAAA,EAC1C,YAAY,SAAiB,QAAiB;AAC5C,UAAM,SAAS,aAAa,MAAM;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,cAAN,cAA0B,cAAc;AAAA,EAC7C,YAAY,SAAiB;AAC3B,UAAM,SAAS,cAAc;AAC7B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,cAA8B,cAAc;AAAA,EACjD,YAAY,SAAiB;AAC3B,UAAM,SAAS,kBAAkB;AACjC,SAAK,OAAO;AAAA,EACd;AACF;;;AC9BO,SAAS,SAAuB;AACrC,QAAM,IAAK,WAAmC,QAAQ;AACtD,MAAI,CAAC,GAAG;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MAEA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,eAAwB;AACtC,SAAO,CAAC,CAAE,WAAmC,QAAQ;AACvD;AAEO,SAAS,cAAc,OAA2B;AACvD,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,QAAO,OAAO,aAAa,MAAM,CAAC,CAAC;AAC1E,SAAO,KAAK,GAAG;AACjB;AAEO,SAAS,iBAAiB,OAA2B;AAC1D,SAAO,cAAc,KAAK,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AACvF;AAEO,SAAS,iBAAiB,GAAuB;AACtD,QAAM,MAAM,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,IAAI,IAAI,QAAQ,IAAK,EAAE,SAAS,KAAM,CAAC;AACzF,QAAM,MAAM,KAAK,GAAG;AACpB,QAAM,MAAM,IAAI,WAAW,IAAI,MAAM;AACrC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,KAAI,CAAC,IAAI,IAAI,WAAW,CAAC;AAC9D,SAAO;AACT;AAEO,SAAS,WAAW,OAA2B;AACpD,SAAO,MAAM,KAAK,OAAO,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACxE;AAEO,SAAS,WAAW,KAAgC;AACzD,MAAI,IAAI,SAAS,MAAM,KAAK,aAAa,KAAK,GAAG,EAAG,QAAO;AAC3D,QAAM,MAAM,IAAI,WAAW,IAAI,SAAS,CAAC;AACzC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,KAAI,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AACtF,SAAO;AACT;AAEO,IAAM,OAAO,CAAC,MAA0B,IAAI,YAAY,EAAE,OAAO,CAAC;AASlE,SAAS,IAAI,OAAgC;AAClD,SAAO,MAAM,MAAM,EAAE;AACvB;AAEA,eAAsB,OAAO,MAAuC;AAClE,SAAO,IAAI,WAAW,MAAM,OAAO,EAAE,OAAO,WAAW,IAAI,IAAI,CAAC,CAAC;AACnE;AAQO,SAAS,cAAc,OAAwB;AACpD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK,KAAK;AACjF,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,IAAI,MAAM,IAAI,aAAa,EAAE,KAAK,GAAG,CAAC;AACvE,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,KAAK,GAAG,EAAE,OAAO,OAAK,IAAI,CAAC,MAAM,MAAS,EAAE,KAAK;AACrE,SAAO,IAAI,KAAK,IAAI,OAAK,GAAG,KAAK,UAAU,CAAC,CAAC,IAAI,cAAc,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AACrF;AAGO,SAAS,WAAmB;AACjC,QAAM,IAAK,WAAmC;AAC9C,MAAI,GAAG,WAAY,QAAO,EAAE,WAAW;AACvC,QAAM,IAAI,IAAI,WAAW,EAAE;AAC3B,MAAI,GAAG,gBAAiB,GAAE,gBAAgB,CAAC;AAAA,MACtC,UAAS,IAAI,GAAG,IAAI,IAAI,IAAK,GAAE,CAAC,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AACvE,IAAE,CAAC,IAAK,EAAE,CAAC,IAAI,KAAQ;AACvB,IAAE,CAAC,IAAK,EAAE,CAAC,IAAI,KAAQ;AACvB,QAAM,IAAI,WAAW,CAAC;AACtB,SAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG;;;ACpDA,IAAM,uBAAuB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAC9D,IAAM,qBAAqB;AAC3B,IAAM,8BAA8B;AACpC,IAAM,oBAAoB;AAEnB,IAAM,eAAe,CAAC,OAAe,IAAI,QAAc,OAAK,WAAW,GAAG,EAAE,CAAC;AACpF,IAAI,QAAQ;AAMZ,SAAS,aAAa,KAAe,UAA0B;AAC7D,QAAM,MAAM,IAAI,SAAS,MAAM,aAAa;AAC5C,QAAM,OAAO,MAAM,OAAO,GAAG,IAAI;AACjC,SAAO,OAAO,SAAS,IAAI,KAAK,QAAQ,IAAI,KAAK,IAAI,OAAO,KAAM,kBAAkB,IAAI;AAC1F;AAQA,eAAe,aAAa,KAAiC;AAC3D,MAAI;AACF,UAAM,OAAQ,OAAO,IAAI,QAAQ,IAAI,MAAM,IAAI,KAAK,KAAK;AAIzD,YAAQ,MAAM,QAAQ,SAAS,MAAM,WAAW;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eACpB,WACA,KACA,OACA,UAAwB,CAAC,GACN;AACnB,QAAM,WAAW,KAAK,IAAI,GAAG,QAAQ,YAAY,CAAC;AAClD,QAAM,OAAO,QAAQ,eAAe;AACpC,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,MAAI,mBAAmB;AAEvB,MAAI,MAAM,SAAS;AACnB,MAAI,MAAM,MAAM,MAAM;AAEtB,WAAS,UAAU,KAAK,WAAW;AACjC,UAAM,OAAO,WAAW;AACxB,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,UAAU,KAAK;AAAA,QACzB,QAAQ;AAAA,QACR,SAAS,EAAE,GAAG,IAAI,SAAS,mBAAmB,IAAI;AAAA,QAClD,MAAM,IAAI;AAAA,MACZ,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,KAAM,OAAM;AAChB,YAAM,MAAM,OAAO,MAAM,UAAU,EAAE;AACrC;AAAA,IACF;AAEA,QAAI,IAAI,GAAI,QAAO;AACnB,QAAI,IAAI,WAAW,KAAK;AACtB,UAAI,CAAE,MAAM,aAAa,GAAG,EAAI,QAAO;AACvC,YAAM,QAAQ,aAAa,KAAK,IAAI;AACpC,UAAI,mBAAmB,QAAQ,iBAAkB,QAAO;AACxD,0BAAoB;AACpB,YAAM,MAAM,KAAK;AACjB;AACA;AAAA,IACF;AACA,QAAI,KAAM,QAAO;AACjB,QAAI,qBAAqB,IAAI,IAAI,MAAM,GAAG;AACxC,YAAM,MAAM,aAAa,KAAK,OAAO,MAAM,UAAU,EAAE,CAAC;AACxD,YAAM,SAAS;AACf,YAAM,MAAM,MAAM;AAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;ACpHA,IAAM,iBAAqB;AAC3B,IAAM,iBAAqB;AAC3B,IAAM,mBAAqB;AAC3B,IAAM,iBAAqB;AAI3B,IAAM,aAAuB,OAAO;AACpC,IAAM,uBAAuB;AAqD7B,eAAsB,YAAY,QAAiE;AACjG,QAAM,mBAAoB,WAAiE;AAC3F,MAAI,CAAC,iBAAkB,QAAO;AAC9B,MAAI;AACF,WAAO,MAAM,IAAI,iBAAiB,EAAE,kBAAkB,GAAG,UAAU,KAAK,CAAC,EAAE,OAAO,MAAM;AAAA,EAC1F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAsBA,IAAM,yBAAyB;AAExB,SAAS,qBAAqB,OAAqC;AACxE,MAAI,MAAM,UAAU,EAAG,QAAO,MAAM;AACpC,QAAM,QAAQ,MAAM,IAAI,OAAK,EAAE,YAAY,QAAQ,EAAE,YAAY,MAAM;AACvE,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK;AACjC,SAAO,MAAM,OAAO,OAAK,KAAK,UAAU,sBAAsB,EAAE;AAClE;AAOA,IAAM,aAAa,CAAC,KAAM,KAAM,GAAI;AACpC,IAAM,YAAa,CAAC,KAAM,IAAM,IAAM,EAAI;AAU1C,eAAsB,uBAAuB,OAA+B;AAC1E,QAAMA,UAAS,IAAI,WAAW,MAAM,MAAM,MAAM,GAAG,CAAC,EAAE,YAAY,CAAC;AACnE,QAAM,UAAU,CAAC,UAAoB,MAAM,MAAM,CAAC,MAAM,MAAMA,QAAO,CAAC,MAAM,IAAI;AAChF,SAAO,QAAQ,UAAU,KAAK,QAAQ,SAAS;AACjD;AASA,SAAS,cAAc,MAAoB,GAAW,GAAW,IAAY,IAAoB;AAC/F,MAAI,WAAW;AACf,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,MAAM,GAAG,QAAQ;AACrB,aAAS,IAAI,IAAI,IAAI,KAAK,IAAI,KAAK;AACjC,YAAM,IAAI,KAAK,IAAI,IAAI,CAAC;AACxB,aAAS;AACT,eAAS,IAAI;AAAA,IACf;AACA,UAAM,OAAW,MAAM;AACvB,UAAM,WAAW,QAAQ,KAAK,OAAO;AACrC,QAAI,WAAW,OAAO,OAAO,MAAM,OAAO,IAAK;AAAA,EACjD;AACA,SAAO;AACT;AAwCA,IAAM,aAAa;AACnB,IAAM,iBAAiB;AAEvB,IAAM,0BAA0B;AAEhC,IAAM,oBAAoB;AAE1B,IAAM,YAAY,CAAC,GAAG,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS,QAAQ,OAAO;AACxF,IAAM,SAAS;AAEf,IAAM,iBAAiB,KAAK,KAAK,SAAS,IAAI;AAC9C,IAAM,OAAO;AAEb,IAAM,oBAAoB;AAE1B,IAAM,uBAAuB;AAC7B,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAEtB,IAAM,sBAAsB;AAErB,IAAM,wBACX;AAKK,SAAS,sBAAsB,QAAwB;AAC5D,QAAM,MAAM,WAAW,oBAAoB,qBAAqB;AAChE,SACE,wDAAwD,GAAG;AAI/D;AAqDO,SAAS,iBACd,MACA,GACA,GACA,KAAK,GACL,KAAK,GAEL,mBACkB;AAKlB,QAAM,MAAM,WAAW,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,oBAAoB,UAAU,MAAM,GAAG,GAAG,IAAI,EAAE,IAAI;AAClG,QAAM,KAAK,MAAM,CAAC,GAAW,MAAc,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,GAAW,MAAc,KAAK,IAAI,IAAI,KAAK,CAAC;AAC1G,QAAM,YAAY,qBAAqB,cAAc,IAAI,IAAI,CAAC;AAC9D,QAAM,KAAe,CAAC;AACtB,QAAM,KAAe,CAAC;AACtB,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,aAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,UAAI,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,WAAW;AACjD,WAAG,KAAK,CAAC;AACT,WAAG,KAAK,CAAC;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACA,MAAI,GAAG,WAAW,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAI5D,MAAI,UAAU,GAAG,YAAY;AAC7B,aAAW,KAAK,WAAW;AACzB,UAAMC,SAAQ,KAAK,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE;AACxC,UAAM,SAAS,IAAI,WAAW,IAAIA,SAAQ,CAAC;AAC3C,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,aAAO,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,IAAIA,SAAQ,EAAE;AAAA,IAC5D;AACA,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,UAAS,OAAO,CAAC,IAAI,OAAO,CAAC;AACrE,QAAI,QAAQ,WAAW;AAAE,kBAAY;AAAO,gBAAU;AAAA,IAAG;AAAA,EAC3D;AAGA,QAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,OAAO,IAAI,EAAE;AAC9C,QAAM,OAAO,IAAI,QAAQ;AACzB,QAAM,KAAK,IAAI,WAAW,GAAG,MAAM;AACnC,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,MAAM,CAAC;AAClD,QAAM,MAAM,KAAK,KAAK,SAAS,IAAI;AACnC,QAAM,QAAQ,IAAI,YAAY,OAAO,SAAS,GAAG;AACjD,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,UAAM,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG,CAAC,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ;AACvE,OAAG,CAAC,IAAI;AACR,UAAM,QAAQ,KAAK,IAAI,SAAS,GAAG,KAAK,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC;AAC7D,UAAM,OAAO,KAAK,IAAI,MAAM,GAAG,KAAK,OAAO,GAAG,CAAC,IAAI,QAAQ,UAAU,IAAI,CAAC;AAC1E,WAAO,IAAI,SAAS,SAAS,MAAM,IAAI;AAAA,EACzC;AAIA,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC;AAC9C,QAAM,OAAO,KAAK,IAAI,OAAO,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC;AACpD,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC;AACpD,QAAM,QAAoB,CAAC;AAE3B,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAM,WAAW,IAAI,WAAW,IAAI;AACpC,aAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,UAAI,IAAI;AACR,YAAM,QAAQ,IAAI,SAAS,KAAK;AAChC,eAAS,IAAI,GAAG,IAAI,KAAK,IAAK,MAAK,MAAM,OAAO,CAAC;AACjD,eAAS,CAAC,IAAI;AAAA,IAChB;AAEA,UAAM,OAAgC,CAAC;AACvC,QAAI,QAAQ,IAAI,MAAM;AACtB,aAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAC9B,YAAM,QAAQ,IAAI,QAAQ,SAAS,CAAC,KAAK;AACzC,UAAI,OAAO;AACT,YAAI,QAAQ,EAAG,SAAQ;AACvB,cAAM;AAAA,MACR,WAAW,SAAS,MAAM,EAAE,MAAM,KAAK,MAAM,OAAO;AAClD,aAAK,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC;AAC1B,gBAAQ;AACR,cAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,QAAQ,CAAC,GAAW,MAAuC;AAC/D,UAAI,IAAI,IAAI,IAAI,IAAI,OAAO,EAAG,QAAO,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5C,UAAI,OAAO;AACX,eAAS,IAAI,GAAG,KAAK,GAAG,IAAK,QAAO,KAAK,IAAI,MAAM,SAAS,CAAC,CAAC;AAC9D,UAAI,SAAS,IAAI,YAAY;AAC7B,eAAS,IAAI,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK;AACzC,YAAI,SAAS,CAAC,IAAI,WAAW;AAAE,sBAAY,SAAS,CAAC;AAAG,mBAAS;AAAA,QAAG;AAAA,MACtE;AACA,UAAI,SAAS,KAAK,aAAa,OAAO,IAAK,QAAO,CAAC,CAAC,GAAG,CAAC,CAAC;AACzD,aAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,GAAG,MAAM,SAAS,GAAG,CAAC,CAAC;AAAA,IAC1D;AAEA,eAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAACC,IAAGC,EAAC,MAAM,MAAMD,IAAGC,EAAC,CAAC,GAAG;AAC1D,YAAM,SAAS,IAAI,IAAI;AACvB,UAAI,SAAS,QAAQ,SAAS,KAAM;AACpC,UAAI,QAAQ,IAAI,OAAO,IAAI,SAAS,GAAG,SAAS;AAChD,eAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,YAAI,IAAI;AACR,iBAAS,IAAI,GAAG,KAAK,GAAG,IAAK,MAAK,OAAO,IAAI,SAAS,KAAK,MAAM,CAAC;AAClE,YAAI,KAAK,GAAG;AACV,cAAI,QAAQ,EAAG,SAAQ;AACvB,iBAAO;AACP;AAAA,QACF;AACA,kBAAU;AAAA,MACZ;AACA,UAAI,QAAQ,EAAG;AACf,YAAM,OAAO,OAAO,QAAQ;AAE5B,UAAI,UAAU,GAAG,cAAc;AAC/B,iBAAW,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG;AAC5C,YAAI,IAAI,KAAK,KAAK,KAAM;AACxB;AACA,iBAAS,IAAI,OAAO,KAAK,MAAM,IAAK,YAAW,OAAO,IAAI,SAAS,KAAK,MAAM,CAAC;AAAA,MACjF;AACA,YAAM,eAAe,SAAS;AAC9B,YAAM,KAAK;AAAA,QACT,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,IAAI,KAAK;AAAA,QAClB;AAAA,QACA,OAAO;AAAA,QACP,WAAW;AAAA,QACX,UAAU;AAAA,QACV,UAAU,OAAO;AAAA,QACjB,UAAU,SAAS;AAAA,QACnB,SAAS,UAAU,OAAO,OAAO;AAAA,QACjC,OAAO,cAAc,KAAK,eAAe,IAAI,UAAU,cAAc,eAAe;AAAA,MACtF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,iBAAiB,aAAa,MAAM,OAAO,OAAK,EAAE,YAAY,QAAQ,EAAE,YAAY,IAAI,CAAC,EAC5F,OAAO,OAAK,IAAI,IAAI,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,QAAQ,cAAc,EAC/D,IAAI,OAAK;AACR,UAAM,MAAM,KAAK,IAAI,GAAG,EAAE,IAAI,OAAK,EAAE,GAAG,CAAC;AACzC,UAAM,SAAS,KAAK,IAAI,GAAG,EAAE,IAAI,OAAK,EAAE,MAAM,CAAC;AAC/C,UAAM,SAAS,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE;AAG3D,UAAM,UAAU,IAAI,aAAa,EAAE;AACnC,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,IAAK,KAAI,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC,KAAK,OAAQ,SAAQ,GAAG,CAAC,CAAC;AACtF,UAAM,QAAQ,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE;AACzD,WAAO,EAAE,KAAK,QAAQ,SAAS,MAAM,UAAU,GAAG,QAAQ,aAAa,YAAY,SAAS,MAAM,GAAG,MAAM;AAAA,EAC7G,CAAC;AAEH,SAAO,EAAE,OAAO,eAAe;AACjC;AAGA,SAAS,WAAW,MAAoB,GAAW,GAAW,IAAY,IAAoB;AAC5F,QAAM,OAAO,IAAI,YAAY,GAAG;AAChC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,IAAI,IAAI;AACpB,aAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,WAAK,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3E;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,WAAO,KAAK,CAAC;AACb,QAAI,OAAO,IAAI,EAAG,QAAO;AAAA,EAC3B;AACA,SAAO;AACT;AAMA,SAAS,cAAc,IAAsC,IAAY,GAAmB;AAC1F,QAAM,OAAO,IAAI,YAAY,GAAG;AAChC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,aAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,WAAK,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,GAAG,MAAM;AACnB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,WAAO,KAAK,CAAC;AACb,QAAI,OAAO,IAAI,MAAM;AAAE,YAAM;AAAG;AAAA,IAAO;AAAA,EACzC;AACA,SAAO,KAAK,IAAI,gBAAgB,KAAK,IAAI,YAAY,KAAK,MAAM,MAAM,uBAAuB,CAAC,CAAC;AACjG;AAGA,SAAS,UAAU,MAAoB,GAAW,GAAW,IAAY,IAA0B;AACjG,QAAM,MAAM,IAAI,aAAa,KAAK,CAAC;AACnC,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,UAAI,MAAM,GAAG,IAAI;AACjB,eAAS,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC,GAAG,MAAM,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,MAAM;AACpE,iBAAS,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC,GAAG,MAAM,KAAK,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,MAAM;AACrE,iBAAO,KAAK,KAAK,IAAI,KAAK,EAAE;AAC5B;AAAA,QACF;AAAA,MACF;AACA,UAAI,IAAI,KAAK,CAAC,IAAI,MAAM;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,OAAiC;AACrD,QAAM,SAAS,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AAC5D,QAAM,SAAuB,CAAC;AAC9B,aAAW,QAAQ,QAAQ;AACzB,UAAM,IAAI,OAAO,OAAO,SAAS,CAAC;AAClC,UAAM,MAAM,IAAI,EAAE,SAAS,CAAC;AAC5B,QAAI,OAAO,KAAK,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,MAAM,KAAK,IAAI,KAAK,QAAQ,IAAI,MAAM,EAAG,GAAE,KAAK,IAAI;AAAA,QAChG,QAAO,KAAK,CAAC,IAAI,CAAC;AAAA,EACzB;AACA,SAAO;AACT;AAGA,SAAS,YAAY,SAAuB,aAA6B;AACvE,QAAM,IAAI,QAAQ;AAClB,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,GAAG,IAAK,SAAQ,QAAQ,CAAC;AAC7C,UAAQ;AACR,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,GAAG,IAAK,WAAU,QAAQ,CAAC,IAAI,SAAS;AAC5D,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,OAAO;AACX,QAAM,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,cAAc,GAAG,CAAC;AACpD,QAAM,KAAK,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,cAAc,GAAG,CAAC;AACxD,WAAS,MAAM,IAAI,OAAO,IAAI,OAAO;AACnC,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,MAAM,GAAG,IAAK,SAAQ,QAAQ,CAAC,IAAI,SAAS,QAAQ,IAAI,GAAG,IAAI;AACnF,WAAO,KAAK,IAAI,MAAM,MAAM,KAAK;AAAA,EACnC;AACA,SAAO;AACT;AAOO,SAAS,kBACd,MACA,GACA,GACA,KAAK,GACL,KAAK,GACL,mBACY;AACZ,QAAM,EAAE,OAAO,eAAe,IAAI,iBAAiB,MAAM,GAAG,GAAG,IAAI,IAAI,iBAAiB;AAKxF,QAAM,aAAa,KAAK,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,MAAM,CAAC,IAAI,IAAI;AACxE,QAAM,QAAQ,MAAM,OAAO,OACzB,EAAE,YAAY,OAAO,EAAE,YAAY,QAAQ,EAAE,SAAS,kBAClD,EAAE,WAAW,aAAa,OAAQ,EAAE,UAAU,CAAC;AACrD,QAAM,YAAY,aAAa,KAAK,EAAE;AAItC,MAAI,eAAe;AACnB,aAAW,KAAK,OAAO;AACrB,UAAM,YAAY,MAAM,OAAO,OAAK,EAAE,UAAU,EAAE,KAAK;AACvD,UAAM,OAAO,UAAU,OAAO,OAAK,KAAK,IAAI,EAAE,YAAY,EAAE,SAAS,KAAK,CAAC,EAAE;AAC7E,UAAM,QAAQ,UAAU,OAAO,OAAK,KAAK,IAAI,EAAE,WAAW,EAAE,QAAQ,KAAK,CAAC,EAAE;AAC5E,mBAAe,KAAK,IAAI,cAAc,MAAM,KAAK;AAAA,EACnD;AAQA,QAAM,YAAY,CAAC,MAAwB,EAAE,SAAS,iBAAiB,EAAE,eAAe;AACxF,QAAM,UAAU,CAAC,GAAqB,MAAwB;AAC5D,UAAM,QAAQ,EAAE,SAAS,EAAE,UAAU;AACrC,UAAM,QAAQ,EAAE,SAAS,EAAE;AAC3B,WAAO,SAAS,MAAM,QAAQ,SAAS,MAAM,QACxC,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK;AAAA,EACtE;AACA,MAAIC,UAAS;AACb,MAAI,WAAW;AACf,WAAS,IAAI,GAAG,KAAK,eAAe,QAAQ,KAAK;AAC/C,QAAI,IAAI,eAAe,UAAU,QAAQ,eAAe,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC,EAAG;AACpF,UAAM,MAAM,eAAe,MAAM,UAAU,CAAC;AAC5C,QAAI,IAAI,UAAU,KAAK,IAAI,UAAU,GAAG;AACtC,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAI,UAAU,IAAI,IAAI,CAAC,CAAC,KAAK,UAAU,IAAI,CAAC,CAAC,EAAG,CAAAA,UAAS;AAAA,MAC3D;AAAA,IACF;AACA,eAAW;AAAA,EACb;AAEA,SAAO,EAAE,WAAW,cAAc,QAAAA,QAAO;AAC3C;AASA,IAAM,uBAAuB;AAStB,SAAS,OAAO,MAAoB,GAAW,GAAW,KAAK,GAAG,KAAK,GAAY;AACxF,SACE,kBAAkB,MAAM,GAAG,GAAG,IAAI,EAAE,EAAE,UACtC,kBAAkB,MAAM,GAAG,GAAG,IAAI,IAAI,oBAAoB,EAAE;AAEhE;AAeA,eAAsB,WAAW,QAAiE;AAChG,QAAM,mBAAoB,WAAiE;AAC3F,MAAI,CAAC,iBAAkB,QAAO;AAC9B,MAAI;AACF,WAAO,MAAM,IAAI,iBAAiB,EAAE,OAAO,MAAM;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAAiB,MAAuB;AACtD,QAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE,EAAE,YAAY;AACrD,SAAO,QAAQ,UAAU,MAAM,eAAe,KAAK,OAAO,KAAK,QAAQ,SAAS,GAAG;AACrF;AAEA,SAAS,kBAAkB,MAAoB,GAAW,GAAW,IAAY,IAAoB;AACnG,MAAI,SAAS,GAAG,WAAW;AAC3B,WAAS,IAAI,GAAG,IAAI,IAAI,GAAG,KAAK;AAC9B,aAAS,IAAI,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,KAAK;AACzC,YAAM,MACJ,IAAI,KAAK,IAAI,IAAI,CAAC,IAChB,MAAM,IAAI,KAAK,IAAI,CAAC,IACpB,MAAM,IAAI,KAAK,IAAI,CAAC,IACpB,KAAK,IAAI,KAAK,IAAI,EAAE,IACpB,KAAK,IAAI,KAAK,IAAI,EAAE;AACxB,gBAAU,MAAM;AAChB;AAAA,IACF;AAAA,EACF;AACA,SAAO,WAAW,IAAI,SAAS,WAAW;AAC5C;AAeO,SAAS,sBACd,MACA,GACA,GACA,kBAAkB,OACR;AACV,QAAM,QAAQ,IAAI;AAClB,QAAM,UACJ,QAAQ,IACJ,CAAC,EAAE,IAAI,GAAG,IAAI,KAAK,MAAM,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,EAAE,CAAC,IACvF,CAAC,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC;AAEvB,MAAI,WAAW,QAAQ,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,OAAO,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC;AACtE,MAAI,CAAC,YAAY,iBAAiB;AAChC,eAAW,QAAQ,OAAO,CAAC,MAAM,MAC/B,cAAc,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,cAAc,MAAM,GAAG,GAAG,KAAK,IAAI,KAAK,EAAE,IAAI,IAAI,IAAI;AAAA,EAClG;AACA,MAAI,CAAC,SAAU,QAAO,CAAC,qBAAqB;AAE5C,MAAI,kBAAkB,MAAM,GAAG,GAAG,SAAS,IAAI,SAAS,EAAE,IAAI,gBAAgB;AAC5E,WAAO;AAAA,MACL;AAAA,IAEF;AAAA,EACF;AACA,SAAO,CAAC;AACV;AAWO,SAAS,kBACd,MACA,GACA,GACA,QACA,oBAAmC,MACzB;AACV,OAAK,qBAAqB,MAAM,EAAG,QAAO,CAAC;AAC3C,QAAM,SAAS,kBAAkB,MAAM,GAAG,CAAC;AAC3C,MAAI,OAAO,OAAQ,QAAO,CAAC;AAC3B,MAAI,OAAO,aAAa,qBAAqB,OAAO,gBAAgB,qBAAsB,QAAO,CAAC;AAClG,SAAO,CAAC,sBAAsB,MAAM,CAAC;AACvC;AAyBO,SAAS,sBACd,MACA,GACA,GACA,QAGA,0BACU;AACV,QAAM,QAAQ,IAAI;AAElB,QAAM,gBAAgB,4BAA4B,cAAc,MAAM,GAAG,GAAG,GAAG,CAAC;AAChF,MAAI,gBAAgB,KAAK,IAAI,GAAG,IAAI,IAAI,GAAG;AACzC,WAAO;AAAA,MACL;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,WAAW,YAAY;AACzB,QAAI,QAAQ,KAAO,KAAK,IAAI,QAAQ,UAAU,IAAI,sBAAsB;AACtE,aAAO;AAAA,QACL;AAAA,MAEF;AAAA,IACF;AAAA,EACF,WAAW,QAAQ,GAAK;AACtB,WAAO;AAAA,MACL;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,CAAC;AACV;AAWO,IAAM,6BACX;AAUK,SAAS,gBACd,MACA,GACA,GACA,kBAAkB,OACM;AACxB,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,UAAS,KAAK,CAAC;AACrD,QAAM,aAAa,QAAQ,KAAK;AAEhC,QAAM,SAAmB,CAAC;AAC1B,MAAI,aAAa,eAAgB,QAAO,KAAK,6CAA6C;AAC1F,MAAI,aAAa;AACf,WAAO,KAAK,0EAA0E;AAExF,QAAM,WAAW,mBAAmB,OAAO,MAAM,GAAG,CAAC;AACrD,MAAI,YAAY,kBAAkB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,gBAAgB;AACpE,WAAO,KAAK,2FAA2F;AAAA,EACzG;AAEA,MAAI,CAAC,YAAY,OAAO,WAAW,EAAG,QAAO,KAAK,0BAA0B;AAC5E,SAAO,EAAE,IAAI,OAAO,WAAW,GAAG,QAAQ,QAAQ,SAAS;AAC7D;AAUA,eAAsB,sBAAsB,OAA8C;AACxF,MAAI,MAAM,OAAO,gBAAgB;AAC/B,WAAO,EAAE,IAAI,OAAO,QAAQ,OAAO,QAAQ,CAAC,6DAA6D,EAAE;AAAA,EAC7G;AACA,MAAI,CAAE,MAAM,uBAAuB,KAAK,GAAI;AAC1C,WAAO,EAAE,IAAI,OAAO,QAAQ,OAAO,QAAQ,CAAC,2DAA2D,EAAE;AAAA,EAC3G;AAEA,SAAO,IAAI,QAAQ,aAAW;AAC5B,UAAM,MAAM,IAAI,MAAM;AACtB,UAAM,MAAM,IAAI,gBAAgB,KAAK;AACrC,QAAI,UAAU,MAAM;AAClB,UAAI,gBAAgB,GAAG;AACvB,cAAQ,EAAE,IAAI,OAAO,QAAQ,OAAO,QAAQ,CAAC,yDAAyD,EAAE,CAAC;AAAA,IAC3G;AACA,QAAI,SAAS,YAAY;AACvB,UAAI,gBAAgB,GAAG;AAEvB,YAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,MAAM,CAAC;AAC/D,YAAM,IAAI,KAAK,MAAM,IAAI,QAAQ,KAAK;AACtC,YAAM,IAAI,KAAK,MAAM,IAAI,SAAS,KAAK;AACvC,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAQ;AACf,aAAO,SAAS;AAChB,YAAM,MAAM,OAAO,WAAW,IAAI;AAClC,UAAI,wBAAwB;AAC5B,UAAI,UAAU,KAAK,GAAG,GAAG,GAAG,CAAC;AAC7B,YAAM,cAAc,WAAW,MAAM;AAErC,YAAM,EAAE,KAAK,IAAI,IAAI,aAAa,GAAG,GAAG,GAAG,CAAC;AAC5C,YAAM,OAAO,IAAI,aAAa,IAAI,CAAC;AACnC,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,aAAK,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC;AAAA,MAClF;AACA,YAAM,QAAQ,MAAM;AACpB,YAAM,kBAAkB,OAAO,KAAK,OAAK,iBAAiB,EAAE,YAAY,EAAE,CAAC,KAAK;AAChF,cAAQ,gBAAgB,MAAM,GAAG,GAAG,eAAe,CAAC;AAAA,IACtD;AACA,QAAI,MAAM;AAAA,EACZ,CAAC;AACH;AAoBA,eAAsB,kBACpB,OACA,SAAS,eACoB;AAC7B,QAAM,SAAmB,CAAC;AAE1B,MAAI,MAAM,OAAO,gBAAgB;AAC/B,WAAO,KAAK,6DAA6D;AACzE,WAAO,EAAE,IAAI,OAAO,OAAO;AAAA,EAC7B;AAEA,MAAI,CAAE,MAAM,uBAAuB,KAAK,GAAI;AAC1C,WAAO,KAAK,2DAA2D;AACvE,WAAO,EAAE,IAAI,OAAO,OAAO;AAAA,EAC7B;AAEA,SAAO,IAAI,QAAQ,aAAW;AAC5B,UAAM,MAAM,IAAI,MAAM;AACtB,UAAM,MAAM,IAAI,gBAAgB,KAAK;AAErC,QAAI,UAAU,MAAM;AAClB,UAAI,gBAAgB,GAAG;AACvB,cAAQ,EAAE,IAAI,OAAO,QAAQ,CAAC,yDAAyD,EAAE,CAAC;AAAA,IAC5F;AAEA,QAAI,SAAS,YAAY;AACvB,UAAI,gBAAgB,GAAG;AAGvB,YAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,MAAM,CAAC;AAC/D,YAAM,IAAI,KAAK,MAAM,IAAI,QAAS,KAAK;AACvC,YAAM,IAAI,KAAK,MAAM,IAAI,SAAS,KAAK;AAEvC,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAS;AAChB,aAAO,SAAS;AAChB,YAAM,MAAM,OAAO,WAAW,IAAI;AAOlC,UAAI,wBAAwB;AAC5B,UAAI,UAAU,KAAK,GAAG,GAAG,GAAG,CAAC;AAK7B,YAAM,eAAe,YAAY,MAAM;AACvC,YAAM,cAAe,WAAW,MAAM;AAEtC,YAAM,EAAE,KAAK,IAAI,IAAI,aAAa,GAAG,GAAG,GAAG,CAAC;AAC5C,YAAM,MAAO,IAAI;AAGjB,YAAM,OAAO,IAAI,aAAa,GAAG;AACjC,UAAI,WAAW;AACf,eAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,cAAM,MAAM,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC;AAClF,aAAK,CAAC,IAAM;AACZ,oBAAY;AAAA,MACd;AACA,YAAM,aAAa,WAAW;AAM9B,UAAI,WAAW,cAAc,kBAAkB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI;AACjE,eAAO,KAAK,2FAA2F;AACzG,UAAI,aAAa;AACf,eAAO,KAAK,6CAA6C;AAC3D,UAAI,aAAa;AACf,eAAO,KAAK,8EAA8E;AAM5F,YAAM,gBAAgB,cAAc,MAAM,GAAG,GAAG,GAAG,CAAC;AACpD,YAAM,iBAAiB,sBAAsB,MAAM,GAAG,GAAG,QAAQ,aAAa;AAC9E,aAAO,KAAK,GAAG,cAAc;AAM7B,UAAI,eAAe,WAAW,GAAG;AAC/B,cAAM,QAAQ,MAAM;AACpB,YAAI,WAAW,YAAY;AACzB,gBAAM,kBAAkB,OAAO,KAAK,OAAK,iBAAiB,EAAE,YAAY,EAAE,CAAC,KAAK;AAChF,gBAAM,iBAAiB,sBAAsB,MAAM,GAAG,GAAG,eAAe;AACxE,cAAI,EAAE,OAAO,SAAS,KAAK,eAAe,CAAC,MAAM,uBAAwB,QAAO,KAAK,GAAG,cAAc;AAAA,QACxG,OAAO;AACL,cAAI,OAAO,WAAW,EAAG,QAAO,KAAK,GAAG,kBAAkB,MAAM,GAAG,GAAG,QAAQ,OAAO,UAAU,IAAI,CAAC;AAAA,QACtG;AAAA,MACF;AASA,YAAM,QAAQ,MAAM;AACpB,YAAM,YAAY,UAAU,OAAO,OAAO,qBAAqB,KAAK;AACpE,UAAI,cAAc,GAAG;AACnB,eAAO,KAAK,6GAA6G;AAAA,MAC3H,WAAW,cAAc,QAAQ,YAAY,GAAG;AAC9C,eAAO,KAAK,oGAAoG;AAAA,MAClH;AAEA,cAAQ,EAAE,IAAI,OAAO,WAAW,GAAG,OAAO,CAAC;AAAA,IAC7C;AAEA,QAAI,MAAM;AAAA,EACZ,CAAC;AACH;;;AC79BA,IAAM,UAAU,CAAC,MAA6B,GAAG,UAAU,CAAC,OAAO,SAAS,MAAM,OAAO,IAAI;AAe7F,eAAe,oBAAoB,KAAgC;AACjE,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAM,SAAS,OAAO;AACtB,QAAI,OAAO,WAAW,SAAU,QAAO;AACvC,QAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,YAAM,UAAW,OAAiC;AAClD,UAAI,OAAO,YAAY,SAAU,QAAO;AAAA,IAC1C;AAEA,QAAI,WAAW,UAAa,OAAO,OAAO,YAAY,SAAU,QAAO,OAAO;AAAA,EAChF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,eAAe,eAAkB,KAA2B;AAC1D,MAAI,IAAI,GAAI,QAAO,IAAI,KAAK;AAC5B,QAAM,UAAU,MAAM,oBAAoB,GAAG;AAC7C,MAAI,IAAI,WAAW,OAAO,IAAI,WAAW,IAAK,OAAM,IAAI,UAAU,OAAO;AACzE,QAAM,IAAI,SAAS,SAAS,IAAI,MAAM;AACxC;AAOA,eAAsB,iBACpB,QACA,QACA,aACA,QACA,WAC6B;AAC7B,QAAM,MAAM,MAAM;AAAA,IAChB,QAAQ,SAAS;AAAA,IACjB,GAAG,MAAM;AAAA,IACT,OAAO;AAAA,MACL,SAAS;AAAA,QACP,aAAa;AAAA,QACb,mBAAmB;AAAA,QACnB,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,SAAS,OAAO,CAAC;AAAA,IAC1C;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,eAAmC,GAAG;AAC/C;AASO,IAAM,wBAAwB;AAErC,SAAS,WAAW,UAAkB,eAAqE;AACzG,QAAM,UAAkC,EAAE,iBAAiB,UAAU,QAAQ,GAAG;AAChF,MAAI,eAAe,OAAQ,SAAQ,qBAAqB,IAAI,cAAc,KAAK,GAAG;AAClF,SAAO;AACT;AAEA,eAAsB,SACpB,QACA,UACA,SACA,WACAC,cACA,eAC2B;AAM3B,MAAI,CAAE,MAAM,uBAAuB,QAAQ,OAAO,GAAI;AACpD,UAAM,IAAI,gBAAgB,kEAAkE;AAAA,EAC9F;AACA,MAAI,QAAQ,aAAa;AACvB,QAAI,QAAQ,WAAW,YAAY;AACjC,YAAM,IAAI,gBAAgB,sEAAsE;AAAA,IAClG;AACA,QAAI,CAAE,MAAM,uBAAuB,QAAQ,WAAW,GAAI;AACxD,YAAM,IAAI,gBAAgB,2FAA2F;AAAA,IACvH;AAAA,EACF;AAEA,QAAM,QAAQ,YAAY;AACxB,UAAM,OAAO,IAAI,SAAS;AAC1B,SAAK,OAAO,WAAW,QAAQ,MAAM;AACrC,SAAK,OAAO,WAAW,QAAQ,MAAM;AACrC,SAAK,OAAO,YAAY,QAAQ,OAAO;AACvC,QAAI,QAAQ,YAAa,MAAK,OAAO,iBAAiB,QAAQ,WAAW;AACzE,SAAK,OAAO,oBAAoB,QAAQ,eAAe,YAAY,CAAC;AACpE,SAAK,OAAO,sBAAsB,QAAQ,qBAAqB,uBAAuB;AACtF,UAAM,YAAYA,eAAc,MAAMA,aAAY,IAAI;AACtD,QAAI,WAAW;AAEb,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,EAAG,MAAK,OAAO,GAAG,CAAC;AAAA,IAClE;AACA,WAAO,EAAE,SAAS,WAAW,UAAU,aAAa,GAAG,MAAM,KAAK;AAAA,EACpE;AAEA,QAAM,MAAM,MAAM,eAAe,QAAQ,SAAS,GAAG,GAAG,MAAM,iBAAiB,OAAO,WAAW,KAAK;AACtG,SAAO,eAAiC,GAAG;AAC7C;AAOA,eAAsB,mBACpB,QACA,UACA,WAC0B;AAC1B,QAAM,MAAM,MAAM,QAAQ,SAAS,EAAE,GAAG,MAAM,4BAA4B;AAAA,IACxE,SAAS,EAAE,iBAAiB,UAAU,QAAQ,GAAG;AAAA,EACnD,CAAC;AACD,MAAI,IAAI,WAAW,IAAK,QAAO,EAAE,eAAe,MAAM;AACtD,SAAO,eAAgC,GAAG;AAC5C;AAEA,eAAsB,uBACpB,QACA,UACA,WAC8B;AAC9B,QAAM,MAAM,MAAM,QAAQ,SAAS,EAAE,GAAG,MAAM,gCAAgC;AAAA,IAC5E,SAAS,EAAE,iBAAiB,UAAU,QAAQ,GAAG;AAAA,EACnD,CAAC;AACD,SAAO,eAAoC,GAAG;AAChD;AAEA,IAAM,MAAM;AAGZ,eAAsB,qBACpB,QACA,UACA,gBACA,WAC4B;AAC5B,QAAM,MAAM,MAAM,QAAQ,SAAS,EAAE,GAAG,MAAM,WAAW,IAAI,cAAc,CAAC,uBAAuB;AAAA,IACjG,SAAS,EAAE,iBAAiB,UAAU,QAAQ,GAAG;AAAA,EACnD,CAAC;AACD,SAAO,eAAkC,GAAG;AAC9C;AAcA,eAAsB,oBACpB,QACA,UACA,gBACA,OACA,SACA,WACAA,cAC8B;AAC9B,QAAM,QAAQ,MAAM,QAAQ,cAAc,MAAM,GAAG,EAAE,CAAC;AACtD,QAAM,MAAM,SAAS,cAAc,QAAQ,SAAS,oBAAoB,QAAQ;AAChF,QAAM,QAAQ,YAAY;AACxB,UAAM,OAAO,IAAI,SAAS;AAG1B,SAAK,OAAO,SAAS,IAAI,KAAK,CAAC,KAAK,GAAG,YAAY,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;AACnE,SAAK,OAAO,SAAS,QAAQ,KAAK;AAClC,SAAK,OAAO,YAAY,QAAQ,WAAW,SAAS,OAAO;AAC3D,UAAM,YAAYA,eAAc,MAAMA,aAAY,IAAI;AACtD,QAAI,WAAW;AACb,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,EAAG,MAAK,OAAO,GAAG,CAAC;AAAA,IAClE;AACA,WAAO,EAAE,SAAS,EAAE,iBAAiB,UAAU,QAAQ,GAAG,GAAG,MAAM,KAAK;AAAA,EAC1E;AACA,QAAM,MAAM,MAAM;AAAA,IAChB,QAAQ,SAAS;AAAA,IACjB,GAAG,MAAM,WAAW,IAAI,cAAc,CAAC;AAAA,IACvC;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,eAAoC,GAAG;AAChD;AAGA,eAAsB,kBACpB,QACA,UACA,gBACA,WAC8B;AAC9B,QAAM,MAAM,MAAM,QAAQ,SAAS,EAAE,GAAG,MAAM,WAAW,IAAI,cAAc,CAAC,oBAAoB;AAAA,IAC9F,SAAS,EAAE,iBAAiB,UAAU,QAAQ,GAAG;AAAA,EACnD,CAAC;AACD,SAAO,eAAoC,GAAG;AAChD;AAOA,eAAsB,cACpB,QACA,UACA,gBACA,WACA,eAC2B;AAC3B,QAAM,MAAM,MAAM;AAAA,IAChB,QAAQ,SAAS;AAAA,IACjB,GAAG,MAAM,WAAW,IAAI,cAAc,CAAC;AAAA,IACvC,OAAO,EAAE,SAAS,WAAW,UAAU,aAAa,GAAG,MAAM,IAAI,SAAS,EAAE;AAAA,IAC5E,WAAW;AAAA,EACb;AACA,SAAO,eAAiC,GAAG;AAC7C;AAEA,eAAsB,YACpB,QACA,UACA,gBACA,UACA,WACoB;AACpB,QAAM,MAAM,MAAM;AAAA,IAChB,QAAQ,SAAS;AAAA,IACjB,GAAG,MAAM;AAAA,IACT,MAAM;AACJ,YAAM,OAAO,IAAI,SAAS;AAC1B,WAAK,OAAO,mBAAmB,cAAc;AAC7C,UAAI,SAAU,MAAK,OAAO,aAAa,QAAQ;AAC/C,aAAO,EAAE,SAAS,EAAE,iBAAiB,UAAU,QAAQ,GAAG,GAAG,MAAM,KAAK;AAAA,IAC1E;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,eAA0B,GAAG;AACtC;;;AC5PA,IAAM,UAAU;AAChB,IAAM,QAAQ;AACd,IAAM,SAAS;AAEf,IAAI,YAA2C;AAE/C,SAAS,MAA8B;AACrC,SAAQ,WAA0C;AACpD;AAEA,SAAS,OAAO,SAA2C;AACzD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,QAAQ,KAAK,SAAS,CAAC;AACnC,QAAI,kBAAkB,MAAM,IAAI,OAAO,kBAAkB,KAAK;AAC9D,QAAI,YAAY,MAAM,QAAQ,IAAI,MAAM;AACxC,QAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,EACtC,CAAC;AACH;AAEA,SAAS,WAAc,IAAiB,MAA0B,IAAmD;AACnH,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,GAAG,GAAG,YAAY,OAAO,IAAI,EAAE,YAAY,KAAK,CAAC;AAC7D,QAAI,YAAY,MAAM,QAAQ,IAAI,MAAW;AAC7C,QAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,EACtC,CAAC;AACH;AAEA,SAAS,WAAmC;AAC1C,SAAO,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,YAAY,QAAQ,GAAG,OAAO,CAAC,QAAQ,QAAQ,CAAC;AAC/F;AAOO,SAAS,mBAA2C;AACzD,QAAM,UAAU,IAAI;AACpB,MAAI,CAAC,QAAS,QAAQ,0BAAc,SAAS;AAC7C,UAAQ,YAAY;AAClB,QAAI;AACF,YAAM,KAAK,MAAM,OAAO,OAAO;AAC/B,UAAI;AACF,cAAM,WAAW,MAAM,WAAsC,IAAI,YAAY,OAAK,EAAE,IAAI,MAAM,CAAC;AAC/F,YAAI,UAAU,cAAc,SAAS,UAAW,QAAO;AACvD,cAAM,OAAO,MAAM,SAAS;AAC5B,cAAM,WAAW,IAAI,aAAa,OAAK,EAAE,IAAI,MAAM,MAAM,CAAC;AAC1D,eAAO;AAAA,MACT,UAAE;AACA,WAAG,MAAM;AAAA,MACX;AAAA,IACF,QAAQ;AACN,aAAQ,0BAAc,SAAS;AAAA,IACjC;AAAA,EACF,GAAG;AACL;AAEO,SAAS,yBAAkC;AAChD,SAAO,aAAa;AACtB;AAEA,eAAe,SAAY,WAAsB,KAAa,OAAe,MAA4B;AACvG,QAAM,MAAM,MAAM,UAAU,KAAK;AAAA,IAC/B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,GAAI,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,IACrE;AAAA,IACA,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EACpD,CAAC;AACD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,GAAG,GAAG,gBAAW,IAAI,MAAM,EAAE;AAC1D,SAAO,IAAI,KAAK;AAClB;AAEA,eAAe,UAAU,WAAsB,QAAgB,OAAgC;AAC7F,UAAQ,MAAM,SAA4B,WAAW,GAAG,MAAM,4BAA4B,KAAK,GAAG;AACpG;AAGA,eAAsB,kBACpB,WACA,QACA,OACA,MACiB;AACjB,QAAM,OAAO,IAAI,WAAW,MAAM,OAAO,EAAE,UAAU,QAAQ,KAAK,SAAS,CAAC;AAC5E,QAAM,QAAQ,MAAM,UAAU,WAAW,QAAQ,KAAK;AACtD,QAAM,MAAM,MAAM,SAAoC,WAAW,GAAG,MAAM,2BAA2B,OAAO;AAAA,IAC1G,UAAU;AAAA,IACV,YAAY,cAAc,IAAI;AAAA,IAC9B;AAAA,EACF,CAAC;AACD,SAAO,IAAI;AACb;AAMO,SAAS,eACd,gBACA,QACA,OACA,IACA,oBACQ;AACR,MAAI,uBAAuB,QAAW;AACpC,WAAO,uBAAuB,cAAc,IAAI,kBAAkB,IAAI,MAAM,IAAI,KAAK,IAAI,EAAE;AAAA,EAC7F;AACA,SAAO,uBAAuB,cAAc,IAAI,MAAM,IAAI,KAAK,IAAI,EAAE;AACvE;AAOA,eAAsB,YACpB,WACA,QACA,OACA,QACA,OACA,WACkC;AAClC,MAAI;AACF,QAAI,CAAC,uBAAuB,EAAG,QAAO;AACtC,UAAM,OAAO,MAAM,iBAAiB;AACpC,UAAM,cAAc,MAAM,kBAAkB,WAAW,QAAQ,OAAO,IAAI;AAC1E,UAAM,YAAY,WAAW,MAAM,OAAO,IAAI,WAAW,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC;AACpF,UAAM,WAAW,YACb,WAAW,MAAM,OAAO,IAAI,WAAW,MAAM,UAAU,YAAY,CAAC,CAAC,CAAC,IACtE;AACJ,UAAM,QAAQ,MAAM,UAAU,WAAW,QAAQ,KAAK;AACtD,UAAM,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAEvC,UAAM,MAAM,IAAI;AAAA,MACd,MAAM,OAAO,EAAE;AAAA,QACb,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,KAAK;AAAA,QACL,IAAI,KAAK,eAAe,WAAW,QAAQ,OAAO,IAAI,QAAQ,CAAC,CAAC;AAAA,MAClE;AAAA,IACF;AACA,WAAO;AAAA,MACL,eAAe;AAAA,MACf,eAAe;AAAA,MACf,mBAAmB,OAAO,EAAE;AAAA,MAC5B,mBAAmB,cAAc,GAAG;AAAA,IACtC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC9LO,SAAS,aAAa,OAAuB;AAClD,QAAM,MAAM,MAAM,QAAQ,GAAG;AAC7B,SAAO,QAAQ,KAAK,MAAM,MAAM,MAAM,CAAC,IAAI;AAC7C;AAGO,IAAM,aAAqC;AAAA,EAChD,aAAiB,OAAO;AAAA;AAAA,EACxB,iBAAiB,OAAO;AAAA;AAAA,EACxB,UAAiB,MAAO;AAAA;AAC1B;AAOO,SAAS,iBAAiB,QAA+C;AAC9E,QAAM,QAAQ,OAAO,QAAQ,eAAe,CAAC;AAC7C,MAAI,MAAM,SAAS,uBAAuB,EAAG,QAAO;AACpD,MAAI,OAAO,mBAAmB,uBAAuB,MAAM,SAAS,6BAA6B,GAAG;AAClG,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAWO,IAAM,6BAAkD,oBAAI,IAAI;AAAA,EACrE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,iBAAiB,QAA4B;AAC3D,MAAI,OAAO,YAAY,CAAC,OAAO,eAAgB,QAAO;AACtD,SAAO,2BAA2B,IAAI,OAAO,eAAe,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;AAC9E;;;ACtBA,IAAMC,SAAQ,CAAC,OAAe,IAAI,QAAc,OAAK,WAAW,GAAG,EAAE,CAAC;AAGtE,IAAM,yBAAsD,CAAC,OAAO,QAAQ;AAY5E,IAAM,0BAA0B;AAEhC,SAAS,eAAe,QAAwD;AAC9E,SAAO,YAAY;AACrB;AAQA,SAAS,6BAAsC;AAC7C,QAAM,OAAQ,WAA8D;AAC5E,SAAO,CAAC,CAAC,MAAM,UAAU;AAC3B;AAMA,IAAM,iBAAiB,oBAAI,IAAI,CAAC,aAAa,aAAa,OAAO,CAAC;AAElE,SAAS,mBAAmB,QAAsB;AAChD,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,MAAM;AAAA,EACtB,QAAQ;AACN,UAAM,IAAI,cAAc,6CAA6C,MAAM,MAAM,iBAAiB;AAAA,EACpG;AACA,MAAI,IAAI,aAAa,SAAU;AAC/B,MAAI,IAAI,aAAa,WAAW,eAAe,IAAI,IAAI,QAAQ,EAAG;AAClE,QAAM,IAAI;AAAA,IACR,kCAAkC,MAAM;AAAA,IAGxC;AAAA,EACF;AACF;AAEO,IAAM,WAAN,MAAe;AAAA,EAepB,YAAY,QAAwB;AALpC;AAAA;AAAA;AAAA;AAAA,SAAQ,eAAuC;AAC/C,SAAQ,cAA6B;AACrC,SAAQ,iBAAiB;AACzB,SAAQ,gBAAgB;AAGtB,uBAAmB,OAAO,MAAM;AAChC,QAAI,eAAe,MAAM,KAAK,CAAC,2BAA2B,GAAG;AAC3D,YAAM,IAAI;AAAA,QACR;AAAA,QAKA;AAAA,MACF;AAAA,IACF;AACA,SAAK,SAAS,EAAE,QAAQ,aAAa,GAAG,OAAO;AAC/C,SAAK,YAAY,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM;AAC5D,SAAK,gBAAgB,OAAO,eAAe,SAAS,CAAC,GAAG,OAAO,aAAa,IAAI;AAIhF,SAAK,gBAAgB,CAAC,eAAe,MAAM,MAAM,OAAO,iBAAiB;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,YAAY,QAAiC;AACnD,UAAM,WAAW,KAAK,gBAAgB;AACtC,UAAM,aAAa,KAAK,IAAI,IAAI,KAAK,iBAAiB;AAQtD,QAAI,KAAK,gBAAgB,aAAa,cAAc,KAAK,gBAAgB;AACvE,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,cAAc;AACnB,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AACtB,UAAM,SAAS,KAAK;AACpB,UAAM,iBAAiB,eAAe,MAAM,IACxC,iBAAiB,OAAO,QAAQ,OAAO,QAAQ,OAAO,aAAa,QAAQ,KAAK,SAAS,IACzF,OAAO,YAAY,MAAM;AAC7B,UAAM,SAAS,eAAe,KAAK,aAAW;AAQ5C,UAAI,CAAC,SAAS,WAAW;AACvB,cAAM,IAAI;AAAA,UACR;AAAA,QAEF;AAAA,MACF;AACA,WAAK,iBAAiB,KAAK,IAAI,IAAI,QAAQ,aAAa;AACxD,WAAK,gBAAgB;AACrB,aAAO,QAAQ;AAAA,IACjB,CAAC;AAID,WAAO,MAAM,MAAM;AACjB,UAAI,KAAK,iBAAiB,QAAQ;AAChC,aAAK,eAAe;AACpB,aAAK,cAAc;AACnB,aAAK,iBAAiB;AACtB,aAAK,gBAAgB;AAAA,MACvB;AAAA,IACF,CAAC;AACD,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,kBAAkB,SAAqD;AAC3E,QAAI,CAAC,QAAQ,gBAAgB;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,KAAK,YAAY,QAAQ,MAAM;AACnD,UAAM,EAAE,OAAO,IAAI,KAAK;AACxB,UAAM,YAAY,KAAK,UAAU,UAAU,CAAC,OAAe,SAAuB,MAAM,OAAO,IAAI;AACnG,UAAM,SAAS,KAAK,gBAChB,MAAM,YAAY,WAAW,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,WAAW,IAChG;AACJ,WAAO,SAAS,QAAQ,OAAO,SAAS,KAAK,WAAW,QAAQ,KAAK,aAAa;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBAAmB,QAA0C;AACjE,UAAM,QAAQ,MAAM,KAAK,YAAY,MAAM;AAC3C,WAAO,mBAAmB,KAAK,OAAO,QAAQ,OAAO,KAAK,SAAS;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,uBAAuB,QAA8C;AACzE,UAAM,QAAQ,MAAM,KAAK,YAAY,MAAM;AAC3C,WAAO,uBAAuB,KAAK,OAAO,QAAQ,OAAO,KAAK,SAAS;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,OAAuB;AAC1C,WAAO,aAAa,KAAK;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAAiB,OAA2C;AAC1D,WAAO,MAAM,qBAAqB;AAAA,EACpC;AAAA,EAEA,MAAc,aAAa,QAAiC;AAC1D,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAI,cAAc,GAAG,MAAM,+CAA+C,YAAY;AAAA,IAC9F;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,qBAAqB,gBAAoD;AAC7E,UAAM,QAAQ,MAAM,KAAK,aAAa,sBAAsB;AAC5D,WAAO,qBAAqB,KAAK,OAAO,QAAQ,OAAO,gBAAgB,KAAK,SAAS;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,oBACJ,gBACA,OACA,SAC8B;AAC9B,UAAM,QAAQ,MAAM,KAAK,aAAa,qBAAqB;AAC3D,UAAM,EAAE,OAAO,IAAI,KAAK;AACxB,UAAM,YAAY,KAAK,UAAU,UAAU,CAAC,OAAe,SAAuB,MAAM,OAAO,IAAI;AACnG,UAAM,SAAS,KAAK,eAAe;AACnC,UAAM,SAAS,KAAK,gBAChB,MAAM,YAAY,WAAW,QAAQ,OAAO,QAAQ,KAAK,IACzD;AACJ,WAAO,oBAAoB,QAAQ,OAAO,gBAAgB,OAAO,SAAS,KAAK,WAAW,MAAM;AAAA,EAClG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,sBACJ,gBACA,UAAkC,CAAC,GACL;AAC9B,UAAM,QAAQ,MAAM,KAAK,aAAa,uBAAuB;AAC7D,UAAM,WAAW,QAAQ,cAAc;AACvC,UAAM,WAAW,KAAK,IAAI,KAAK,QAAQ,aAAa;AACpD,eAAS;AACP,UAAI,QAAQ,QAAQ,SAAS;AAC3B,cAAM,IAAI,cAAc,4CAA4C,kBAAkB;AAAA,MACxF;AACA,YAAM,IAAI,MAAM,kBAAkB,KAAK,OAAO,QAAQ,OAAO,gBAAgB,KAAK,SAAS;AAC3F,UAAI,EAAE,WAAW,cAAe,QAAO;AACvC,UAAI,KAAK,IAAI,IAAI,WAAW,UAAU;AACpC,cAAM,IAAI,cAAc,8CAA8C,kBAAkB;AAAA,MAC1F;AACA,YAAMA,OAAM,QAAQ;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,cAAc,gBAAmD;AACrE,QAAI,KAAK,gBAAgB,MAAM;AAC7B,YAAM,IAAI,cAAc,4DAA4D,YAAY;AAAA,IAClG;AAGA,UAAM,QAAQ,MAAM,KAAK,YAAY,KAAK,WAAW;AACrD,WAAO,cAAc,KAAK,OAAO,QAAQ,OAAO,gBAAgB,KAAK,WAAW,KAAK,aAAa;AAAA,EACpG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,qBAAqB,gBAAwB,UAAuC;AACxF,UAAM,QAAQ,MAAM,KAAK,aAAa,sBAAsB;AAC5D,WAAO,YAAY,KAAK,OAAO,QAAQ,OAAO,gBAAgB,UAAU,KAAK,SAAS;AAAA,EACxF;AAAA;AAAA,EAGA,IAAI,SAAiB;AACnB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,iBAAqC;AACvC,WAAO,KAAK,OAAO;AAAA,EACrB;AACF;;;ACzUO,IAAM,mBAAwE;AAAA,EACnF,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AACd;AAGO,IAAM,YAAmE;AAAA,EAC9E,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AACR;AAGO,IAAM,oBAAoB;AAE1B,SAAS,iBAAiB,QAAmC;AAClE,SAAO,OAAO,OAAO,CAAC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,MAAM,GAAG,CAAC;AAC7D;AAEO,SAAS,UAAU,QAA2B,WAAgD;AACnG,SAAO,OAAO,KAAK,OAAK,aAAa,EAAE,YAAY,YAAY,EAAE,MAAM;AACzE;AAEO,SAAS,UAAU,SAA8C;AACtE,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,QAAQ,SAAS,eAAe,QAAQ,OAAQ,QAAO,iBAAiB,QAAQ,MAAM;AAC1F,SAAO,iBAAiB,QAAQ,SAAS,UAAU,UAAU,QAAQ;AACvE;AAIA,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,uBAA2C;AACzD,QAAM,KAAM,WAAwD;AACpE,MAAI,CAAC,GAAI,QAAO;AAChB,MAAI,OAAO,GAAG,oBAAoB,WAAY,QAAO;AACrD,SAAO,gBAAgB,KAAK,OAAK,GAAG,gBAAgB,CAAC,CAAC;AACxD;AAEO,SAAS,2BAAoC;AAClD,QAAM,KAAM,WAAyC,WAAW;AAChE,SAAO,CAAC,CAAC,IAAI,gBAAgB,qBAAqB,MAAM;AAC1D;AAGA,eAAsB,kBAAwC;AAC5D,MAAI;AACF,WAAO,MAAM,UAAU,aAAa,aAAa;AAAA,MAC/C,OAAO;AAAA,MACP,OAAO;AAAA,QACL,YAAY;AAAA,QACZ,OAAO,EAAE,OAAO,IAAI;AAAA,QACpB,QAAQ,EAAE,OAAO,IAAI;AAAA,QACrB,WAAW,EAAE,OAAO,IAAI,KAAK,GAAG;AAAA,MAClC;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AACN,UAAM,IAAI,YAAY,uDAAuD;AAAA,EAC/E;AACF;AAUO,IAAM,eAAe;AACrB,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAY5B,IAAM,wBAAsD;AAAA,EACjE,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AACd;AAGO,SAAS,SAAS,MAAiC;AACxD,MAAI,MAAM;AACV,QAAM,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC;AACpC,WAAS,IAAI,GAAG,IAAI,IAAI,GAAG,KAAK,EAAG,QAAO,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC;AACpG,SAAO,IAAI,MAAM,IAAI;AACvB;AAEO,SAAS,mBAAmB,KAAoB,YAAgD;AACrG,MAAI,eAAe,QAAQ,aAAa,oBAAqB,QAAO;AACpE,MAAI,eAAe,QAAQ,aAAa,oBAAqB,QAAO;AACpE,MAAI,QAAQ,QAAQ,MAAM,aAAc,QAAO;AAC/C,SAAO;AACT;AAOA,SAAS,WAAW,OAA2B,UAA0C;AACvF,MAAI,OAAO,MAAM,8BAA8B,WAAY,QAAO,QAAQ,QAAQ,IAAI;AACtF,SAAO,IAAI,QAAQ,aAAW;AAC5B,QAAI,QAA8C;AAClD,QAAI,OAA6C;AACjD,QAAI,SAAS;AACb,UAAM,UAAU,CAAC,KAAa,SAAsC;AAClE,UAAI,CAAC,MAAO,SAAQ,EAAE,GAAG,KAAK,QAAQ,KAAK,gBAAgB;AAC3D,aAAO,EAAE,GAAG,KAAK,QAAQ,KAAK,gBAAgB;AAC9C,eAAS,MAAM,0BAA2B,OAAO;AAAA,IACnD;AACA,aAAS,MAAM,0BAA2B,OAAO;AACjD,eAAW,MAAM;AACf,YAAM,2BAA2B,MAAM;AACvC,UAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,KAAK,MAAM,EAAG,QAAO,QAAQ,IAAI;AAC7D,cAAS,OAAQ,KAAK,SAAS,MAAM,WAAY,KAAK,IAAI,MAAM,EAAE;AAAA,IACpE,GAAG,QAAQ;AAAA,EACb,CAAC;AACH;AAEA,SAAS,kBAAkB,OAAwC;AACjE,QAAM,IAAI,MAAM;AAChB,QAAM,IAAI,MAAM;AAChB,MAAI,CAAC,KAAK,CAAC,KAAK,OAAO,aAAa,YAAa,QAAO;AACxD,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,MAAM,OAAO,WAAW,MAAM,EAAE,oBAAoB,KAAK,CAAC;AAChE,MAAI,CAAC,IAAK,QAAO;AAEjB,MAAI,UAAU,OAAO,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,EAAE;AAC7D,MAAI;AACF,WAAO,SAAS,IAAI,aAAa,GAAG,GAAG,IAAI,EAAE,EAAE,IAAI;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,uBAAuB,OAAyB,WAAW,KAAkC;AACjH,QAAM,MAAM,MAAM,WAAW,OAAO,QAAQ;AAC5C,QAAM,aAAa,kBAAkB,KAAK;AAC1C,SAAO,EAAE,KAAK,YAAY,OAAO,mBAAmB,KAAK,UAAU,EAAE;AACvE;AAsBO,SAAS,eACd,QACA,QACA,UAAiC,CAAC,GACN;AAC5B,QAAM,WAAW,qBAAqB;AACtC,MAAI,aAAa,QAAW;AAC1B,WAAO,QAAQ;AAAA,MACb,IAAI,cAAc,iEAAiE,sBAAsB;AAAA,IAC3G;AAAA,EACF;AACA,QAAM,QAAQ,iBAAiB,MAAM;AACrC,QAAM,SAAS,QAAQ;AAEvB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI;AACJ,QAAI;AACF,iBAAW,IAAI,cAAc,QAAQ;AAAA,QACnC,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,QAC/B,oBAAoB;AAAA,MACtB,CAAC;AAAA,IACH,SAAS,GAAG;AACV,aAAO,IAAI,cAAc,8BAA8B,OAAO,CAAC,CAAC,IAAI,sBAAsB,CAAC;AAC3F;AAAA,IACF;AAEA,UAAM,SAAiB,CAAC;AACxB,QAAI,KAAK;AACT,QAAI,UAA8C;AAClD,QAAI,MAAM;AACV,QAAI,UAAU;AAEd,UAAM,MAAM,MAAO,WAAW,aAAa,MAAM,KAAK,KAAK,IAAI;AAC/D,UAAM,WAAW,CAAC,OAChB,OAAO,0BAA0B,aAAa,sBAAsB,EAAE,IAAK,WAAW,IAAI,EAAE;AAC9F,UAAM,SAAS,CAAC,OACd,OAAO,yBAAyB,aAAa,qBAAqB,EAAE,IAAI,aAAa,EAAE;AAEzF,UAAM,OAAO,MAAM;AACjB,YAAM,UAAU,IAAI,IAAI;AACxB,YAAM,MAAM,UAAU,QAAQ,OAAO;AACrC,UAAI,QAAQ,SAAS;AACnB,kBAAU;AACV,gBAAQ,YAAY,KAAK,OAAO;AAAA,MAClC;AACA,cAAQ,SAAS,KAAK,IAAI,SAAS,KAAK,GAAG,KAAK;AAChD,UAAI,WAAW,QAAQ;AACrB,YAAI,SAAS,UAAU,WAAY,UAAS,KAAK;AACjD;AAAA,MACF;AACA,YAAM,SAAS,IAAI;AAAA,IACrB;AAEA,UAAM,UAAU,MAAM;AACpB,gBAAU;AACV,aAAO,GAAG;AACV,UAAI,SAAS,UAAU,WAAY,UAAS,KAAK;AACjD,aAAO,IAAI,cAAc,qCAAqC,kBAAkB,CAAC;AAAA,IACnF;AACA,QAAI,QAAQ,QAAQ,SAAS;AAC3B,cAAQ;AACR;AAAA,IACF;AACA,YAAQ,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAEjE,aAAS,kBAAkB,OAAK;AAC9B,UAAI,EAAE,QAAQ,EAAE,KAAK,OAAO,EAAG,QAAO,KAAK,EAAE,IAAI;AAAA,IACnD;AACA,aAAS,UAAU,MAAM;AACvB,aAAO,GAAG;AACV,aAAO,IAAI,cAAc,qBAAqB,2BAA2B,CAAC;AAAA,IAC5E;AACA,aAAS,UAAU,MAAM;AACvB,WAAK,IAAI;AACT,YAAM,SAAS,IAAI;AAAA,IACrB;AACA,aAAS,SAAS,MAAM;AACtB,aAAO,GAAG;AACV,cAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,UAAI,QAAS;AACb,YAAM,OAAO,SAAS,YAAY,YAAY;AAC9C,YAAM,QAAQ,IAAI,KAAK,QAAQ,EAAE,KAAK,CAAC;AACvC,UAAI,MAAM,SAAS,GAAG;AACpB,eAAO,IAAI,cAAc,gCAAgC,2BAA2B,CAAC;AACrF;AAAA,MACF;AACA,cAAQ,EAAE,OAAO,UAAU,MAAM,YAAY,KAAK,MAAM,IAAI,IAAI,EAAE,EAAE,CAAC;AAAA,IACvE;AAIA,aAAS,MAAM,GAAI;AAAA,EACrB,CAAC;AACH;;;ACvSA,eAAsB,kBAAkB,QAAwC;AAC9E,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,UAAU,aAAa,aAAa;AAAA,MACjD,OAAO;AAAA,QACL,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,QAKZ,OAAQ,EAAE,OAAO,KAAK;AAAA,QACtB,QAAQ,EAAE,OAAO,KAAK;AAAA;AAAA,QAEtB,WAAW;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH,SAAS,GAAG;AACV,UAAM,IAAI,YAAY,uDAAuD;AAAA,EAC/E;AAEA,WAAS,aAAa,SAAsB,SAA0C;AACpF,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,QAAQ,QAAQ,sBAAsB;AAC5C,YAAM,QAAQ,QAAQ,sBAAsB;AAQ5C,YAAM,QAAS,KAAK,IAAI,MAAM,QAAQ,QAAQ,YAAY,MAAM,SAAS,QAAQ,WAAW;AAC5F,YAAM,WAAW,QAAQ,aAAc,QAAQ,MAAM,SAAU;AAC/D,YAAM,WAAW,QAAQ,cAAc,QAAQ,MAAM,UAAU;AAE/D,YAAM,MAAM,WAAW,MAAM,OAAO,MAAM,SAAS;AACnD,YAAM,MAAM,WAAW,MAAM,MAAO,MAAM,QAAS;AACnD,YAAM,KAAK,MAAM,QAAS;AAC1B,YAAM,KAAK,MAAM,SAAS;AAE1B,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAS;AAChB,aAAO,SAAS;AAChB,aAAO,WAAW,IAAI,EAAG,UAAU,SAAS,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE;AACxE,aAAO,OAAO,UAAQ;AACpB,YAAI,CAAC,MAAM;AAAE,iBAAO,IAAI,YAAY,yBAAyB,CAAC;AAAG;AAAA,QAAQ;AACzE,gBAAQ,IAAI,KAAK,CAAC,IAAI,GAAG,gBAAgB,EAAE,MAAM,aAAa,CAAC,CAAC;AAAA,MAClE,GAAG,cAAc,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,WAAS,OAAO;AACd,WAAO,UAAU,EAAE,QAAQ,OAAK,EAAE,KAAK,CAAC;AAAA,EAC1C;AAEA,OAAK;AACL,SAAO,EAAE,QAAQ,cAAc,KAAK;AACtC;;;ACzCO,IAAM,iBAAN,cAA6B,cAAc;AAAA,EAChD,YAAY,SAAiB;AAC3B,UAAM,SAAS,mBAAmB;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAWA,IAAM,cAAc,KAAK;AACzB,IAAM,YAAY,oBAAI,IAA0D;AAOhF,eAAe,SAAS,QAAgB,WAAuD;AAC7F,QAAM,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,OAAO,EAAE,CAAC,wBAAwB;AAChF,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,cAAc,uCAAuC,IAAI,MAAM,KAAK,kBAAkB;AAC7G,QAAM,EAAE,KAAK,IAAK,MAAM,IAAI,KAAK;AACjC,QAAM,MAAM,oBAAI,IAAuB;AACvC,aAAW,KAAK,QAAQ,CAAC,GAAG;AAC1B,QAAI,EAAE,QAAQ,QAAQ,EAAE,QAAQ,QAAS;AACzC,QAAI;AAAA,MACF,EAAE;AAAA,MACF,MAAM,OAAO,EAAE;AAAA,QACb;AAAA,QACA,EAAE,KAAK,MAAM,KAAK,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,KAAK,KAAK;AAAA,QACrD,EAAE,MAAM,SAAS,YAAY,QAAQ;AAAA,QACrC;AAAA,QACA,CAAC,QAAQ;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACA,YAAU,IAAI,QAAQ,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;AACnD,SAAO;AACT;AAEA,eAAe,OAAO,QAAgB,KAAa,WAA0C;AAC3F,QAAM,SAAS,UAAU,IAAI,MAAM;AACnC,QAAM,QAAQ,CAAC,UAAU,KAAK,IAAI,IAAI,OAAO,MAAM;AACnD,MAAI,OAAO,QAAQ,MAAM,SAAS,QAAQ,SAAS,IAAI,OAAO;AAC9D,MAAI,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC,MAAO,QAAO,MAAM,SAAS,QAAQ,SAAS;AACrE,QAAM,MAAM,KAAK,IAAI,GAAG;AACxB,MAAI,CAAC,IAAK,OAAM,IAAI,eAAe,oCAAoC,GAAG,GAAG;AAC7E,SAAO;AACT;AAEA,SAAS,UAAa,OAAmB,MAAiB;AACxD,MAAI;AACF,WAAO,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAAA,EACnD,QAAQ;AACN,UAAM,IAAI,eAAe,aAAa,IAAI,EAAE;AAAA,EAC9C;AACF;AAEA,eAAe,cACb,WACA,YACA,QACA,MACkC;AAClC,QAAMC,UAAS,UAAmC,iBAAiB,SAAS,GAAG,YAAY;AAE3F,MAAIA,QAAO,QAAQ,WAAW,OAAOA,QAAO,QAAQ,UAAU;AAC5D,UAAM,IAAI,eAAe,yCAAyC;AAAA,EACpE;AACA,QAAM,MAAM,MAAM,OAAO,KAAK,QAAQA,QAAO,KAAK,KAAK,SAAS,KAAK;AACrE,QAAM,KAAK,MAAM,OAAO,EAAE;AAAA,IACxB,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,IACjC;AAAA,IACA,IAAI,iBAAiB,MAAM,CAAC;AAAA,IAC5B,IAAI,KAAK,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC;AAAA,EACxC;AACA,MAAI,CAAC,GAAI,OAAM,IAAI,eAAe,2BAA2B;AAC7D,SAAOA;AACT;AAyBA,eAAsB,kBACpB,aACA,SAC4B;AAC5B,QAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,MAAI,MAAM,WAAW,KAAK,CAAC,MAAM,CAAC,EAAG,OAAM,IAAI,eAAe,uBAAuB;AACrF,QAAMA,UAAS,MAAM,cAAc,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,OAAO;AACxE,MAAIA,QAAO,QAAQ,4BAA6B,OAAM,IAAI,eAAe,0CAA0C;AACnH,QAAM,SAAS,UAA6B,iBAAiB,MAAM,CAAC,CAAC,GAAG,qBAAqB;AAE7F,MAAI,QAAQ,kBAAkB,OAAO,QAAQ,QAAQ,gBAAgB;AACnE,UAAM,IAAI,eAAe,6CAA6C;AAAA,EACxE;AACA,MAAI,QAAQ,aAAa,OAAO,eAAe,QAAQ,WAAW;AAChE,UAAM,IAAI,eAAe,wCAAwC;AAAA,EACnE;AACA,MAAI,QAAQ,aAAa,QAAW;AAClC,UAAM,WAAW,QAAQ,WACrB,iBAAiB,MAAM,OAAO,KAAK,cAAc,QAAQ,QAAQ,CAAC,CAAC,CAAC,IACpE;AACJ,QAAI,OAAO,oBAAoB,UAAU;AACvC,YAAM,IAAI,eAAe,qDAAqD;AAAA,IAChF;AAAA,EACF;AACA,SAAO;AACT;AAiBA,SAAS,OAAO,SAAuB,MAAkC;AACvE,MAAI,OAAQ,QAAoB,QAAQ,WAAY,QAAQ,QAAoB,IAAI,IAAI,KAAK;AAC7F,QAAM,MAAM;AACZ,QAAM,MAAM,OAAO,KAAK,GAAG,EAAE,KAAK,OAAK,EAAE,YAAY,MAAM,KAAK,YAAY,CAAC;AAC7E,QAAM,IAAI,MAAM,IAAI,GAAG,IAAI;AAC3B,SAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI;AACnC;AAEA,eAAe,WAAW,MAAkB,WAAmB,SAAmB,WAAkC;AAClH,QAAM,QAAQ,UAAU,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,MAAM,KAAK,CAAC,CAAqB;AAC/E,QAAM,IAAI,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC;AAC5C,QAAM,OAAO,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;AAC/D,MAAI,CAAC,KAAK,CAAC,KAAK,OAAQ,OAAM,IAAI,eAAe,qCAAqC;AACtF,MAAI,KAAK,IAAI,KAAK,IAAI,IAAI,MAAO,OAAO,CAAC,CAAC,IAAI,UAAW,OAAM,IAAI,eAAe,uDAAuD;AAEzI,QAAM,UAAU,IAAI,WAAW,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC;AAC1D,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,MAAM,OAAO,EAAE,UAAU,OAAO,IAAI,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,QAAQ,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;AACnH,eAAW,OAAO,MAAM;AACtB,YAAM,MAAM,WAAW,GAAG;AAE1B,UAAI,OAAQ,MAAM,OAAO,EAAE,OAAO,QAAQ,KAAK,IAAI,GAAG,GAAG,IAAI,OAAO,CAAC,EAAI;AAAA,IAC3E;AAAA,EACF;AACA,QAAM,IAAI,eAAe,yDAAyD;AACpF;AASA,eAAsB,cACpB,SACA,SACA,SACY;AACZ,QAAM,OACJ,OAAO,YAAY,WAAW,KAAK,OAAO,IAAI,mBAAmB,aAAa,UAAU,IAAI,WAAW,OAAO;AAChH,QAAM,YAAY,QAAQ,oBAAoB;AAC9C,QAAM,UAAU,QAAQ,YAAY,SAAY,CAAC,IAAK,CAAC,EAAe,OAAO,QAAQ,OAAO;AAC5F,MAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,QAAQ;AACtC,UAAM,IAAI,cAAc,oDAAoD,kBAAkB;AAAA,EAChG;AAEA,MAAI,QAAQ,QAAQ;AAClB,UAAM,MAAM,OAAO,SAAS,oBAAoB;AAChD,QAAI,CAAC,IAAK,OAAM,IAAI,eAAe,mCAAmC;AACtE,UAAM,WAAW,MAAM,KAAK,SAAS,SAAS;AAAA,EAChD;AAEA,MAAI,QAAQ,QAAQ;AAClB,UAAM,MAAM,OAAO,SAAS,cAAc;AAC1C,QAAI,CAAC,IAAK,OAAM,IAAI,eAAe,6BAA6B;AAChE,UAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,GAAI,OAAM,IAAI,eAAe,gDAAgD;AACpH,UAAM,IAAI,MAAM,cAAc,MAAM,CAAC,GAAG,iBAAiB,IAAI,GAAG,MAAM,CAAC,GAAG,EAAE,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,MAAM,CAAC;AAC1H,QAAI,EAAE,QAAQ,uBAAwB,OAAM,IAAI,eAAe,mCAAmC;AAClG,QAAI,OAAO,EAAE,QAAQ,YAAY,KAAK,IAAI,KAAK,IAAI,IAAI,MAAO,EAAE,GAAG,IAAI,WAAW;AAChF,YAAM,IAAI,eAAe,qDAAqD;AAAA,IAChF;AAAA,EACF;AAEA,SAAO,UAAa,MAAM,cAAc;AAC1C;;;AC3OO,SAAS,iBAA0B;AACxC,MAAI,OAAO,cAAc,YAAa,QAAO;AAK7C,QAAM,SAAU,UAAmE;AACnF,MAAI,OAAO,QAAQ,WAAW,UAAW,QAAO,OAAO;AAEvD,QAAM,KAAK,UAAU,aAAa;AAClC,MAAI,oEAAoE,KAAK,EAAE,EAAG,QAAO;AAKzF,MAAI,QAAQ,KAAK,EAAE,KAAM,GAAG,SAAS,WAAW,KAAK,UAAU,iBAAiB,EAAI,QAAO;AAK3F,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,qCAAqC,EAAE,SAAS;AACvG,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;ACwBO,IAAM,UAAN,MAAM,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0HZ,YAGW,SAGA,sBAEhB,eAEA,KAAU;AAPM;AAGA;AAlBlB;AAAA;AAAA,SAAiB,UAAoC,CAAC;AAGtD;AAAA,SAAiB,aAAoC,CAAC;AAsBrD,QAAI,UAAU,QAAO,eAAe,UAAU,QAAO;AACpD,YAAM,IAAI,WAAW,4BAA4B;AAClD,QAAI,MAAM,MAAM,MAAM;AACrB,YAAM,IAAI,WAAW,yBAAyB;AAC/C,SAAK,OAAO,UAAU,IAAI;AAG1B,QAAI,MAAsB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM;AAC9B,UAAI,KAAK,KAAK;AACf,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,WAAK,QAAW,KAAK,IAAI,MAAM,CAAC;AAChC,WAAK,WAAW,KAAK,IAAI,MAAM,CAAC;AAAA,IACjC;AAGA,SAAK,qBAAqB;AAC1B,UAAM,eAA4B,KAAK,oBAAoB,aAAa;AACxE,SAAK,cAAc,YAAY;AAG/B,QAAI,OAAO,IAAI;AACd,UAAI,aAAkB;AACtB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,aAAK,UAAU,CAAC;AAChB,aAAK,eAAe,CAAC;AACrB,cAAM,UAAe,KAAK,gBAAgB;AAC1C,YAAI,UAAU,YAAY;AACzB,gBAAM;AACN,uBAAa;AAAA,QACd;AACA,aAAK,UAAU,CAAC;AAAA,MACjB;AAAA,IACD;AACA,WAAO,KAAK,OAAO,OAAO,CAAC;AAC3B,SAAK,OAAO;AACZ,SAAK,UAAU,GAAG;AAClB,SAAK,eAAe,GAAG;AAEvB,SAAK,aAAa,CAAC;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAtKA,OAAc,WAAW,MAAc,KAAyB;AAC/D,UAAM,OAAyB,UAAU,aAAa,IAAI;AAC1D,WAAO,QAAO,eAAe,MAAM,GAAG;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAc,aAAa,MAA6B,KAAyB;AAChF,UAAM,MAAiB,UAAU,UAAU,IAAI;AAC/C,WAAO,QAAO,eAAe,CAAC,GAAG,GAAG,GAAG;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAc,eAAe,MAAkC,KAC7D,aAAkB,GAAG,aAAkB,IACvC,OAAY,IAAI,WAAoB,MAAc;AAEnD,QAAI,EAAE,QAAO,eAAe,cAAc,cAAc,cAAc,cAAc,QAAO,gBACtF,OAAO,MAAM,OAAO;AACxB,YAAM,IAAI,WAAW,eAAe;AAGrC,QAAI;AACJ,QAAI;AACJ,SAAK,UAAU,cAAc,WAAW;AACvC,YAAMC,oBAAwB,QAAO,oBAAoB,SAAS,GAAG,IAAI;AACzE,YAAM,WAAmB,UAAU,aAAa,MAAM,OAAO;AAC7D,UAAI,YAAYA,mBAAkB;AACjC,uBAAe;AACf;AAAA,MACD;AACA,UAAI,WAAW;AACd,cAAM,IAAI,WAAW,eAAe;AAAA,IACtC;AAGA,eAAW,UAAU,CAAC,QAAO,IAAI,QAAQ,QAAO,IAAI,UAAU,QAAO,IAAI,IAAI,GAAG;AAC/E,UAAI,YAAY,gBAAgB,QAAO,oBAAoB,SAAS,MAAM,IAAI;AAC7E,cAAM;AAAA,IACR;AAGA,QAAI,KAAiB,CAAC;AACtB,eAAW,OAAO,MAAM;AACvB,iBAAW,IAAI,KAAK,UAAU,GAAG,EAAE;AACnC,iBAAW,IAAI,UAAU,IAAI,KAAK,iBAAiB,OAAO,GAAG,EAAE;AAC/D,iBAAW,KAAK,IAAI,QAAQ;AAC3B,WAAG,KAAK,CAAC;AAAA,IACX;AACA,WAAO,GAAG,UAAU,YAAY;AAGhC,UAAM,mBAAwB,QAAO,oBAAoB,SAAS,GAAG,IAAI;AACzE,WAAO,GAAG,UAAU,gBAAgB;AACpC,eAAW,GAAG,KAAK,IAAI,GAAG,mBAAmB,GAAG,MAAM,GAAG,EAAE;AAC3D,eAAW,IAAI,IAAI,GAAG,SAAS,KAAK,GAAG,EAAE;AACzC,WAAO,GAAG,SAAS,KAAK,CAAC;AAGzB,aAAS,UAAU,KAAM,GAAG,SAAS,kBAAkB,WAAW,MAAO;AACxE,iBAAW,SAAS,GAAG,EAAE;AAG1B,QAAI,gBAA6B,CAAC;AAClC,WAAO,cAAc,SAAS,IAAI,GAAG;AACpC,oBAAc,KAAK,CAAC;AACrB,OAAG,QAAQ,CAAC,GAAQ,MACnB,cAAc,MAAM,CAAC,KAAK,KAAM,KAAK,IAAI,EAAG;AAG7C,WAAO,IAAI,QAAO,SAAS,KAAK,eAAe,IAAI;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAyFO,UAAU,GAAQ,GAAiB;AACzC,WAAO,KAAK,KAAK,IAAI,KAAK,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC/E;AAAA;AAAA;AAAA,EAMQ,uBAA6B;AAEpC,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,WAAK,kBAAkB,GAAG,GAAG,IAAI,KAAK,CAAC;AACvC,WAAK,kBAAkB,GAAG,GAAG,IAAI,KAAK,CAAC;AAAA,IACxC;AAGA,SAAK,kBAAkB,GAAG,CAAC;AAC3B,SAAK,kBAAkB,KAAK,OAAO,GAAG,CAAC;AACvC,SAAK,kBAAkB,GAAG,KAAK,OAAO,CAAC;AAGvC,UAAM,cAA0B,KAAK,6BAA6B;AAClE,UAAM,WAAgB,YAAY;AAClC,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAClC,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAElC,YAAI,EAAE,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,WAAW,KAAK,KAAK,WAAW,KAAK,KAAK;AAClF,eAAK,qBAAqB,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,MAC1D;AAAA,IACD;AAGA,SAAK,eAAe,CAAC;AACrB,SAAK,YAAY;AAAA,EAClB;AAAA;AAAA;AAAA,EAKQ,eAAe,MAAiB;AAEvC,UAAM,OAAY,KAAK,qBAAqB,cAAc,IAAI;AAC9D,QAAI,MAAW;AACf,aAAS,IAAI,GAAG,IAAI,IAAI;AACvB,YAAO,OAAO,KAAO,QAAQ,KAAK;AACnC,UAAM,QAAQ,QAAQ,KAAK,OAAO;AAClC,WAAO,SAAS,MAAM,CAAC;AAGvB,aAAS,IAAI,GAAG,KAAK,GAAG;AACvB,WAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC7C,SAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5C,SAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5C,SAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5C,aAAS,IAAI,GAAG,IAAI,IAAI;AACvB,WAAK,kBAAkB,KAAK,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAGlD,aAAS,IAAI,GAAG,IAAI,GAAG;AACtB,WAAK,kBAAkB,KAAK,OAAO,IAAI,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC7D,aAAS,IAAI,GAAG,IAAI,IAAI;AACvB,WAAK,kBAAkB,GAAG,KAAK,OAAO,KAAK,GAAG,OAAO,MAAM,CAAC,CAAC;AAC9D,SAAK,kBAAkB,GAAG,KAAK,OAAO,GAAG,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC3B,QAAI,KAAK,UAAU;AAClB;AAGD,QAAI,MAAW,KAAK;AACpB,aAAS,IAAI,GAAG,IAAI,IAAI;AACvB,YAAO,OAAO,KAAO,QAAQ,MAAM;AACpC,UAAM,OAAY,KAAK,WAAW,KAAK;AACvC,WAAO,SAAS,MAAM,CAAC;AAGvB,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC5B,YAAM,QAAiB,OAAO,MAAM,CAAC;AACrC,YAAM,IAAS,KAAK,OAAO,KAAK,IAAI;AACpC,YAAM,IAAS,KAAK,MAAM,IAAI,CAAC;AAC/B,WAAK,kBAAkB,GAAG,GAAG,KAAK;AAClC,WAAK,kBAAkB,GAAG,GAAG,KAAK;AAAA,IACnC;AAAA,EACD;AAAA;AAAA;AAAA,EAKQ,kBAAkB,GAAQ,GAAc;AAC/C,aAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAChC,eAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAChC,cAAM,OAAY,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC;AACrD,cAAM,KAAU,IAAI;AACpB,cAAM,KAAU,IAAI;AACpB,YAAI,KAAK,MAAM,KAAK,KAAK,QAAQ,KAAK,MAAM,KAAK,KAAK;AACrD,eAAK,kBAAkB,IAAI,IAAI,QAAQ,KAAK,QAAQ,CAAC;AAAA,MACvD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA,EAKQ,qBAAqB,GAAQ,GAAc;AAClD,aAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAChC,eAAS,KAAK,IAAI,MAAM,GAAG;AAC1B,aAAK,kBAAkB,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC;AAAA,IAClF;AAAA,EACD;AAAA;AAAA;AAAA,EAKQ,kBAAkB,GAAQ,GAAQ,QAAuB;AAChE,SAAK,QAAQ,CAAC,EAAE,CAAC,IAAI;AACrB,SAAK,WAAW,CAAC,EAAE,CAAC,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAOQ,oBAAoB,MAA0C;AACrE,UAAM,MAAW,KAAK;AACtB,UAAM,MAAkB,KAAK;AAC7B,QAAI,KAAK,UAAU,QAAO,oBAAoB,KAAK,GAAG;AACrD,YAAM,IAAI,WAAW,kBAAkB;AAGxC,UAAM,YAAiB,QAAO,4BAA4B,IAAI,OAAO,EAAE,GAAG;AAC1E,UAAM,cAAmB,QAAO,wBAA0B,IAAI,OAAO,EAAE,GAAG;AAC1E,UAAM,eAAoB,KAAK,MAAM,QAAO,qBAAqB,GAAG,IAAI,CAAC;AACzE,UAAM,iBAAsB,YAAY,eAAe;AACvD,UAAM,gBAAqB,KAAK,MAAM,eAAe,SAAS;AAG9D,QAAI,SAA6B,CAAC;AAClC,UAAM,QAAqB,QAAO,0BAA0B,WAAW;AACvE,aAAS,IAAI,GAAG,IAAI,GAAG,IAAI,WAAW,KAAK;AAC1C,UAAI,MAAmB,KAAK,MAAM,GAAG,IAAI,gBAAgB,eAAe,IAAI,iBAAiB,IAAI,EAAE;AACnG,WAAK,IAAI;AACT,YAAM,MAAmB,QAAO,4BAA4B,KAAK,KAAK;AACtE,UAAI,IAAI;AACP,YAAI,KAAK,CAAC;AACX,aAAO,KAAK,IAAI,OAAO,GAAG,CAAC;AAAA,IAC5B;AAGA,QAAI,SAAsB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,OAAO,CAAC,EAAE,QAAQ,KAAK;AAC1C,aAAO,QAAQ,CAAC,OAAO,MAAM;AAE5B,YAAI,KAAK,gBAAgB,eAAe,KAAK;AAC5C,iBAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MACtB,CAAC;AAAA,IACF;AACA,WAAO,OAAO,UAAU,YAAY;AACpC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA,EAKQ,cAAc,MAAmC;AACxD,QAAI,KAAK,UAAU,KAAK,MAAM,QAAO,qBAAqB,KAAK,OAAO,IAAI,CAAC;AAC1E,YAAM,IAAI,WAAW,kBAAkB;AACxC,QAAI,IAAS;AAEb,aAAS,QAAQ,KAAK,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG;AACvD,UAAI,SAAS;AACZ,gBAAQ;AACT,eAAS,OAAO,GAAG,OAAO,KAAK,MAAM,QAAQ;AAC5C,iBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,gBAAM,IAAS,QAAQ;AACvB,gBAAM,UAAoB,QAAQ,IAAK,MAAM;AAC7C,gBAAM,IAAS,SAAS,KAAK,OAAO,IAAI,OAAO;AAC/C,cAAI,CAAC,KAAK,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,GAAG;AAClD,iBAAK,QAAQ,CAAC,EAAE,CAAC,IAAI,OAAO,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,EAAE;AACtD;AAAA,UACD;AAAA,QAGD;AAAA,MACD;AAAA,IACD;AACA,WAAO,KAAK,KAAK,SAAS,CAAC;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,UAAU,MAAiB;AAClC,QAAI,OAAO,KAAK,OAAO;AACtB,YAAM,IAAI,WAAW,yBAAyB;AAC/C,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,YAAI;AACJ,gBAAQ,MAAM;AAAA,UACb,KAAK;AAAI,sBAAU,IAAI,KAAK,KAAK;AAAoC;AAAA,UACrE,KAAK;AAAI,qBAAS,IAAI,KAAK;AAA0C;AAAA,UACrE,KAAK;AAAI,qBAAS,IAAI,KAAK;AAA0C;AAAA,UACrE,KAAK;AAAI,sBAAU,IAAI,KAAK,KAAK;AAAoC;AAAA,UACrE,KAAK;AAAI,sBAAU,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,KAAK;AAAI;AAAA,UACrE,KAAK;AAAI,qBAAS,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;AAA0B;AAAA,UACrE,KAAK;AAAI,sBAAU,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,KAAK;AAAoB;AAAA,UACrE,KAAK;AAAI,uBAAW,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK;AAAkB;AAAA,UACrE;AAAU,kBAAM,IAAI,MAAM,aAAa;AAAA,QACxC;AACA,YAAI,CAAC,KAAK,WAAW,CAAC,EAAE,CAAC,KAAK;AAC7B,eAAK,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;AAAA,MACzC;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA,EAKQ,kBAAuB;AAC9B,QAAI,SAAc;AAGlB,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,UAAI,WAAW;AACf,UAAI,OAAO;AACX,UAAI,aAAa,CAAC,GAAE,GAAE,GAAE,GAAE,GAAE,GAAE,CAAC;AAC/B,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,YAAI,KAAK,QAAQ,CAAC,EAAE,CAAC,KAAK,UAAU;AACnC;AACA,cAAI,QAAQ;AACX,sBAAU,QAAO;AAAA,mBACT,OAAO;AACf;AAAA,QACF,OAAO;AACN,eAAK,wBAAwB,MAAM,UAAU;AAC7C,cAAI,CAAC;AACJ,sBAAU,KAAK,2BAA2B,UAAU,IAAI,QAAO;AAChE,qBAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC5B,iBAAO;AAAA,QACR;AAAA,MACD;AACA,gBAAU,KAAK,+BAA+B,UAAU,MAAM,UAAU,IAAI,QAAO;AAAA,IACpF;AAEA,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,UAAI,WAAW;AACf,UAAI,OAAO;AACX,UAAI,aAAa,CAAC,GAAE,GAAE,GAAE,GAAE,GAAE,GAAE,CAAC;AAC/B,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AACnC,YAAI,KAAK,QAAQ,CAAC,EAAE,CAAC,KAAK,UAAU;AACnC;AACA,cAAI,QAAQ;AACX,sBAAU,QAAO;AAAA,mBACT,OAAO;AACf;AAAA,QACF,OAAO;AACN,eAAK,wBAAwB,MAAM,UAAU;AAC7C,cAAI,CAAC;AACJ,sBAAU,KAAK,2BAA2B,UAAU,IAAI,QAAO;AAChE,qBAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC5B,iBAAO;AAAA,QACR;AAAA,MACD;AACA,gBAAU,KAAK,+BAA+B,UAAU,MAAM,UAAU,IAAI,QAAO;AAAA,IACpF;AAGA,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,GAAG,KAAK;AACvC,eAAS,IAAI,GAAG,IAAI,KAAK,OAAO,GAAG,KAAK;AACvC,cAAM,QAAiB,KAAK,QAAQ,CAAC,EAAE,CAAC;AACxC,YAAM,SAAS,KAAK,QAAQ,CAAC,EAAE,IAAI,CAAC,KAC9B,SAAS,KAAK,QAAQ,IAAI,CAAC,EAAE,CAAC,KAC9B,SAAS,KAAK,QAAQ,IAAI,CAAC,EAAE,IAAI,CAAC;AACvC,oBAAU,QAAO;AAAA,MACnB;AAAA,IACD;AAGA,QAAI,OAAY;AAChB,eAAW,OAAO,KAAK;AACtB,aAAO,IAAI,OAAO,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,IAAI,IAAI;AAC9D,UAAM,QAAa,KAAK,OAAO,KAAK;AAEpC,UAAM,IAAS,KAAK,KAAK,KAAK,IAAI,OAAO,KAAK,QAAQ,EAAE,IAAI,KAAK,IAAI;AACrE,WAAO,KAAK,KAAK,KAAK,CAAC;AACvB,cAAU,IAAI,QAAO;AACrB,WAAO,KAAK,UAAU,UAAU,OAAO;AACvC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,+BAA2C;AAClD,QAAI,KAAK,WAAW;AACnB,aAAO,CAAC;AAAA,SACJ;AACJ,YAAM,WAAgB,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI;AACrD,YAAM,OAAY,KAAK,OAAO,KAAK,UAAU,IAAI,WAAW,IAAI,MAAM,WAAW,IAAI,EAAE,IAAI;AAC3F,UAAI,SAAqB,CAAC,CAAC;AAC3B,eAAS,MAAM,KAAK,OAAO,GAAG,OAAO,SAAS,UAAU,OAAO;AAC9D,eAAO,OAAO,GAAG,GAAG,GAAG;AACxB,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAMA,OAAe,qBAAqB,KAAe;AAClD,QAAI,MAAM,QAAO,eAAe,MAAM,QAAO;AAC5C,YAAM,IAAI,WAAW,6BAA6B;AACnD,QAAI,UAAe,KAAK,MAAM,OAAO,MAAM;AAC3C,QAAI,OAAO,GAAG;AACb,YAAM,WAAgB,KAAK,MAAM,MAAM,CAAC,IAAI;AAC5C,iBAAW,KAAK,WAAW,MAAM,WAAW;AAC5C,UAAI,OAAO;AACV,kBAAU;AAAA,IACZ;AACA,WAAO,OAAO,UAAU,UAAU,KAAK;AACvC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAMA,OAAe,oBAAoB,KAAU,KAAsB;AAClE,WAAO,KAAK,MAAM,QAAO,qBAAqB,GAAG,IAAI,CAAC,IACrD,QAAO,wBAA4B,IAAI,OAAO,EAAE,GAAG,IACnD,QAAO,4BAA4B,IAAI,OAAO,EAAE,GAAG;AAAA,EACrD;AAAA;AAAA;AAAA,EAKA,OAAe,0BAA0B,QAA0B;AAClE,QAAI,SAAS,KAAK,SAAS;AAC1B,YAAM,IAAI,WAAW,qBAAqB;AAG3C,QAAI,SAAsB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,SAAS,GAAG;AAC/B,aAAO,KAAK,CAAC;AACd,WAAO,KAAK,CAAC;AAKb,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAEhC,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACvC,eAAO,CAAC,IAAI,QAAO,oBAAoB,OAAO,CAAC,GAAG,IAAI;AACtD,YAAI,IAAI,IAAI,OAAO;AAClB,iBAAO,CAAC,KAAK,OAAO,IAAI,CAAC;AAAA,MAC3B;AACA,aAAO,QAAO,oBAAoB,MAAM,CAAI;AAAA,IAC7C;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAIA,OAAe,4BAA4B,MAA6B,SAA6C;AACpH,QAAI,SAAsB,QAAQ,IAAI,OAAK,CAAC;AAC5C,eAAW,KAAK,MAAM;AACrB,YAAM,SAAe,IAAK,OAAO,MAAM;AACvC,aAAO,KAAK,CAAC;AACb,cAAQ,QAAQ,CAAC,MAAM,MACtB,OAAO,CAAC,KAAK,QAAO,oBAAoB,MAAM,MAAM,CAAC;AAAA,IACvD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA,EAKA,OAAe,oBAAoB,GAAS,GAAe;AAC1D,QAAI,MAAM,KAAK,KAAK,MAAM,KAAK;AAC9B,YAAM,IAAI,WAAW,mBAAmB;AAEzC,QAAI,IAAS;AACb,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC5B,UAAK,KAAK,KAAO,MAAM,KAAK;AAC5B,YAAO,MAAM,IAAK,KAAK;AAAA,IACxB;AACA,WAAO,MAAM,KAAK,CAAC;AACnB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA,EAKQ,2BAA2B,YAAuC;AACzE,UAAM,IAAS,WAAW,CAAC;AAC3B,WAAO,KAAK,KAAK,OAAO,CAAC;AACzB,UAAM,OAAgB,IAAI,KAAK,WAAW,CAAC,KAAK,KAAK,WAAW,CAAC,KAAK,IAAI,KAAK,WAAW,CAAC,KAAK,KAAK,WAAW,CAAC,KAAK;AACtH,YAAQ,QAAQ,WAAW,CAAC,KAAK,IAAI,KAAK,WAAW,CAAC,KAAK,IAAI,IAAI,MAC3D,QAAQ,WAAW,CAAC,KAAK,IAAI,KAAK,WAAW,CAAC,KAAK,IAAI,IAAI;AAAA,EACpE;AAAA;AAAA,EAIQ,+BAA+B,iBAA0B,kBAAuB,YAA6B;AACpH,QAAI,iBAAiB;AACpB,WAAK,wBAAwB,kBAAkB,UAAU;AACzD,yBAAmB;AAAA,IACpB;AACA,wBAAoB,KAAK;AACzB,SAAK,wBAAwB,kBAAkB,UAAU;AACzD,WAAO,KAAK,2BAA2B,UAAU;AAAA,EAClD;AAAA;AAAA,EAIQ,wBAAwB,kBAAuB,YAA8B;AACpF,QAAI,WAAW,CAAC,KAAK;AACpB,0BAAoB,KAAK;AAC1B,eAAW,IAAI;AACf,eAAW,QAAQ,gBAAgB;AAAA,EACpC;AAkCD;AAAA;AAAA;AAzoBa,QA6mBW,cAAoB;AAAA;AA7mB/B,QA+mBW,cAAmB;AAAA;AA/mB9B,QAknBY,aAAmB;AAlnB/B,QAmnBY,aAAmB;AAnnB/B,QAonBY,aAAkB;AApnB9B,QAqnBY,aAAkB;AArnB9B,QAunBY,0BAA6C;AAAA;AAAA;AAAA,EAGpE,CAAC,IAAK,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EACnK,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EACnK,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EACnK,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AACpK;AA9nBY,QAgoBY,8BAAiD;AAAA;AAAA;AAAA,EAGxE,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EACzJ,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAI,GAAI,GAAI,GAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EACzJ,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EACzJ,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAC1J;AAvoBM,IAAM,SAAN;AA8oBP,SAAS,WAAW,KAAU,KAAU,IAAsB;AAC7D,MAAI,MAAM,KAAK,MAAM,MAAM,QAAQ,OAAO;AACzC,UAAM,IAAI,WAAW,oBAAoB;AAC1C,WAAS,IAAI,MAAM,GAAG,KAAK,GAAG;AAC7B,OAAG,KAAM,QAAQ,IAAK,CAAC;AACzB;AAIA,SAAS,OAAO,GAAQ,GAAiB;AACxC,UAAS,MAAM,IAAK,MAAM;AAC3B;AAIA,SAAS,OAAO,MAAqB;AACpC,MAAI,CAAC;AACJ,UAAM,IAAI,MAAM,iBAAiB;AACnC;AAiBO,IAAM,aAAN,MAAM,WAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAuGf,YAEW,MAKA,UAGC,SAAqB;AARtB;AAKA;AAGC;AAElB,QAAI,WAAW;AACd,YAAM,IAAI,WAAW,kBAAkB;AACxC,SAAK,UAAU,QAAQ,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EA/GA,OAAc,UAAU,MAAwC;AAC/D,QAAI,KAAiB,CAAC;AACtB,eAAW,KAAK;AACf,iBAAW,GAAG,GAAG,EAAE;AACpB,WAAO,IAAI,WAAU,WAAU,KAAK,MAAM,KAAK,QAAQ,EAAE;AAAA,EAC1D;AAAA;AAAA,EAIA,OAAc,YAAY,QAA2B;AACpD,QAAI,CAAC,WAAU,UAAU,MAAM;AAC9B,YAAM,IAAI,WAAW,wCAAwC;AAC9D,QAAI,KAAiB,CAAC;AACtB,aAAS,IAAI,GAAG,IAAI,OAAO,UAAU;AACpC,YAAM,IAAS,KAAK,IAAI,OAAO,SAAS,GAAG,CAAC;AAC5C,iBAAW,SAAS,OAAO,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE;AAClE,WAAK;AAAA,IACN;AACA,WAAO,IAAI,WAAU,WAAU,KAAK,SAAS,OAAO,QAAQ,EAAE;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAMA,OAAc,iBAAiB,MAAyB;AACvD,QAAI,CAAC,WAAU,eAAe,IAAI;AACjC,YAAM,IAAI,WAAW,6DAA6D;AACnF,QAAI,KAAiB,CAAC;AACtB,QAAI;AACJ,SAAK,IAAI,GAAG,IAAI,KAAK,KAAK,QAAQ,KAAK,GAAG;AACzC,UAAI,OAAY,WAAU,qBAAqB,QAAQ,KAAK,OAAO,CAAC,CAAC,IAAI;AACzE,cAAQ,WAAU,qBAAqB,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC;AACjE,iBAAW,MAAM,IAAI,EAAE;AAAA,IACxB;AACA,QAAI,IAAI,KAAK;AACZ,iBAAW,WAAU,qBAAqB,QAAQ,KAAK,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE;AACzE,WAAO,IAAI,WAAU,WAAU,KAAK,cAAc,KAAK,QAAQ,EAAE;AAAA,EAClE;AAAA;AAAA;AAAA,EAKA,OAAc,aAAa,MAAgC;AAE1D,QAAI,QAAQ;AACX,aAAO,CAAC;AAAA,aACA,WAAU,UAAU,IAAI;AAChC,aAAO,CAAC,WAAU,YAAY,IAAI,CAAC;AAAA,aAC3B,WAAU,eAAe,IAAI;AACrC,aAAO,CAAC,WAAU,iBAAiB,IAAI,CAAC;AAAA;AAExC,aAAO,CAAC,WAAU,UAAU,WAAU,gBAAgB,IAAI,CAAC,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA,EAKA,OAAc,QAAQ,WAA2B;AAChD,QAAI,KAAiB,CAAC;AACtB,QAAI,YAAY;AACf,YAAM,IAAI,WAAW,mCAAmC;AAAA,aAChD,YAAa,KAAK;AAC1B,iBAAW,WAAW,GAAG,EAAE;AAAA,aACnB,YAAa,KAAK,IAAK;AAC/B,iBAAW,GAAM,GAAG,EAAE;AACtB,iBAAW,WAAW,IAAI,EAAE;AAAA,IAC7B,WAAW,YAAY,KAAS;AAC/B,iBAAW,GAAO,GAAG,EAAE;AACvB,iBAAW,WAAW,IAAI,EAAE;AAAA,IAC7B;AACC,YAAM,IAAI,WAAW,mCAAmC;AACzD,WAAO,IAAI,WAAU,WAAU,KAAK,KAAK,GAAG,EAAE;AAAA,EAC/C;AAAA;AAAA;AAAA,EAKA,OAAc,UAAU,MAAuB;AAC9C,WAAO,WAAU,cAAc,KAAK,IAAI;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAMA,OAAc,eAAe,MAAuB;AACnD,WAAO,WAAU,mBAAmB,KAAK,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA,EA6BO,UAAsB;AAC5B,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA,EAKA,OAAc,aAAa,MAAkC,SAAsB;AAClF,QAAI,SAAiB;AACrB,eAAW,OAAO,MAAM;AACvB,YAAM,SAAc,IAAI,KAAK,iBAAiB,OAAO;AACrD,UAAI,IAAI,YAAa,KAAK;AACzB,eAAO;AACR,gBAAU,IAAI,SAAS,IAAI,QAAQ;AAAA,IACpC;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAIA,OAAe,gBAAgB,KAA0B;AACxD,UAAM,UAAU,GAAG;AACnB,QAAI,SAAsB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACpC,UAAI,IAAI,OAAO,CAAC,KAAK;AACpB,eAAO,KAAK,IAAI,WAAW,CAAC,CAAC;AAAA,WACzB;AACJ,eAAO,KAAK,SAAS,IAAI,UAAU,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AACrD,aAAK;AAAA,MACN;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAeD;AAAA;AAAA;AA3Ka,WAkKY,gBAAwB;AAAA;AAlKpC,WAqKY,qBAA6B;AAAA;AAAA;AArKzC,WAyKY,uBAA+B;AAzKjD,IAAM,YAAN;AAAA,CAkLA,CAAUC,YAAV;AAQC,QAAM,OAAN,MAAM,KAAI;AAAA;AAAA;AAAA,IAYR,YAES,SAEA,YAAiB;AAFjB;AAEA;AAAA,IAAkB;AAAA,EAEpC;AAdC;AAAA,EAJY,KAIW,MAAW,IAAI,KAAI,GAAG,CAAC;AAC9C;AAAA,EALY,KAKW,SAAW,IAAI,KAAI,GAAG,CAAC;AAC9C;AAAA,EANY,KAMW,WAAW,IAAI,KAAI,GAAG,CAAC;AAC9C;AAAA,EAPY,KAOW,OAAW,IAAI,KAAI,GAAG,CAAC;AAPxC,MAAM,MAAN;AAAA,EAAAA,QAAM;AAAA,GARG;AAAA,CAiCV,CAAUC,eAAV;AAQC,QAAM,QAAN,MAAM,MAAK;AAAA;AAAA,IAaT,YAES,UAEC,kBAAiC;AAFlC;AAEC;AAAA,IAAkC;AAAA;AAAA;AAAA;AAAA,IAO7C,iBAAiB,KAAe;AACtC,aAAO,KAAK,iBAAiB,KAAK,OAAO,MAAM,KAAK,EAAE,CAAC;AAAA,IACxD;AAAA,EAED;AAxBC;AAAA,EAJY,MAIW,UAAe,IAAI,MAAK,GAAK,CAAC,IAAI,IAAI,EAAE,CAAC;AAChE,EALY,MAKW,eAAe,IAAI,MAAK,GAAK,CAAE,GAAG,IAAI,EAAE,CAAC;AAChE,EANY,MAMW,OAAe,IAAI,MAAK,GAAK,CAAE,GAAG,IAAI,EAAE,CAAC;AAChE,EAPY,MAOW,QAAe,IAAI,MAAK,GAAK,CAAE,GAAG,IAAI,EAAE,CAAC;AAChE,EARY,MAQW,MAAe,IAAI,MAAK,GAAK,CAAE,GAAI,GAAI,CAAC,CAAC;AAR1D,MAAM,OAAN;AAAA,EAAAA,WAAM;AAAA,GARG;;;ACz7BjB,IAAM,MAA6C;AAAA,EACjD,KAAU,OAAO,IAAI;AAAA,EACrB,QAAU,OAAO,IAAI;AAAA,EACrB,UAAU,OAAO,IAAI;AAAA,EACrB,MAAU,OAAO,IAAI;AACvB;AAQO,SAAS,YAAY,MAAc,MAAyB,UAAuB;AACxF,QAAM,KAAK,OAAO,WAAW,MAAM,IAAI,GAAG,CAAC;AAC3C,QAAM,OAAoB,CAAC;AAC3B,WAAS,IAAI,GAAG,IAAI,GAAG,MAAM,KAAK;AAChC,UAAM,MAAiB,CAAC;AACxB,aAAS,IAAI,GAAG,IAAI,GAAG,MAAM,IAAK,KAAI,KAAK,GAAG,UAAU,GAAG,CAAC,CAAC;AAC7D,SAAK,KAAK,GAAG;AAAA,EACf;AACA,SAAO;AACT;;;ACxBA,IAAM,cAAiB;AACvB,IAAM,WAAkB;AACxB,IAAM,aAAkB;AACxB,IAAM,gBAAkB;AACxB,IAAM,gBAAkB;AAExB,IAAM,iBAAoC,CAAC,eAAe,YAAY,iBAAiB;AAMvF,IAAM,qBAAqB,KAAK,KAAK;AAG9B,IAAM,qBAAqB,CAAC,aAAa,UAAU,YAAY,eAAe,aAAa;AAS3F,SAAS,uBAA+B;AAC7C,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AAGA,SAAO,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE;AAC9F;AAqBO,SAAS,gBACd,SACA,QACQ;AACR,QAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,MAAI,aAAa,IAAI,aAAa,OAAO,KAAK;AAC9C,MAAI,aAAa,IAAI,UAAU,OAAO,KAAK,IAAI,CAAC,CAAC;AACjD,MAAI,OAAO,OAAQ,KAAI,aAAa,IAAI,YAAY,OAAO,MAAM;AACjE,MAAI,OAAO,OAAQ,KAAI,aAAa,IAAI,eAAe,OAAO,MAAM;AACpE,MAAI,OAAO,eAAgB,KAAI,aAAa,IAAI,eAAe,OAAO,eAAe,YAAY,CAAC;AAClG,SAAO,IAAI,SAAS;AACtB;AAGO,SAAS,oBAAmC;AACjD,QAAM,QAAuB,EAAE,OAAO,MAAM,QAAQ,MAAM,QAAQ,MAAM,gBAAgB,KAAK;AAC7F,MAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,QAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,QAAM,QAAQ,OAAO,IAAI,WAAW;AACpC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,KAAK,OAAO,OAAO,IAAI,QAAQ,CAAC;AACtC,QAAM,MAAM,KAAK,IAAI,IAAI;AACzB,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,MAAM,KAAK,MAAM,oBAAoB;AAG/D,WAAO,EAAE,GAAG,OAAO,MAAM;AAAA,EAC3B;AAEA,QAAM,YAAY,OAAO,IAAI,aAAa;AAC1C,QAAM,aAAa,OAAO,IAAI,aAAa;AAC3C,QAAM,cAAc,aAAa,IAAI,KAAK,UAAU,IAAI;AAExD,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,OAAO,IAAI,UAAU;AAAA,IAC7B,QAAQ,eAAe,SAAS,SAAmB,IAAK,YAAuB;AAAA,IAC/E,gBAAgB,eAAe,CAAC,OAAO,MAAM,YAAY,QAAQ,CAAC,IAAI,cAAc;AAAA,EACtF;AACF;AAGO,SAAS,iCAAyC;AACvD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,qBAAmB,QAAQ,SAAO,IAAI,aAAa,OAAO,GAAG,CAAC;AAC9D,SAAO,IAAI,SAAS;AACtB;AAGO,SAAS,mCAAyC;AACvD,MAAI,OAAO,WAAW,YAAa;AACnC,QAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,qBAAmB,QAAQ,SAAO,IAAI,aAAa,OAAO,GAAG,CAAC;AAC9D,SAAO,QAAQ,aAAa,MAAM,IAAI,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM,GAAG,IAAI,IAAI,EAAE;AACjF;","names":["header","shift","a","b","hasMrz","signCapture","sleep","header","dataCapacityBits","QrCode","QrSegment"]}
|