@sekyro/web 0.1.0-rc.5 → 0.1.0-rc.6
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/CHANGELOG.md +10 -0
- package/dist/{chunk-SGDXLAXR.js → chunk-R3SJQHYU.js} +11 -1
- package/dist/{chunk-SGDXLAXR.js.map → chunk-R3SJQHYU.js.map} +1 -1
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/{create-verification-CA-3szPI.d.cts → create-verification-BKc9NFQj.d.cts} +8 -1
- package/dist/{create-verification-c98uAxbl.d.ts → create-verification-DjcMZHAQ.d.ts} +8 -1
- package/dist/{hosted-client-DWzCRYJK.d.ts → hosted-client-DJXq_CB9.d.cts} +29 -1
- package/dist/{hosted-client-DWzCRYJK.d.cts → hosted-client-DJXq_CB9.d.ts} +29 -1
- package/dist/index.cjs +10 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/internal/index.cjs +10 -0
- package/dist/internal/index.cjs.map +1 -1
- package/dist/internal/index.d.cts +3 -3
- package/dist/internal/index.d.ts +3 -3
- package/dist/internal/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @sekyro/web
|
|
2
2
|
|
|
3
|
+
## 0.1.0-rc.6
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 54b153c: `HostedSessionView.voice` + `Verification.voiceClips()` — Sekyro-hosted voice clips of the session (used by `@sekyro/react` verification mode).
|
|
8
|
+
|
|
9
|
+
- **`HostedVoiceView` / `HostedVoiceLocale`** (new types): `{ setId, clips: { ar?, fr?, en? } }`, each locale mapping a voice key (`fcv3.*`) to an absolute clip URL on the Sekyro assets host (`assets.sekyro.ai`). A page with a Content-Security-Policy must allow that origin in `media-src`.
|
|
10
|
+
- **`HostedSessionView.voice?: HostedVoiceView | null`** — `null` when no clip set is active; absent on older APIs.
|
|
11
|
+
- **`verification.voiceClips()`** resolves the session's clips (fetching the hosted view if needed), or `null`. It never rejects: a failed bootstrap or a destroyed verification resolves `null`.
|
|
12
|
+
|
|
3
13
|
## 0.1.0-rc.5
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
|
@@ -1105,6 +1105,16 @@ function createVerificationInternal(options, internals) {
|
|
|
1105
1105
|
consent: () => ensureConsent(),
|
|
1106
1106
|
startRegistry,
|
|
1107
1107
|
liveness,
|
|
1108
|
+
voiceClips: async () => {
|
|
1109
|
+
if (destroyed) return null;
|
|
1110
|
+
try {
|
|
1111
|
+
const current = await ensureView();
|
|
1112
|
+
if (destroyed) return null;
|
|
1113
|
+
return current.voice ?? null;
|
|
1114
|
+
} catch {
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1108
1118
|
on: (event, handler) => emitter.on(event, handler),
|
|
1109
1119
|
destroy: () => {
|
|
1110
1120
|
destroyed = true;
|
|
@@ -1160,4 +1170,4 @@ export {
|
|
|
1160
1170
|
createVerification,
|
|
1161
1171
|
createVerificationInternal
|
|
1162
1172
|
};
|
|
1163
|
-
//# sourceMappingURL=chunk-
|
|
1173
|
+
//# sourceMappingURL=chunk-R3SJQHYU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/capture/preload.ts","../src/verification/emitter.ts","../src/capture/wake-lock.ts","../src/capture/create-liveness-capture.ts","../src/verification/result.ts","../src/verification/liveness-flow.ts","../src/verification/turnstile.ts","../src/verification/create-verification.ts"],"sourcesContent":["// Detector preload (spec D3–D5) — the public generalization of identity-hosted's\n// lib/warm-models.ts. Warms the v3 face detector (blaze tier, generous init\n// deadline: nobody is racing this) so a capture mounts with a HOT instance and\n// the hosts' Start control can wait on a status instead of racing the engine's\n// 10 s cold-init deadline. One entry per normalized asset base URL, stored on a\n// globalThis Symbol.for key so two bundled copies of @sekyro/web share it.\n// createLivenessCapture consumes it through preloadedDetectorFactory (a\n// NON-OWNING view: CaptureSessionV3.destroy() can never tear the shared\n// instance down — it survives remounts and retries).\nimport {\n createDetector,\n DEFAULT_V3_ASSET_BASE_URL,\n type Detector,\n} from \"../core/v3/detector\";\n\n/** Preload state per asset base URL. */\nexport type LivenessAssetsStatus = \"idle\" | \"loading\" | \"ready\" | \"failed\";\n\n/** Options of {@link preloadLivenessAssets}. */\nexport interface PreloadLivenessAssetsOptions {\n /** Same value as the capture's `assetBaseUrl`; default `/models-v3`. */\n assetBaseUrl?: string;\n /** Defer the load to browser idle time (`requestIdleCallback`, 1.2 s timeout fallback). Default `false`. */\n idle?: boolean;\n /** Skip when the browser asks to save data (`navigator.connection.saveData`). Default `false`. */\n respectSaveData?: boolean;\n}\n\n/** Blaze init deadline for the preload — generous: a slow ~11 MB wasm download must finish. */\nexport const PRELOAD_BLAZE_INIT_TIMEOUT_MS = 120_000;\n/** Outer budget reclaiming only a genuinely hung load (≥ the init deadline + slack). */\nexport const PRELOAD_BUDGET_MS = 130_000;\nconst IDLE_TIMEOUT_MS = 3_000;\nconst IDLE_FALLBACK_MS = 1_200;\n\n/** An idle-deferred load not yet started (status stays 'idle' meanwhile). */\ninterface ScheduledLoad {\n readonly promise: Promise<LivenessAssetsStatus>;\n /** Drop the pending idle callback and settle `promise` with `outcome`. */\n readonly settle: (outcome: LivenessAssetsStatus | Promise<LivenessAssetsStatus>) => void;\n}\n\ninterface PreloadEntry {\n status: LivenessAssetsStatus;\n promise: Promise<LivenessAssetsStatus> | null;\n scheduled: ScheduledLoad | null;\n detectorPromise: Promise<Detector> | null;\n detector: Detector | null;\n readonly listeners: Set<(status: LivenessAssetsStatus) => void>;\n}\n\nconst REGISTRY_KEY = Symbol.for(\"@sekyro/web.preload.v1\");\ntype RegistryHost = { [REGISTRY_KEY]?: Map<string, PreloadEntry> };\n\nfunction registry(): Map<string, PreloadEntry> {\n const host = globalThis as unknown as RegistryHost;\n host[REGISTRY_KEY] ??= new Map();\n return host[REGISTRY_KEY];\n}\n\nfunction normalizeBase(assetBaseUrl: string | undefined): string {\n const base = (assetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL).replace(/\\/+$/, \"\");\n return base === \"\" ? \"/\" : base;\n}\n\nfunction entryFor(base: string): PreloadEntry {\n const map = registry();\n let entry = map.get(base);\n if (entry === undefined) {\n entry = { status: \"idle\", promise: null, scheduled: null, detectorPromise: null, detector: null, listeners: new Set() };\n map.set(base, entry);\n }\n return entry;\n}\n\nfunction setStatus(entry: PreloadEntry, next: LivenessAssetsStatus): void {\n if (entry.status === next) return;\n entry.status = next;\n for (const listener of [...entry.listeners]) listener(next);\n}\n\ntype Loader = (base: string) => Promise<Detector>;\nconst defaultLoader: Loader = (base) =>\n createDetector({ preferred: [\"blaze\"], v3AssetBaseUrl: base, blazeInitTimeoutMs: PRELOAD_BLAZE_INIT_TIMEOUT_MS });\nlet loader: Loader = defaultLoader;\n\n/** @internal Test seam: replace the detector loader (null restores the real one). */\nexport function setPreloadLoaderForTests(next: Loader | null): void {\n loader = next ?? defaultLoader;\n}\n\n/** @internal Test seam: drop every entry (destroying warmed detectors). */\nexport function resetLivenessAssetsForTests(): void {\n for (const entry of registry().values()) entry.detector?.destroy();\n registry().clear();\n loader = defaultLoader;\n}\n\nfunction canCapture(): boolean {\n return (\n typeof window !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof navigator.mediaDevices?.getUserMedia === \"function\"\n );\n}\n\nfunction saveDataOn(): boolean {\n return (navigator as { connection?: { saveData?: boolean } }).connection?.saveData === true;\n}\n\n/** Schedules `run` at idle time; returns the cancel function. */\nfunction scheduleIdle(run: () => void): () => void {\n const w = window as Window & {\n requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n cancelIdleCallback?: (handle: number) => void;\n };\n if (typeof w.requestIdleCallback === \"function\") {\n const handle = w.requestIdleCallback(run, { timeout: IDLE_TIMEOUT_MS });\n return () => w.cancelIdleCallback?.(handle);\n }\n const timer = setTimeout(run, IDLE_FALLBACK_MS);\n return () => clearTimeout(timer);\n}\n\n/**\n * Defer the load to idle time. The schedule is cancellable (release) and\n * promotable (an immediate preload / a capture needing the detector): either\n * settles it, so a late-firing idle callback is a no-op — never a second load\n * and never undoing a release.\n */\nfunction schedule(entry: PreloadEntry, base: string): Promise<LivenessAssetsStatus> {\n let resolve!: (outcome: LivenessAssetsStatus | Promise<LivenessAssetsStatus>) => void;\n const promise = new Promise<LivenessAssetsStatus>((res) => {\n resolve = res;\n });\n let done = false;\n let cancel: () => void = () => undefined;\n const scheduled: ScheduledLoad = {\n promise,\n settle: (outcome) => {\n if (done) return;\n done = true;\n cancel();\n if (entry.scheduled === scheduled) entry.scheduled = null;\n resolve(outcome);\n },\n };\n entry.scheduled = scheduled;\n cancel = scheduleIdle(() => {\n if (done) return;\n scheduled.settle(begin(entry, base));\n });\n return promise;\n}\n\nfunction begin(entry: PreloadEntry, base: string): Promise<LivenessAssetsStatus> {\n if ((entry.status === \"loading\" || entry.status === \"ready\") && entry.promise !== null) return entry.promise;\n setStatus(entry, \"loading\");\n // A synchronously throwing loader becomes a rejection ('failed'), never a throw.\n const detectorPromise = new Promise<Detector>((resolve) => resolve(loader(base)));\n entry.detectorPromise = detectorPromise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const budget = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // A late resolution is an orphan (wasm instance) — release it.\n detectorPromise.then((late) => late.destroy(), () => undefined);\n reject(new Error(`detector preload exceeded ${PRELOAD_BUDGET_MS} ms`));\n }, PRELOAD_BUDGET_MS);\n });\n const settled = Promise.race([detectorPromise, budget]).then(\n (detector): LivenessAssetsStatus => {\n if (entry.detectorPromise !== detectorPromise) {\n detector.destroy(); // released (or superseded) while loading\n return entry.status;\n }\n entry.detector = detector;\n setStatus(entry, \"ready\");\n return \"ready\";\n },\n (err: unknown): LivenessAssetsStatus => {\n if (entry.detectorPromise === detectorPromise) {\n entry.detectorPromise = null;\n entry.detector = null;\n setStatus(entry, \"failed\");\n console.warn(\"[@sekyro/web] face-detector preload failed:\", err);\n }\n return entry.status;\n },\n );\n const promise = settled.finally(() => clearTimeout(timer));\n entry.promise = promise;\n entry.scheduled?.settle(promise); // promote a pending idle schedule into this load\n return promise;\n}\n\n/**\n * Warm the face detector for `assetBaseUrl` so the capture starts instantly.\n * Idempotent per base URL and never rejects: resolves the settled status.\n * `'idle'` means nothing was preloaded here (SSR, no camera API, Data-Saver\n * skip) — start normally. Calling again after `'failed'` retries the load.\n * With `idle: true` the load is deferred (status stays `'idle'` until it\n * starts); a later non-idle call — or a capture mounting meanwhile — starts it\n * at once, and {@link releaseLivenessAssets} cancels it.\n */\nexport function preloadLivenessAssets(options: PreloadLivenessAssetsOptions = {}): Promise<LivenessAssetsStatus> {\n if (!canCapture()) return Promise.resolve(\"idle\");\n const base = normalizeBase(options.assetBaseUrl);\n const entry = entryFor(base);\n if (options.respectSaveData === true && saveDataOn()) return Promise.resolve(entry.status);\n if ((entry.status === \"loading\" || entry.status === \"ready\") && entry.promise !== null) return entry.promise;\n if (options.idle === true) return entry.scheduled?.promise ?? schedule(entry, base);\n return begin(entry, base);\n}\n\n/** Current preload status for `assetBaseUrl` (default `/models-v3`). */\nexport function getLivenessAssetsStatus(assetBaseUrl?: string): LivenessAssetsStatus {\n return registry().get(normalizeBase(assetBaseUrl))?.status ?? \"idle\";\n}\n\n/**\n * Subscribe to status changes for `assetBaseUrl`; returns the unsubscribe\n * function. Does NOT emit the current status on subscribe (read it with\n * {@link getLivenessAssetsStatus}) — the\n * `useSyncExternalStore(subscribe, getSnapshot)` contract, so it plugs into\n * that hook directly.\n */\nexport function subscribeLivenessAssets(\n listener: (status: LivenessAssetsStatus) => void,\n assetBaseUrl?: string,\n): () => void {\n const entry = entryFor(normalizeBase(assetBaseUrl));\n entry.listeners.add(listener);\n return () => {\n entry.listeners.delete(listener);\n };\n}\n\n/** Free the warmed detector (an SPA leaving the flow). Never call while a capture is mounted. */\nexport function releaseLivenessAssets(assetBaseUrl?: string): void {\n const entry = registry().get(normalizeBase(assetBaseUrl));\n if (entry === undefined) return;\n entry.scheduled?.settle(\"idle\"); // cancel a pending idle-deferred load\n entry.detector?.destroy();\n entry.detector = null;\n entry.detectorPromise = null; // an in-flight load is destroyed when it lands (begin)\n entry.promise = null;\n setStatus(entry, \"idle\");\n}\n\nfunction nonOwningView(shared: Detector): Detector {\n return {\n get tier() {\n return shared.tier;\n },\n get fallbackReason() {\n return shared.fallbackReason;\n },\n detect: (...args: Parameters<Detector[\"detect\"]>) => shared.detect(...args),\n destroy: () => {\n // no-op: the preload registry owns the shared detector\n },\n };\n}\n\n/**\n * @internal The capture engine's detector source when a preload exists for\n * `assetBaseUrl`: a non-owning view of the warm instance (waiting for an\n * in-flight load), or — once that load failed — a cold detector OWNED by the\n * session (default ladder + deadline). `undefined` when nothing was preloaded.\n * Invoking it on an idle-scheduled preload starts that load now and joins it.\n */\nexport function preloadedDetectorFactory(assetBaseUrl?: string): (() => Promise<Detector>) | undefined {\n const base = normalizeBase(assetBaseUrl);\n const entry = registry().get(base);\n if (\n entry === undefined ||\n (entry.detector === null && entry.detectorPromise === null && entry.scheduled === null)\n ) {\n return undefined;\n }\n return async () => {\n if (entry.scheduled !== null) void begin(entry, base); // promote: the capture needs it now\n // Wait on the SETTLED preload (not the raw detector promise): a load that\n // blew the budget or was released lands destroyed and must not be wrapped.\n // Re-check after each settle — a release + new load meanwhile is joined.\n let waitingOn = entry.promise;\n while (entry.detector === null && waitingOn !== null) {\n await waitingOn;\n if (entry.promise === waitingOn) break;\n waitingOn = entry.promise;\n }\n return entry.detector !== null ? nonOwningView(entry.detector) : createDetector({ v3AssetBaseUrl: base });\n };\n}\n","// Tiny typed emitter (zero deps). Handlers are snapshotted before dispatch so an\n// unsubscribe from inside a handler never skips a sibling.\n\nexport interface Emitter<Events extends object> {\n on<K extends keyof Events>(event: K, handler: (payload: Events[K]) => void): () => void;\n emit<K extends keyof Events>(event: K, payload: Events[K]): void;\n clear(): void;\n}\n\nexport function createEmitter<Events extends object>(): Emitter<Events> {\n const handlers = new Map<keyof Events, Set<(payload: never) => void>>();\n return {\n on(event, handler) {\n let set = handlers.get(event);\n if (set === undefined) {\n set = new Set();\n handlers.set(event, set);\n }\n const h = handler as (payload: never) => void;\n set.add(h);\n return () => {\n set?.delete(h);\n };\n },\n emit(event, payload) {\n const set = handlers.get(event);\n if (set === undefined) return;\n for (const handler of [...set]) {\n (handler as (p: Events[typeof event]) => void)(payload);\n }\n },\n clear() {\n handlers.clear();\n },\n };\n}\n","// Screen Wake Lock for the live camera (starting / gating / recording): keeps a\n// phone from dimming or locking mid-capture. Best-effort by design — SSR-safe,\n// feature-detected at call time, and NEVER throws or rejects into the capture:\n// a refused request (NotAllowedError while hidden, `screen-wake-lock`\n// Permissions-Policy blocked in an iframe, insecure context) just means no lock.\n\n/** The wake-lock controller the capture handle drives from its state stream. */\nexport interface ScreenWakeLock {\n /** `true` while the camera is live: hold the lock (and re-take it on return to the page). */\n setActive(active: boolean): void;\n}\n\nfunction wakeLockApi(): WakeLock | null {\n if (typeof navigator === \"undefined\" || !(\"wakeLock\" in navigator)) return null;\n const api = navigator.wakeLock;\n return typeof api?.request === \"function\" ? api : null;\n}\n\nfunction isVisible(): boolean {\n return typeof document === \"undefined\" || document.visibilityState === \"visible\";\n}\n\nfunction releaseQuietly(sentinel: WakeLockSentinel): void {\n try {\n void sentinel.release().catch(() => undefined);\n } catch {\n // A throwing release (already released, odd polyfill) is not the capture's problem.\n }\n}\n\n/** `enabled: false` (`keepScreenAwake: false`) → a no-op controller that never requests. */\nexport function createScreenWakeLock(options: { enabled?: boolean } = {}): ScreenWakeLock {\n if (options.enabled === false) return { setActive: () => undefined };\n\n let wanted = false;\n let sentinel: WakeLockSentinel | null = null;\n // One request at a time: the state stream repeats live states (preflight →\n // acquiring both map to `starting`), and a visibility return may race them.\n let inFlight = false;\n let listening = false;\n\n const acquire = (): void => {\n if (!wanted || inFlight || (sentinel !== null && !sentinel.released)) return;\n const api = wakeLockApi();\n // Hidden → the request would reject with NotAllowedError; the\n // visibilitychange listener takes it once the page is visible again.\n if (api === null || !isVisible()) return;\n let request: Promise<WakeLockSentinel>;\n try {\n request = api.request(\"screen\");\n } catch {\n return;\n }\n inFlight = true;\n request.then(\n (granted) => {\n inFlight = false;\n // Released (captured / error / destroy) while the request was pending.\n if (!wanted) {\n releaseQuietly(granted);\n return;\n }\n sentinel = granted;\n // The browser auto-releases on page hide; forget it so a return re-acquires.\n granted.addEventListener?.(\"release\", () => {\n if (sentinel === granted) sentinel = null;\n });\n },\n () => {\n inFlight = false;\n },\n );\n };\n\n const onVisibilityChange = (): void => {\n if (isVisible()) acquire();\n };\n\n const listen = (on: boolean): void => {\n if (typeof document === \"undefined\" || on === listening) return;\n listening = on;\n if (on) document.addEventListener(\"visibilitychange\", onVisibilityChange);\n else document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n };\n\n return {\n setActive: (active) => {\n wanted = active;\n if (active) {\n // No API → nothing to re-acquire either; skip the listener.\n if (wakeLockApi() === null) return;\n listen(true);\n acquire();\n return;\n }\n listen(false);\n if (sentinel !== null) {\n const held = sentinel;\n sentinel = null;\n releaseQuietly(held);\n }\n },\n };\n}\n","// createLivenessCapture — the public, framework-free capture handle (spec\n// §3.1) over the calibration-pinned CaptureSessionV3 engine. It MAPS the\n// engine's event surface (statechange / checks / guidance / artifacts / error /\n// frozen) onto the partner contract (state / gate / captured / error) and owns\n// the NO_RECORDER degrade (photo mode → keyframe-only `captured`). Pinned\n// capture constants stay inside the engine — nothing here is configurable.\nimport { detectIsMobile } from \"../core/capture-constraints\";\nimport { isMediaRecorderSupported } from \"../core/media\";\nimport type { CaptureErrorKind as EngineErrorKind, CaptureState as EngineState } from \"../core/types\";\nimport {\n CaptureSessionV3,\n type CaptureSessionV3Events,\n type CaptureSessionV3Options,\n} from \"../core/v3/capture-session-v3\";\nimport { createDetector as createLadderDetector } from \"../core/v3/detector\";\nimport { createEmitter } from \"../verification/emitter\";\nimport { preloadedDetectorFactory } from \"./preload\";\nimport { createScreenWakeLock } from \"./wake-lock\";\nimport type {\n CaptureErrorKind,\n CaptureState,\n GateHint,\n LivenessCaptureEvents,\n LivenessCaptureHandle,\n LivenessCaptureOptions,\n} from \"../verification/types\";\n\n/** The engine surface the handle binds — CaptureSessionV3 or a test double. */\nexport interface CaptureEngine {\n readonly state: EngineState;\n on<K extends keyof CaptureSessionV3Events>(\n event: K,\n handler: (payload: CaptureSessionV3Events[K]) => void,\n ): () => void;\n /** `facingMode` sets the camera for this attempt and later retries. */\n start(options?: { facingMode?: \"user\" | \"environment\" }): Promise<void>;\n retry(): void;\n switchCamera(): void;\n destroy(): void;\n /** Device-profile chrome hint (LOW class → `'reduced'`); absent on doubles. */\n readonly animations?: \"full\" | \"reduced\";\n}\n\n/** Internal construction seams (tests; Sekyro's own hosted page). */\nexport interface LivenessCaptureInternals {\n /** Replace the engine constructor (default: `new CaptureSessionV3(options)`). */\n createEngine?: (options: CaptureSessionV3Options) => CaptureEngine;\n /** Extra engine options merged in (worker offload, warmed detector, telemetry). */\n engineOptions?: Partial<\n Pick<CaptureSessionV3Options, \"createDetector\" | \"createWorker\" | \"telemetry\" | \"deviceClass\">\n >;\n /** Clock (default `performance.now`). */\n now?: () => number;\n}\n\n/** fcv3.* guidance key → public gate hint code. */\nexport function mapGuidanceHint(key: string): GateHint | null {\n switch (key) {\n case \"fcv3.no_face\":\n case \"fcv3.center_face\":\n case \"fcv3.straighten\":\n return \"center\";\n case \"fcv3.come_closer\":\n return \"closer\";\n case \"fcv3.move_back\":\n return \"farther\";\n case \"fcv3.more_light\":\n case \"fcv3.too_bright\":\n return \"light\";\n case \"fcv3.hold_still\":\n case \"fcv3.steady_camera\":\n return \"still\";\n default:\n return null;\n }\n}\n\n/** Engine state → public state (`preflight|acquiring → starting`, `positioning → gating`). */\nexport function mapEngineState(state: EngineState): CaptureState {\n switch (state) {\n case \"preflight\":\n case \"acquiring\":\n return \"starting\";\n case \"positioning\":\n return \"gating\";\n case \"idle\":\n case \"recording\":\n case \"captured\":\n case \"error\":\n return state;\n default: {\n const unhandled: never = state;\n throw new Error(`mapEngineState: unhandled state ${unhandled as string}`);\n }\n }\n}\n\n/** Engine error kinds → the public six (`models|cliptoolarge|recorder|overconstrained → generic`). */\nexport function mapErrorKind(kind: EngineErrorKind): CaptureErrorKind {\n switch (kind) {\n case \"permission\":\n case \"notfound\":\n case \"inuse\":\n case \"insecure\":\n case \"unsupported\":\n return kind;\n case \"overconstrained\":\n case \"generic\":\n case \"models\":\n case \"cliptoolarge\":\n case \"recorder\":\n return \"generic\";\n default: {\n const unhandled: never = kind;\n throw new Error(`mapErrorKind: unhandled kind ${unhandled as string}`);\n }\n }\n}\n\n/** Handle + the hooks the verification layer uses to drive the flow states. */\nexport interface LivenessCaptureInternalHandle {\n handle: LivenessCaptureHandle;\n /** Push a post-capture state (`uploading | checking | done`). */\n setFlowState(state: Extract<CaptureState, \"uploading\" | \"checking\" | \"done\">): void;\n}\n\nexport function createLivenessCaptureInternal(\n options: LivenessCaptureOptions,\n internals: LivenessCaptureInternals = {},\n): LivenessCaptureInternalHandle {\n const now = internals.now ?? (() => performance.now());\n const emitter = createEmitter<LivenessCaptureEvents>();\n // NO_RECORDER degrade: no MediaRecorder → photo mode (keyframe only). Decided\n // ONCE at construction; the artifacts carry the flag.\n const degraded = !isMediaRecorderSupported();\n // Preloaded detector (spec D3): resolved at CALL time — the preload may start\n // after this handle is constructed (React mounts both in one commit). Host\n // seams win: an explicit createDetector, or worker offload (createWorker).\n // Without a preload this is the engine's own cold default, unchanged.\n const hostSeams = internals.engineOptions;\n const preloadAware: Pick<CaptureSessionV3Options, \"createDetector\"> =\n hostSeams?.createDetector === undefined && hostSeams?.createWorker === undefined\n ? {\n createDetector: () => {\n const warmed = preloadedDetectorFactory(options.assetBaseUrl);\n return warmed !== undefined\n ? warmed()\n : createLadderDetector(\n options.assetBaseUrl !== undefined ? { v3AssetBaseUrl: options.assetBaseUrl } : {},\n );\n },\n }\n : {};\n const engineOptions: CaptureSessionV3Options = {\n video: options.video,\n mode: degraded ? \"photo\" : \"clip\",\n ...(options.facing !== undefined ? { facingMode: options.facing } : {}),\n ...(options.assetBaseUrl !== undefined ? { v3AssetBaseUrl: options.assetBaseUrl } : {}),\n ...(internals.engineOptions ?? {}),\n ...preloadAware,\n };\n const engine: CaptureEngine =\n internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);\n\n let state: CaptureState = \"idle\";\n let hint: GateHint | null = null;\n let progress = 0;\n let faceDetected = false;\n let animations: \"full\" | \"reduced\" = \"full\";\n let recordingStartedAt: number | null = null;\n let frozenAt: number | null = null;\n // Frame size latched while the stream is LIVE: the engine stops the tracks\n // before it emits `artifacts`, and iOS Safari then reports videoWidth /\n // videoHeight as 0 (field: comparison records with width 0 / height 0).\n let frameWidth = 0;\n let frameHeight = 0;\n const latchFrameSize = (): void => {\n if (options.video.videoWidth > 0 && options.video.videoHeight > 0) {\n frameWidth = options.video.videoWidth;\n frameHeight = options.video.videoHeight;\n }\n };\n\n // Screen wake lock while the camera is live; every other state (captured,\n // the post-capture flow states, error, idle) releases it.\n const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });\n const setState = (next: CaptureState): void => {\n state = next;\n wakeLock.setActive(next === \"starting\" || next === \"gating\" || next === \"recording\");\n emitter.emit(\"state\", next);\n };\n const emitGate = (): void => emitter.emit(\"gate\", { progress, hint, faceDetected });\n // The LOW-profile hint settles once the engine probes (statechange) and may\n // flip mid-positioning on a dynamic demotion (per checks tick) — emit on change.\n const syncProfile = (): void => {\n const next = engine.animations ?? \"full\";\n if (next === animations) return;\n animations = next;\n emitter.emit(\"profile\", { animations });\n };\n\n const unsubscribes = [\n engine.on(\"statechange\", ({ state: engineState }) => {\n const next = mapEngineState(engineState);\n if (next === \"starting\" && state !== \"starting\") {\n // Fresh attempt: gate chrome resets.\n hint = null;\n progress = 0;\n faceDetected = false;\n recordingStartedAt = null;\n frozenAt = null;\n frameWidth = 0;\n frameHeight = 0;\n }\n setState(next);\n syncProfile();\n if (next === \"recording\") {\n latchFrameSize();\n recordingStartedAt = now();\n hint = \"hold\";\n progress = 1;\n emitGate();\n }\n }),\n engine.on(\"checks\", (payload) => {\n latchFrameSize(); // the engine only ticks on a live frame (videoWidth > 0)\n progress = payload.progress;\n faceDetected = payload.factors.presence > 0;\n emitGate();\n syncProfile();\n }),\n engine.on(\"guidance\", ({ key }) => {\n hint = mapGuidanceHint(key);\n emitGate();\n }),\n engine.on(\"frozen\", ({ previewDataUrl }) => {\n frozenAt = now();\n emitter.emit(\"frozen\", { previewDataUrl });\n }),\n engine.on(\"artifacts\", (artifacts) => {\n const clipMs =\n recordingStartedAt !== null && frozenAt !== null\n ? Math.max(0, Math.round(frozenAt - recordingStartedAt))\n : undefined;\n emitter.emit(\"captured\", {\n keyframe: artifacts.keyframe,\n ...(artifacts.clip !== undefined ? { clip: artifacts.clip } : {}),\n ...(artifacts.clipMimeType !== undefined ? { clipMime: artifacts.clipMimeType } : {}),\n width: options.video.videoWidth || frameWidth,\n height: options.video.videoHeight || frameHeight,\n ...(clipMs !== undefined ? { clipMs } : {}),\n captureChannel: detectIsMobile() ? \"mobile\" : \"desktop\",\n ...(degraded ? { degraded: \"NO_RECORDER\" as const } : {}),\n engine: \"v3\",\n // Face-capture timing: the engine's marks, forwarded flat (absent → absent).\n ...(artifacts.timing?.cameraReadyMs !== undefined\n ? { cameraReadyMs: artifacts.timing.cameraReadyMs }\n : {}),\n ...(artifacts.timing?.faceAcquireMs !== undefined\n ? { faceAcquireMs: artifacts.timing.faceAcquireMs }\n : {}),\n ...(artifacts.timing?.captureMs !== undefined\n ? { captureMs: artifacts.timing.captureMs }\n : {}),\n });\n }),\n engine.on(\"error\", ({ kind, retryable }) => {\n emitter.emit(\"error\", { kind: mapErrorKind(kind), retryable });\n }),\n ];\n\n const handle: LivenessCaptureHandle = {\n get state() {\n return state;\n },\n on: (event, handler) => emitter.on(event, handler),\n start: (startOptions) =>\n engine.start(startOptions?.facing !== undefined ? { facingMode: startOptions.facing } : {}),\n retry: () => engine.retry(),\n switchCamera: () => engine.switchCamera(),\n destroy: () => {\n for (const unsubscribe of unsubscribes) unsubscribe();\n wakeLock.setActive(false);\n engine.destroy();\n emitter.clear();\n },\n };\n\n return {\n handle,\n setFlowState: (next) => {\n if (state !== next) setState(next);\n },\n };\n}\n\n/**\n * Create a standalone liveness capture (no upload — pair it with your own\n * submission, or use {@link Verification.liveness} for the full flow).\n */\nexport function createLivenessCapture(options: LivenessCaptureOptions): LivenessCaptureHandle {\n return createLivenessCaptureInternal(options).handle;\n}\n","// Result mapping: server view → the public VerificationResult (spec §3.1) plus\n// the view helpers the flow needs (frontier, terminal, view merge).\nimport type {\n CheckReason,\n CheckType,\n HostedSessionView,\n HostedStepView,\n SessionView,\n} from \"../client/types\";\nimport type { ReasonCode, VerificationResult } from \"./types\";\n\n/**\n * Reasons the backend parks at `input_required` while attempts remain\n * (identity-backend/src/checks/check-reasons.ts isRetryable tables — the single\n * source of truth; identity-hosted/lib/reason-copy.ts mirrors the same rows).\n */\nexport const RETRYABLE_REASONS: ReadonlySet<CheckReason> = new Set<CheckReason>([\n \"no_face\",\n \"media_unreadable\",\n \"clip_too_short\",\n \"no_face_source\",\n \"side_missing\",\n \"unreadable\",\n \"registry_unavailable\",\n \"otp_mismatch\",\n \"otp_expired\",\n \"otp_send_failed\",\n]);\n\n/** The two checks one biometric capture feeds. */\nconst BIOMETRIC_CHECKS: ReadonlySet<CheckType> = new Set<CheckType>([\"liveness\", \"face_match\"]);\n\n/** A check the browser may still submit. */\nexport function isExecutable(step: HostedStepView | undefined): boolean {\n return step !== undefined && (step.state === \"pending\" || step.state === \"input_required\");\n}\n\n/** The session accepts no further submissions. */\nexport function isTerminalView(view: Pick<HostedSessionView, \"status\">): boolean {\n return view.status !== \"created\" && view.status !== \"in_progress\";\n}\n\n/** First step in `steps[]` order with state `pending | input_required` (the backend's frontier). */\nexport function frontierStep(view: HostedSessionView): HostedStepView | undefined {\n return view.steps.find((step) => isExecutable(step));\n}\n\nexport function stepOf(view: HostedSessionView, checkType: CheckType): HostedStepView | undefined {\n return view.steps.find((step) => step.checkType === checkType);\n}\n\n/**\n * Fold a submit response (partner `SessionView`: no attemptsRemaining) into the\n * hosted view: status/outcome/step states+reasons come from the response; the\n * submitted check's `attemptsRemaining` is decremented locally (every accepted\n * submit consumes exactly one attempt server-side — `max(0, cap - attempts)`).\n * This keeps the happy path free of extra `GET /hosted/session` round-trips.\n */\nexport function mergeSessionView(\n prev: HostedSessionView,\n next: SessionView,\n submitted: CheckType,\n): HostedSessionView {\n const byType = new Map(prev.steps.map((step) => [step.checkType, step] as const));\n const steps: HostedStepView[] = next.steps.map((step) => {\n const before = byType.get(step.checkType);\n const attemptsBefore = before?.attemptsRemaining ?? 0;\n const merged: HostedStepView = {\n ...(before ?? {}),\n checkType: step.checkType,\n state: step.state,\n attemptsRemaining:\n step.checkType === submitted ? Math.max(0, attemptsBefore - 1) : attemptsBefore,\n };\n if (step.reason !== undefined) merged.reason = step.reason;\n else delete merged.reason;\n return merged;\n });\n return { ...prev, status: next.status, outcome: next.outcome, steps, expiresAt: next.expiresAt };\n}\n\nexport function unavailable(sessionId: string, reason?: ReasonCode): VerificationResult {\n return reason === undefined\n ? { sessionId, status: \"unavailable\" }\n : { sessionId, status: \"unavailable\", reason };\n}\n\nfunction withReason(\n sessionId: string,\n status: VerificationResult[\"status\"],\n reason: CheckReason | undefined,\n): VerificationResult {\n return reason === undefined ? { sessionId, status } : { sessionId, status, reason };\n}\n\n/**\n * Map the session view to the public result:\n * - `completed/approved → approved`, `completed/review` or `review_pending → review`,\n * `failed` or `completed/rejected → rejected` (reason = the deciding step's);\n * - `expired → unavailable` (no reason);\n * - in flight: a biometric step parked at `input_required` with a retryable\n * reason → `retry` when `autoRetry` and attempts remain, else `rejected`\n * (the partner opted out of retries — this capture was refused); a step in\n * `review` with nothing executable left → `review`; otherwise `unavailable`\n * (a non-biometric frontier the SDK cannot drive, or still processing).\n */\nexport function mapSessionResult(\n view: HostedSessionView,\n opts: { autoRetry: boolean },\n): VerificationResult {\n const { sessionId } = view;\n switch (view.status) {\n case \"completed\":\n if (view.outcome === \"approved\") return { sessionId, status: \"approved\" };\n if (view.outcome === \"review\") return withReason(sessionId, \"review\", reasonIn(view, \"review\"));\n return withReason(sessionId, \"rejected\", reasonIn(view, \"failed\"));\n case \"failed\":\n return withReason(sessionId, \"rejected\", reasonIn(view, \"failed\"));\n case \"review_pending\":\n return withReason(sessionId, \"review\", reasonIn(view, \"review\"));\n case \"expired\":\n return unavailable(sessionId);\n case \"created\":\n case \"in_progress\":\n break;\n default: {\n const unhandled: never = view.status;\n throw new Error(`mapSessionResult: unhandled status ${unhandled as string}`);\n }\n }\n\n const parked = view.steps.find(\n (step) =>\n BIOMETRIC_CHECKS.has(step.checkType) &&\n step.state === \"input_required\" &&\n step.reason !== undefined,\n );\n if (parked?.reason !== undefined) {\n const canRetry =\n opts.autoRetry && RETRYABLE_REASONS.has(parked.reason) && parked.attemptsRemaining > 0;\n return {\n sessionId,\n status: canRetry ? \"retry\" : \"rejected\",\n reason: parked.reason,\n attemptsRemaining: parked.attemptsRemaining,\n };\n }\n if (frontierStep(view) === undefined && view.steps.some((step) => step.state === \"review\")) {\n return withReason(sessionId, \"review\", reasonIn(view, \"review\"));\n }\n return unavailable(sessionId);\n}\n\n/** Reason of the first step in the given state (the deciding check). */\nfunction reasonIn(view: HostedSessionView, state: HostedStepView[\"state\"]): CheckReason | undefined {\n return view.steps.find((step) => step.state === state && step.reason !== undefined)?.reason;\n}\n","// The post-capture flow, driven by the CURRENT hosted frontier: when `liveness`\n// is the frontier → upload the clip → attestation → submit `liveness`, then\n// `face_match` from this capture's keyframe; when liveness already settled and\n// `face_match` is the frontier → keyframe + `face_match` only; nothing the SDK\n// can drive → settled (the caller maps the view). A RESYNC 409\n// (check_already_finalized / check_out_of_order / stale frontier) re-GETs the\n// view and RE-ENTERS the same frontier logic (bounded) instead of giving up —\n// a liveness that passed under an earlier run still gets its face_match.\n// Logic ported (not the React) from\n// identity-hosted/lib/use-submit.ts (idempotency-key lifecycle, same-key\n// transport replay, one-shot consent_required recovery) and\n// lib/use-capture-submission.ts + components/steps/liveness-step.tsx (the\n// `buildSubmit` executor closures and the face_match chain).\n//\n// Per-submission caches (promoted media key, attestation) are CLOSURE LOCALS of\n// each executor: a same-key replay re-runs the executor with the caches intact\n// (byte-identical body — a re-presign or a fresh token under the same key would\n// be an idempotency_key_collision); fresh artifacts build fresh closures.\nimport { HostedApiError } from \"../client/errors\";\nimport { type HostedClient, stripMimeParams } from \"../client/hosted-client\";\nimport type {\n AttestationInput,\n CheckType,\n HostedSessionView,\n LivenessCaptureInput,\n SessionView,\n} from \"../client/types\";\nimport { frontierStep, isTerminalView, stepOf } from \"./result\";\nimport type { CapturedArtifacts, SdkFailureReason } from \"./types\";\n\n/** The client surface the flow needs — `HostedClient` or a test fake. */\nexport type HostedClientLike = Pick<\n HostedClient,\n \"getSession\" | \"recordConsent\" | \"presign\" | \"uploadToSpaces\" | \"completeMedia\" | \"submitCheck\"\n>;\n\n/** The presign policy allowlist for `kind: 'clip'` (media.policy.ts mirror). */\nexport const CLIP_MIME_ALLOWLIST: readonly string[] = [\"video/webm\", \"video/mp4\"];\n\n/** The engine's keyframe encode (captureMirroredKeyframe → canvas JPEG). */\nconst KEYFRAME_MIME = \"image/jpeg\";\n\n/** Same-key transport replays AFTER the first try, and the pause between them. */\nexport const TRANSPORT_REPLAY_ATTEMPTS = 2;\nexport const TRANSPORT_REPLAY_DELAY_MS = 1_000;\n/** Cap on honouring a 429's Retry-After before giving up on this capture. */\nconst MAX_RETRY_AFTER_MS = 10_000;\n/**\n * Bound on frontier re-entries after a RESYNC code per capture (each re-entry\n * re-GETs the view; each executable check is submitted at most once per capture).\n */\nexport const RESYNC_REENTRY_MAX = 3;\n\n/** No (usable) response arrived — a same-key replay can succeed. */\nconst TRANSPORT_CODES: ReadonlySet<string> = new Set([\n \"network_error\",\n \"timeout\",\n \"upload_failed\",\n \"unknown_error\",\n \"rate_limit_exceeded\",\n]);\n\n/** The server moved past what we hold — re-GET and map from truth. */\nconst RESYNC_CODES: ReadonlySet<string> = new Set([\n \"session_not_active\",\n \"check_not_found\",\n \"check_already_finalized\",\n \"check_out_of_order\",\n]);\n\n/** A Turnstile challenge failed client-side (load / render / error-callback / timeout). */\nexport class AttestationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AttestationError\";\n }\n}\n\n/** One logical submission: receives the client and ITS idempotency key. */\nexport type SubmitExecutor = (\n client: HostedClientLike,\n idempotencyKey: string,\n) => Promise<SessionView>;\n\nexport interface SubmitRuntime {\n client: HostedClientLike;\n /** Force-record consent (the server just said it is missing). */\n ensureConsent(opts: { force: true }): Promise<void>;\n sleep(ms: number): Promise<void>;\n uuid(): string;\n}\n\n/**\n * Run one logical submission (ported use-submit semantics):\n * - a fresh `crypto.randomUUID()` key per logical submission;\n * - a transport-class failure replays the SAME executor under the SAME key\n * (bounded) — the backend's IdempotencyInterceptor returns the first\n * response instead of burning another attempt;\n * - ONE `consent_required` recovery: force-record consent and re-run under a\n * FRESH key (the 409 envelope is idempotency-cached under the old one);\n * - ONE attestation retry (client-side Turnstile failure or a server\n * `attestation_invalid`): `onAttestationInvalid` drops the cached token and\n * the executor re-runs under a fresh key.\n * Throws the final error otherwise.\n */\nexport async function runSubmission(\n rt: SubmitRuntime,\n executor: SubmitExecutor,\n hooks: { onAttestationInvalid?: () => void } = {},\n): Promise<SessionView> {\n let key = rt.uuid();\n let consentRecovered = false;\n let attestationRetried = false;\n let transportReplays = 0;\n for (;;) {\n try {\n return await executor(rt.client, key);\n } catch (err) {\n if (err instanceof AttestationError) {\n if (attestationRetried) throw err;\n attestationRetried = true;\n hooks.onAttestationInvalid?.();\n key = rt.uuid();\n continue;\n }\n if (!(err instanceof HostedApiError)) throw err;\n if (err.code === \"consent_required\" && !consentRecovered) {\n consentRecovered = true;\n await rt.ensureConsent({ force: true });\n key = rt.uuid();\n continue;\n }\n if (err.code === \"attestation_invalid\" && !attestationRetried) {\n attestationRetried = true;\n hooks.onAttestationInvalid?.();\n key = rt.uuid();\n continue;\n }\n if (TRANSPORT_CODES.has(err.code) && transportReplays < TRANSPORT_REPLAY_ATTEMPTS) {\n transportReplays += 1;\n const retryAfterMs =\n err.retryAfterSeconds !== undefined ? err.retryAfterSeconds * 1000 : undefined;\n await rt.sleep(\n retryAfterMs !== undefined\n ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS)\n : TRANSPORT_REPLAY_DELAY_MS,\n );\n continue;\n }\n throw err;\n }\n }\n}\n\nexport interface LivenessFlowDeps extends SubmitRuntime {\n artifacts: CapturedArtifacts;\n /** Fresh attestation per call (Turnstile execute, or `{sandbox: true}`). */\n getAttestation(): Promise<AttestationInput>;\n /** Latest hosted view (frontier decisions). */\n getView(): HostedSessionView;\n /** Re-GET the view from the server (after a RESYNC code) and fold it in. */\n refreshView(): Promise<HostedSessionView>;\n /** Fold a submit response into the view. */\n applySubmit(view: SessionView, submitted: CheckType): void;\n /** Flow-state hook for the capture handle. */\n onPhase(phase: \"uploading\" | \"checking\"): void;\n /** The verification was destroyed (the only thing that cancels a chain). */\n isCancelled(): boolean;\n}\n\nexport type LivenessFlowOutcome =\n | { kind: \"settled\" }\n | { kind: \"resync\" }\n | { kind: \"unavailable\"; reason: SdkFailureReason }\n | { kind: \"cancelled\" };\n\n/** Liveness states after which THIS capture's keyframe may feed face_match. */\nconst LIVENESS_CHAINABLE: ReadonlySet<string> = new Set([\"passed\", \"review\", \"processing\", \"skipped\"]);\n\n/**\n * Run the biometric submissions the CURRENT frontier allows, re-evaluating\n * after every submit and after every RESYNC re-GET:\n * - frontier `liveness` → clip upload + attestation + `liveness` (once per capture);\n * - frontier `face_match` → keyframe upload + `face_match` (once per capture),\n * allowed when liveness was NOT part of this capture (it had already\n * settled — any state, incl. `failed`: the selfie-fallback case, where this\n * capture only feeds face_match) or settled chainable from this capture\n * (passed / review / processing / skipped — or absent);\n * - anything else (terminal view, a non-biometric frontier, a check this\n * capture already submitted) → `settled` for the caller to map the view.\n * A RESYNC code re-GETs the view and re-enters (at most {@link RESYNC_REENTRY_MAX}\n * times); the final RESYNC surfaces as `resync` for the caller to map.\n */\nexport async function runLivenessFlow(deps: LivenessFlowDeps): Promise<LivenessFlowOutcome> {\n const { artifacts } = deps;\n const clip = artifacts.clip;\n const bareMime = stripMimeParams(artifacts.clipMime ?? \"\");\n if (\n artifacts.degraded === \"NO_RECORDER\" ||\n clip === undefined ||\n !CLIP_MIME_ALLOWLIST.includes(bareMime)\n ) {\n // No clip, or a container outside the presign allowlist (never sign an\n // off-allowlist type): v2 cannot run THIS capture.\n return { kind: \"unavailable\", reason: \"no_recorder\" };\n }\n\n // Per-capture executors: their closure caches (promoted media key,\n // attestation) survive a same-key transport replay; each is run at most once.\n const submitLiveness = buildLivenessSubmit(deps, clip, bareMime);\n const submitFaceMatch = buildFaceMatchSubmit(deps, artifacts.keyframe);\n let livenessSubmitted = false;\n let faceMatchSubmitted = false;\n let livenessChainable = true;\n let resyncs = 0;\n\n for (;;) {\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n const view = deps.getView();\n if (isTerminalView(view)) return { kind: \"settled\" };\n const frontier = frontierStep(view)?.checkType;\n const next =\n frontier === \"liveness\" && !livenessSubmitted\n ? \"liveness\"\n : frontier === \"face_match\" && !faceMatchSubmitted && livenessChainable\n ? \"face_match\"\n : null;\n if (next === null) return { kind: \"settled\" };\n try {\n if (next === \"liveness\") {\n livenessSubmitted = true;\n const response = await submitLiveness();\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n deps.applySubmit(response, \"liveness\");\n const state = stepOf(deps.getView(), \"liveness\")?.state;\n livenessChainable = state === undefined || LIVENESS_CHAINABLE.has(state);\n } else {\n faceMatchSubmitted = true;\n const response = await submitFaceMatch();\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n deps.applySubmit(response, \"face_match\");\n }\n } catch (err) {\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n const outcome = classifyFailure(err);\n if (outcome.kind !== \"resync\" || resyncs >= RESYNC_REENTRY_MAX) return outcome;\n resyncs += 1;\n // The server moved past what we hold: re-GET and re-enter from truth.\n await deps.refreshView();\n }\n }\n}\n\nfunction buildLivenessSubmit(\n deps: LivenessFlowDeps,\n clip: Blob,\n bareMime: string,\n): () => Promise<SessionView> {\n let clipKey: string | null = null;\n let attestation: AttestationInput | null = null;\n return () =>\n runSubmission(\n deps,\n async (client, idempotencyKey) => {\n if (clipKey === null) {\n deps.onPhase(\"uploading\");\n const presigned = await client.presign(\"clip\", bareMime, clip.size);\n await client.uploadToSpaces(presigned.uploadUrl, clip, bareMime);\n clipKey = (await client.completeMedia(presigned.key)).key;\n }\n // Attestation AFTER the upload settles (token stays fresh), cached\n // for the same-key replay.\n if (attestation === null) {\n try {\n attestation = await deps.getAttestation();\n } catch (err) {\n throw new AttestationError(err instanceof Error ? err.message : String(err));\n }\n }\n deps.onPhase(\"checking\");\n return client.submitCheck(\n \"liveness\",\n {\n clipKey,\n attestation,\n captureChannel: deps.artifacts.captureChannel,\n capture: captureInputOf(deps.artifacts),\n },\n { idempotencyKey },\n );\n },\n {\n onAttestationInvalid: () => {\n attestation = null;\n },\n },\n );\n}\n\n/**\n * The `input.capture` block of the liveness submission: the engine id, the\n * stream dimensions, the clip length and the face-capture timing from the\n * `captured` event — exactly the backend's capture-metadata allowlist\n * (identity-backend `capture-metadata.ts`). Never media, never tokens.\n */\nexport function captureInputOf(artifacts: CapturedArtifacts): LivenessCaptureInput {\n return {\n ...(artifacts.engine !== undefined ? { engine: artifacts.engine } : {}),\n width: artifacts.width,\n height: artifacts.height,\n ...(artifacts.clipMs !== undefined ? { clipMs: artifacts.clipMs } : {}),\n ...(artifacts.cameraReadyMs !== undefined ? { cameraReadyMs: artifacts.cameraReadyMs } : {}),\n ...(artifacts.faceAcquireMs !== undefined ? { faceAcquireMs: artifacts.faceAcquireMs } : {}),\n ...(artifacts.captureMs !== undefined ? { captureMs: artifacts.captureMs } : {}),\n };\n}\n\nfunction buildFaceMatchSubmit(deps: LivenessFlowDeps, keyframe: Blob): () => Promise<SessionView> {\n let imageKey: string | null = null;\n return () =>\n runSubmission(deps, async (client, idempotencyKey) => {\n if (imageKey === null) {\n deps.onPhase(\"uploading\");\n const presigned = await client.presign(\"image\", KEYFRAME_MIME, keyframe.size);\n await client.uploadToSpaces(presigned.uploadUrl, keyframe, KEYFRAME_MIME);\n imageKey = (await client.completeMedia(presigned.key)).key;\n }\n deps.onPhase(\"checking\");\n return client.submitCheck(\"face_match\", { imageKey }, { idempotencyKey });\n });\n}\n\nfunction classifyFailure(err: unknown): LivenessFlowOutcome {\n if (err instanceof AttestationError) return { kind: \"unavailable\", reason: \"attestation\" };\n if (err instanceof HostedApiError) {\n if (err.code === \"attestation_invalid\") return { kind: \"unavailable\", reason: \"attestation\" };\n if (RESYNC_CODES.has(err.code)) return { kind: \"resync\" };\n }\n return { kind: \"unavailable\", reason: \"network\" };\n}\n","// Cloudflare Turnstile: lazy script loader + execute-mode invisible challenge.\n// Ported from identity-hosted/lib/turnstile.ts (Plan 6 Task 10 AC4) — same\n// behaviour: the script loads ONCE, lazily, only when a liveness submit first\n// needs a token; each token request renders a fresh execute-mode widget,\n// resolves the callback token, and removes the widget. Hosts' CSP must allow\n// challenges.cloudflare.com.\n//\n// The site key is RUNTIME data (`HostedSessionView.turnstileSiteKey`); when it\n// is null the verification sends `{sandbox: true}` instead of calling this.\n\nexport const TURNSTILE_SCRIPT_SRC = \"https://challenges.cloudflare.com/turnstile/v0/api.js\";\n\n/** Bound on a single execute round-trip (challenge fetch + solve). */\nexport const TURNSTILE_EXECUTE_TIMEOUT_MS = 30_000;\n\nexport interface TurnstileRenderParams {\n sitekey: string;\n callback: (token: string) => void;\n \"error-callback\"?: (code?: string) => void;\n execution?: \"render\" | \"execute\";\n appearance?: \"always\" | \"execute\" | \"interaction-only\";\n}\n\n/** The subset of the Turnstile JS API this module uses. */\nexport interface TurnstileApi {\n render(container: HTMLElement, params: TurnstileRenderParams): string | undefined;\n execute(widgetId: string): void;\n remove(widgetId: string): void;\n}\n\n/** `window.turnstile` without augmenting the global Window type for consumers. */\nfunction installedApi(): TurnstileApi | undefined {\n return (window as unknown as { turnstile?: TurnstileApi }).turnstile;\n}\n\nlet loaderPromise: Promise<TurnstileApi> | null = null;\n\n/** Test-only: reset the module singleton between cases. */\nexport function resetTurnstileForTests(): void {\n loaderPromise = null;\n}\n\n/** Load the Turnstile script once (singleton; a load failure resets it so a\n * retry can re-attempt). */\nexport function loadTurnstile(): Promise<TurnstileApi> {\n loaderPromise ??= new Promise<TurnstileApi>((resolve, reject) => {\n const present = installedApi();\n if (present) {\n resolve(present);\n return;\n }\n const script = document.createElement(\"script\");\n script.src = TURNSTILE_SCRIPT_SRC;\n script.async = true;\n script.onload = () => {\n const api = installedApi();\n if (api) {\n resolve(api);\n } else {\n loaderPromise = null;\n reject(new Error(\"Turnstile script loaded but exposed no API\"));\n }\n };\n script.onerror = () => {\n loaderPromise = null;\n script.remove(); // a retry appends a fresh tag\n reject(new Error(\"Turnstile script failed to load\"));\n };\n document.head.appendChild(script);\n });\n return loaderPromise;\n}\n\n/** Options of {@link executeTurnstile}. */\nexport interface ExecuteTurnstileOptions {\n /** Bound on the challenge round-trip; default {@link TURNSTILE_EXECUTE_TIMEOUT_MS}. */\n timeoutMs?: number;\n /**\n * Aborts the challenge: the promise rejects and the widget, its container and\n * the timeout are cleaned up at once. `createVerification().destroy()` passes\n * its own signal so a torn-down verification never leaves a widget behind.\n */\n signal?: AbortSignal;\n}\n\nfunction abortError(): Error {\n return new Error(\"Turnstile challenge aborted\");\n}\n\n/**\n * Run one invisible execute-mode challenge and resolve its token. Rejects on\n * challenge error, on `signal` abort, or after {@link TURNSTILE_EXECUTE_TIMEOUT_MS}\n * — the verification maps a failure to `result {status:'unavailable',\n * reason:'attestation'}` after one retry.\n */\nexport async function executeTurnstile(\n siteKey: string,\n opts: ExecuteTurnstileOptions = {},\n): Promise<string> {\n const timeoutMs = opts.timeoutMs ?? TURNSTILE_EXECUTE_TIMEOUT_MS;\n const signal = opts.signal;\n if (signal?.aborted) throw abortError();\n const api = await loadTurnstile();\n if (signal?.aborted) throw abortError();\n const container = document.createElement(\"div\");\n document.body.appendChild(container);\n let widgetId: string | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n try {\n return await new Promise<string>((resolve, reject) => {\n timer = setTimeout(\n () => reject(new Error(`Turnstile challenge timed out (${timeoutMs} ms)`)),\n timeoutMs,\n );\n if (signal !== undefined) {\n onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n widgetId = api.render(container, {\n sitekey: siteKey,\n execution: \"execute\",\n appearance: \"execute\",\n callback: resolve,\n \"error-callback\": (code) => {\n reject(new Error(`Turnstile challenge failed${code ? ` (${code})` : \"\"}`));\n },\n });\n if (widgetId === undefined) {\n reject(new Error(\"Turnstile render returned no widget id\"));\n return;\n }\n api.execute(widgetId);\n });\n } finally {\n // ONE cleanup path for every outcome (token / challenge error / timeout /\n // abort / render failure): the challenge timer and the abort listener must\n // never leak past settlement.\n clearTimeout(timer);\n if (signal !== undefined && onAbort !== undefined) signal.removeEventListener(\"abort\", onAbort);\n if (widgetId !== undefined) {\n try {\n api.remove(widgetId);\n } catch {\n // widget already gone — nothing to clean\n }\n }\n container.remove();\n }\n}\n","// createVerification — composes the hosted client, consent, the early registry\n// submit, the capture handle and the post-capture flow behind the public\n// contract (spec §3.1). Framework-free; the only state is the latest hosted\n// view (re-emitted on every update via `on('view')`).\nimport { HostedClient } from \"../client/hosted-client\";\nimport type { AttestationInput, CheckType, HostedSessionView, SessionView } from \"../client/types\";\nimport {\n createLivenessCaptureInternal,\n type LivenessCaptureInternalHandle,\n type LivenessCaptureInternals,\n} from \"../capture/create-liveness-capture\";\nimport { createEmitter } from \"./emitter\";\nimport {\n type HostedClientLike,\n runLivenessFlow,\n runSubmission,\n type SubmitRuntime,\n} from \"./liveness-flow\";\nimport {\n frontierStep,\n isTerminalView,\n mapSessionResult,\n mergeSessionView,\n stepOf,\n unavailable,\n} from \"./result\";\nimport { executeTurnstile } from \"./turnstile\";\nimport type {\n CapturedArtifacts,\n LivenessCaptureHandle,\n LivenessCaptureOptions,\n Verification,\n VerificationEvents,\n VerificationOptions,\n VerificationResult,\n} from \"./types\";\n\n/** Production API origin — the `apiOrigin` default. */\nexport const DEFAULT_API_ORIGIN = \"https://api.sekyro.ai\";\n\n/** Pause before the single `registry_unavailable` re-submit. */\nexport const REGISTRY_RETRY_DELAY_MS = 5_000;\n\n/** Bounded poll while a biometric verdict is still `processing` after the submits. */\nexport const PROCESSING_POLL_DELAY_MS = 1_500;\nexport const PROCESSING_POLL_MAX = 10;\n\n/**\n * Construction seams — tests and Sekyro's own hosted page (`@sekyro/web/internal`).\n * Not part of the partner contract.\n */\nexport interface VerificationInternals {\n /** Replace the HTTP client (default: `new HostedClient({...})`). */\n client?: HostedClientLike;\n /**\n * Replace the Turnstile challenge (default: {@link executeTurnstile}). The\n * `signal` aborts when the verification is destroyed (M1).\n */\n getTurnstileToken?: (siteKey: string, opts: { signal: AbortSignal }) => Promise<string>;\n /** Capture-engine seams forwarded to {@link createLivenessCaptureInternal}. */\n capture?: LivenessCaptureInternals;\n sleep?: (ms: number) => Promise<void>;\n uuid?: () => string;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** `crypto.randomUUID()` with a v4 fallback for hosts that lack it. */\nexport function randomUuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c !== undefined && typeof c.randomUUID === \"function\") return c.randomUUID();\n const bytes = new Uint8Array(16);\n if (c !== undefined && typeof c.getRandomValues === \"function\") c.getRandomValues(bytes);\n else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Create a verification bound to one session (spec §3.1):\n * `consent()` → `startRegistry()` → `liveness({video})` → `on('result')`.\n */\nexport function createVerification(options: VerificationOptions): Verification {\n return createVerificationInternal(options, {});\n}\n\n/** {@link createVerification} with construction seams (tests / Sekyro-internal hosts). */\nexport function createVerificationInternal(\n options: VerificationOptions,\n internals: VerificationInternals,\n): Verification {\n const apiOrigin = options.apiOrigin ?? DEFAULT_API_ORIGIN;\n const autoRetry = options.autoRetry ?? true;\n const rearm = options.rearm ?? \"auto\";\n const client: HostedClientLike =\n internals.client ??\n new HostedClient({\n apiOrigin,\n sessionToken: options.clientToken,\n ...(options.fetch !== undefined ? { fetch: options.fetch } : {}),\n });\n const sleep = internals.sleep ?? defaultSleep;\n const uuid = internals.uuid ?? randomUuid;\n const getTurnstileToken = internals.getTurnstileToken ?? executeTurnstile;\n const emitter = createEmitter<VerificationEvents>();\n\n let destroyed = false;\n // Aborts an in-flight Turnstile challenge on destroy() (widget + timer clean up).\n const lifetime = new AbortController();\n\n // ── view ───────────────────────────────────────────────────────────────────\n let view: HostedSessionView | null = null;\n let bootstrap: Promise<HostedSessionView> | null = null;\n const setView = (next: HostedSessionView): void => {\n view = next;\n emitter.emit(\"view\", next);\n };\n const ensureView = (): Promise<HostedSessionView> => {\n if (view !== null) return Promise.resolve(view);\n bootstrap ??= client\n .getSession()\n .then((next) => {\n setView(next);\n return next;\n })\n .finally(() => {\n bootstrap = null;\n });\n return bootstrap;\n };\n const refreshView = async (): Promise<HostedSessionView> => {\n const next = await client.getSession();\n setView(next);\n return next;\n };\n const applySubmit = (response: SessionView, submitted: CheckType): void => {\n if (view !== null) setView(mergeSessionView(view, response, submitted));\n };\n\n // ── consent (idempotent; `force` after a server consent_required) ──────────\n let consentInFlight: Promise<void> | null = null;\n const recordConsent = async (force: boolean): Promise<void> => {\n const current = await ensureView();\n if (!force && current.consentRecorded) return;\n await client.recordConsent(options.locale);\n if (view !== null) setView({ ...view, consentRecorded: true });\n };\n const ensureConsent = (opts?: { force?: boolean }): Promise<void> => {\n if (opts?.force === true) return recordConsent(true);\n consentInFlight ??= recordConsent(false).finally(() => {\n consentInFlight = null;\n });\n return consentInFlight;\n };\n\n const runtime: SubmitRuntime = {\n client,\n ensureConsent: (opts) => ensureConsent(opts),\n sleep,\n uuid,\n };\n\n // ── result emission ────────────────────────────────────────────────────────\n // A session verdict is emitted ONCE (the registry may settle it before any\n // capture); `retry` / `unavailable` are per capture.\n let terminalEmitted = false;\n const emitResult = (result: VerificationResult): void => {\n if (result.status === \"approved\" || result.status === \"rejected\" || result.status === \"review\") {\n if (terminalEmitted) return;\n terminalEmitted = true;\n }\n emitter.emit(\"result\", result);\n };\n\n // ── early registry ─────────────────────────────────────────────────────────\n let registry: Promise<void> | null = null;\n let registryFailed = false;\n // The frontier is population_registry on a session minted WITHOUT a bound\n // NNI: the SDK has nothing to submit (and must never guess one).\n let registryUnbound = false;\n const submitRegistry = (): Promise<SessionView> =>\n runSubmission(runtime, (c, idempotencyKey) =>\n // `{}` input: the session is bound to a SERVER-provided NNI (Plan A Task 2).\n c.submitCheck(\"population_registry\", {}, { idempotencyKey }),\n );\n const runRegistry = async (): Promise<void> => {\n try {\n const current = await ensureView();\n if (isTerminalView(current) || frontierStep(current)?.checkType !== \"population_registry\") {\n return;\n }\n if (current.nniBound !== true) {\n registryUnbound = true;\n return;\n }\n await ensureConsent();\n applySubmit(await submitRegistry(), \"population_registry\");\n const step = view === null ? undefined : stepOf(view, \"population_registry\");\n if (\n step?.state === \"input_required\" &&\n step.reason === \"registry_unavailable\" &&\n step.attemptsRemaining > 0\n ) {\n await sleep(REGISTRY_RETRY_DELAY_MS);\n if (destroyed) return;\n applySubmit(await submitRegistry(), \"population_registry\");\n }\n } catch {\n // Never throws (incl. 409 nni_locked / transport failures): the flow\n // surfaces it through result mapping.\n registryFailed = true;\n return;\n }\n if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));\n };\n const startRegistry = (): Promise<void> => {\n registry ??= runRegistry();\n return registry;\n };\n\n // ── attestation ────────────────────────────────────────────────────────────\n // The SDK echoes the site key it rendered (`siteKey`) so the backend selects\n // the matching widget secret deterministically — Turnstile tokens are\n // single-use, so the verifier never retries with another secret (C1).\n const getAttestation = async (): Promise<AttestationInput> => {\n const current = await ensureView();\n const siteKey = current.turnstileSiteKey;\n if (siteKey === null) return { sandbox: true };\n return {\n channel: \"hosted_web\",\n siteKey,\n turnstileToken: await getTurnstileToken(siteKey, { signal: lifetime.signal }),\n };\n };\n\n // ── capture + flow ─────────────────────────────────────────────────────────\n // Submission chains are SERIALIZED: a new capture (incl. a host remount that\n // destroyed the old handle mid-upload) never abandons an in-flight chain — it\n // queues behind it and then evaluates the CURRENT frontier. Only destroy() of\n // the verification cancels. Handle side effects (flow states, auto re-arm)\n // reach the handle only while it is alive and still on the attempt that\n // produced the artifacts.\n interface BoundCapture {\n capture: LivenessCaptureInternalHandle;\n alive: boolean;\n /** Engine attempt counter (bumped on every `starting`). */\n attempt: number;\n }\n let active: BoundCapture | null = null;\n let chain: Promise<void> = Promise.resolve();\n let chainDepth = 0;\n\n const settleProcessing = async (): Promise<HostedSessionView> => {\n let current = await ensureView();\n for (\n let i = 0;\n i < PROCESSING_POLL_MAX &&\n !destroyed &&\n !isTerminalView(current) &&\n frontierStep(current) === undefined &&\n current.steps.some((step) => step.state === \"processing\");\n i += 1\n ) {\n await sleep(PROCESSING_POLL_DELAY_MS);\n if (destroyed) break;\n current = await refreshView();\n }\n return current;\n };\n\n const runFlow = async (\n bound: BoundCapture,\n artifacts: CapturedArtifacts,\n capturedAttempt: number,\n ): Promise<void> => {\n const { capture } = bound;\n const isCancelled = (): boolean => destroyed;\n // The handle still shows the attempt these artifacts came from (not\n // destroyed by a remount, no retry()/switchCamera() since).\n const ownsHandle = (): boolean => bound.alive && bound.attempt === capturedAttempt;\n const setPhase = (phase: \"uploading\" | \"checking\" | \"done\"): void => {\n if (ownsHandle()) capture.setFlowState(phase);\n };\n const finish = (result: VerificationResult): void => {\n if (isCancelled()) return;\n emitResult(result);\n if (!ownsHandle()) return;\n if (result.status === \"retry\" && rearm === \"auto\") {\n // Re-arm: the engine restarts and the next `captured` runs a new flow.\n capture.handle.retry();\n return;\n }\n // `done` (incl. a manual-rearm `retry`): the host calls `retry()` on the\n // handle when the user is ready — the engine restarts from there.\n capture.setFlowState(\"done\");\n };\n\n if (artifacts.degraded === \"NO_RECORDER\") {\n // No MediaRecorder: nothing is submitted (no liveness, no face_match).\n const sessionId = view?.sessionId ?? (await ensureView().catch(() => null))?.sessionId ?? \"\";\n finish(unavailable(sessionId, \"no_recorder\"));\n return;\n }\n\n setPhase(\"uploading\");\n try {\n // Registry first: it records consent itself only when it actually\n // submits — an unbound / failed registry frontier ends the flow below\n // without recording a consent nothing was submitted under.\n await startRegistry();\n if (isCancelled()) return;\n let current = await ensureView();\n if (!isTerminalView(current)) {\n if (frontierStep(current)?.checkType === \"population_registry\") {\n // The registry never passed (outage after the retry, or a request-\n // level failure): the biometric checks cannot be submitted.\n finish(\n unavailable(\n current.sessionId,\n registryUnbound ? \"nni_unbound\" : registryFailed ? \"network\" : \"registry_unavailable\",\n ),\n );\n return;\n }\n await ensureConsent();\n if (isCancelled()) return;\n const outcome = await runLivenessFlow({\n ...runtime,\n artifacts,\n getAttestation,\n getView: () => view ?? current,\n refreshView,\n applySubmit,\n onPhase: setPhase,\n isCancelled,\n });\n if (isCancelled()) return;\n if (outcome.kind === \"unavailable\") {\n finish(unavailable(current.sessionId, outcome.reason));\n return;\n }\n if (outcome.kind === \"resync\") await refreshView();\n current = await settleProcessing();\n }\n finish(mapSessionResult(current, { autoRetry }));\n } catch {\n if (isCancelled()) return;\n finish(unavailable(view?.sessionId ?? \"\", \"network\"));\n }\n };\n\n const enqueueFlow = (bound: BoundCapture, artifacts: CapturedArtifacts): void => {\n const capturedAttempt = bound.attempt;\n if (chainDepth > 0 && bound.alive) {\n // Queued behind an in-flight chain: the host sees `uploading` meanwhile.\n bound.capture.setFlowState(\"uploading\");\n }\n chainDepth += 1;\n chain = chain\n .then(() => runFlow(bound, artifacts, capturedAttempt))\n .finally(() => {\n chainDepth -= 1;\n })\n // runFlow never rejects (it maps failures to results); a rejection must\n // still never poison the chain for the captures queued after it.\n .catch(() => undefined);\n };\n\n const liveness = (captureOptions: LivenessCaptureOptions): LivenessCaptureHandle => {\n // Camera teardown of the previous handle (its chain, if any, survives).\n active?.capture.handle.destroy();\n if (active !== null) active.alive = false;\n const capture = createLivenessCaptureInternal(captureOptions, internals.capture);\n const bound: BoundCapture = { capture, alive: true, attempt: 0 };\n active = bound;\n capture.handle.on(\"state\", (state) => {\n // A fresh engine attempt: in-flight flows for older artifacts complete,\n // but no longer push states into (or re-arm) this handle.\n if (state === \"starting\") bound.attempt += 1;\n });\n capture.handle.on(\"captured\", (artifacts) => {\n // Partners' own `captured` handlers run first (the event fires BEFORE\n // any upload); the flow starts on the next microtask, after any\n // in-flight chain.\n void Promise.resolve().then(() => enqueueFlow(bound, artifacts));\n });\n const inner = capture.handle;\n return {\n get state() {\n return inner.state;\n },\n on: (event, handler) => inner.on(event, handler),\n start: (startOptions) => inner.start(startOptions),\n retry: () => inner.retry(),\n switchCamera: () => inner.switchCamera(),\n destroy: () => {\n bound.alive = false;\n if (active === bound) active = null;\n inner.destroy();\n },\n };\n };\n\n return {\n consent: () => ensureConsent(),\n startRegistry,\n liveness,\n on: (event, handler) => emitter.on(event, handler),\n destroy: () => {\n destroyed = true;\n lifetime.abort();\n if (active !== null) {\n active.alive = false;\n active.capture.handle.destroy();\n active = null;\n }\n emitter.clear();\n },\n };\n}\n"],"mappings":"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","names":["registry"]}
|
|
1
|
+
{"version":3,"sources":["../src/capture/preload.ts","../src/verification/emitter.ts","../src/capture/wake-lock.ts","../src/capture/create-liveness-capture.ts","../src/verification/result.ts","../src/verification/liveness-flow.ts","../src/verification/turnstile.ts","../src/verification/create-verification.ts"],"sourcesContent":["// Detector preload (spec D3–D5) — the public generalization of identity-hosted's\n// lib/warm-models.ts. Warms the v3 face detector (blaze tier, generous init\n// deadline: nobody is racing this) so a capture mounts with a HOT instance and\n// the hosts' Start control can wait on a status instead of racing the engine's\n// 10 s cold-init deadline. One entry per normalized asset base URL, stored on a\n// globalThis Symbol.for key so two bundled copies of @sekyro/web share it.\n// createLivenessCapture consumes it through preloadedDetectorFactory (a\n// NON-OWNING view: CaptureSessionV3.destroy() can never tear the shared\n// instance down — it survives remounts and retries).\nimport {\n createDetector,\n DEFAULT_V3_ASSET_BASE_URL,\n type Detector,\n} from \"../core/v3/detector\";\n\n/** Preload state per asset base URL. */\nexport type LivenessAssetsStatus = \"idle\" | \"loading\" | \"ready\" | \"failed\";\n\n/** Options of {@link preloadLivenessAssets}. */\nexport interface PreloadLivenessAssetsOptions {\n /** Same value as the capture's `assetBaseUrl`; default `/models-v3`. */\n assetBaseUrl?: string;\n /** Defer the load to browser idle time (`requestIdleCallback`, 1.2 s timeout fallback). Default `false`. */\n idle?: boolean;\n /** Skip when the browser asks to save data (`navigator.connection.saveData`). Default `false`. */\n respectSaveData?: boolean;\n}\n\n/** Blaze init deadline for the preload — generous: a slow ~11 MB wasm download must finish. */\nexport const PRELOAD_BLAZE_INIT_TIMEOUT_MS = 120_000;\n/** Outer budget reclaiming only a genuinely hung load (≥ the init deadline + slack). */\nexport const PRELOAD_BUDGET_MS = 130_000;\nconst IDLE_TIMEOUT_MS = 3_000;\nconst IDLE_FALLBACK_MS = 1_200;\n\n/** An idle-deferred load not yet started (status stays 'idle' meanwhile). */\ninterface ScheduledLoad {\n readonly promise: Promise<LivenessAssetsStatus>;\n /** Drop the pending idle callback and settle `promise` with `outcome`. */\n readonly settle: (outcome: LivenessAssetsStatus | Promise<LivenessAssetsStatus>) => void;\n}\n\ninterface PreloadEntry {\n status: LivenessAssetsStatus;\n promise: Promise<LivenessAssetsStatus> | null;\n scheduled: ScheduledLoad | null;\n detectorPromise: Promise<Detector> | null;\n detector: Detector | null;\n readonly listeners: Set<(status: LivenessAssetsStatus) => void>;\n}\n\nconst REGISTRY_KEY = Symbol.for(\"@sekyro/web.preload.v1\");\ntype RegistryHost = { [REGISTRY_KEY]?: Map<string, PreloadEntry> };\n\nfunction registry(): Map<string, PreloadEntry> {\n const host = globalThis as unknown as RegistryHost;\n host[REGISTRY_KEY] ??= new Map();\n return host[REGISTRY_KEY];\n}\n\nfunction normalizeBase(assetBaseUrl: string | undefined): string {\n const base = (assetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL).replace(/\\/+$/, \"\");\n return base === \"\" ? \"/\" : base;\n}\n\nfunction entryFor(base: string): PreloadEntry {\n const map = registry();\n let entry = map.get(base);\n if (entry === undefined) {\n entry = { status: \"idle\", promise: null, scheduled: null, detectorPromise: null, detector: null, listeners: new Set() };\n map.set(base, entry);\n }\n return entry;\n}\n\nfunction setStatus(entry: PreloadEntry, next: LivenessAssetsStatus): void {\n if (entry.status === next) return;\n entry.status = next;\n for (const listener of [...entry.listeners]) listener(next);\n}\n\ntype Loader = (base: string) => Promise<Detector>;\nconst defaultLoader: Loader = (base) =>\n createDetector({ preferred: [\"blaze\"], v3AssetBaseUrl: base, blazeInitTimeoutMs: PRELOAD_BLAZE_INIT_TIMEOUT_MS });\nlet loader: Loader = defaultLoader;\n\n/** @internal Test seam: replace the detector loader (null restores the real one). */\nexport function setPreloadLoaderForTests(next: Loader | null): void {\n loader = next ?? defaultLoader;\n}\n\n/** @internal Test seam: drop every entry (destroying warmed detectors). */\nexport function resetLivenessAssetsForTests(): void {\n for (const entry of registry().values()) entry.detector?.destroy();\n registry().clear();\n loader = defaultLoader;\n}\n\nfunction canCapture(): boolean {\n return (\n typeof window !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof navigator.mediaDevices?.getUserMedia === \"function\"\n );\n}\n\nfunction saveDataOn(): boolean {\n return (navigator as { connection?: { saveData?: boolean } }).connection?.saveData === true;\n}\n\n/** Schedules `run` at idle time; returns the cancel function. */\nfunction scheduleIdle(run: () => void): () => void {\n const w = window as Window & {\n requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n cancelIdleCallback?: (handle: number) => void;\n };\n if (typeof w.requestIdleCallback === \"function\") {\n const handle = w.requestIdleCallback(run, { timeout: IDLE_TIMEOUT_MS });\n return () => w.cancelIdleCallback?.(handle);\n }\n const timer = setTimeout(run, IDLE_FALLBACK_MS);\n return () => clearTimeout(timer);\n}\n\n/**\n * Defer the load to idle time. The schedule is cancellable (release) and\n * promotable (an immediate preload / a capture needing the detector): either\n * settles it, so a late-firing idle callback is a no-op — never a second load\n * and never undoing a release.\n */\nfunction schedule(entry: PreloadEntry, base: string): Promise<LivenessAssetsStatus> {\n let resolve!: (outcome: LivenessAssetsStatus | Promise<LivenessAssetsStatus>) => void;\n const promise = new Promise<LivenessAssetsStatus>((res) => {\n resolve = res;\n });\n let done = false;\n let cancel: () => void = () => undefined;\n const scheduled: ScheduledLoad = {\n promise,\n settle: (outcome) => {\n if (done) return;\n done = true;\n cancel();\n if (entry.scheduled === scheduled) entry.scheduled = null;\n resolve(outcome);\n },\n };\n entry.scheduled = scheduled;\n cancel = scheduleIdle(() => {\n if (done) return;\n scheduled.settle(begin(entry, base));\n });\n return promise;\n}\n\nfunction begin(entry: PreloadEntry, base: string): Promise<LivenessAssetsStatus> {\n if ((entry.status === \"loading\" || entry.status === \"ready\") && entry.promise !== null) return entry.promise;\n setStatus(entry, \"loading\");\n // A synchronously throwing loader becomes a rejection ('failed'), never a throw.\n const detectorPromise = new Promise<Detector>((resolve) => resolve(loader(base)));\n entry.detectorPromise = detectorPromise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const budget = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // A late resolution is an orphan (wasm instance) — release it.\n detectorPromise.then((late) => late.destroy(), () => undefined);\n reject(new Error(`detector preload exceeded ${PRELOAD_BUDGET_MS} ms`));\n }, PRELOAD_BUDGET_MS);\n });\n const settled = Promise.race([detectorPromise, budget]).then(\n (detector): LivenessAssetsStatus => {\n if (entry.detectorPromise !== detectorPromise) {\n detector.destroy(); // released (or superseded) while loading\n return entry.status;\n }\n entry.detector = detector;\n setStatus(entry, \"ready\");\n return \"ready\";\n },\n (err: unknown): LivenessAssetsStatus => {\n if (entry.detectorPromise === detectorPromise) {\n entry.detectorPromise = null;\n entry.detector = null;\n setStatus(entry, \"failed\");\n console.warn(\"[@sekyro/web] face-detector preload failed:\", err);\n }\n return entry.status;\n },\n );\n const promise = settled.finally(() => clearTimeout(timer));\n entry.promise = promise;\n entry.scheduled?.settle(promise); // promote a pending idle schedule into this load\n return promise;\n}\n\n/**\n * Warm the face detector for `assetBaseUrl` so the capture starts instantly.\n * Idempotent per base URL and never rejects: resolves the settled status.\n * `'idle'` means nothing was preloaded here (SSR, no camera API, Data-Saver\n * skip) — start normally. Calling again after `'failed'` retries the load.\n * With `idle: true` the load is deferred (status stays `'idle'` until it\n * starts); a later non-idle call — or a capture mounting meanwhile — starts it\n * at once, and {@link releaseLivenessAssets} cancels it.\n */\nexport function preloadLivenessAssets(options: PreloadLivenessAssetsOptions = {}): Promise<LivenessAssetsStatus> {\n if (!canCapture()) return Promise.resolve(\"idle\");\n const base = normalizeBase(options.assetBaseUrl);\n const entry = entryFor(base);\n if (options.respectSaveData === true && saveDataOn()) return Promise.resolve(entry.status);\n if ((entry.status === \"loading\" || entry.status === \"ready\") && entry.promise !== null) return entry.promise;\n if (options.idle === true) return entry.scheduled?.promise ?? schedule(entry, base);\n return begin(entry, base);\n}\n\n/** Current preload status for `assetBaseUrl` (default `/models-v3`). */\nexport function getLivenessAssetsStatus(assetBaseUrl?: string): LivenessAssetsStatus {\n return registry().get(normalizeBase(assetBaseUrl))?.status ?? \"idle\";\n}\n\n/**\n * Subscribe to status changes for `assetBaseUrl`; returns the unsubscribe\n * function. Does NOT emit the current status on subscribe (read it with\n * {@link getLivenessAssetsStatus}) — the\n * `useSyncExternalStore(subscribe, getSnapshot)` contract, so it plugs into\n * that hook directly.\n */\nexport function subscribeLivenessAssets(\n listener: (status: LivenessAssetsStatus) => void,\n assetBaseUrl?: string,\n): () => void {\n const entry = entryFor(normalizeBase(assetBaseUrl));\n entry.listeners.add(listener);\n return () => {\n entry.listeners.delete(listener);\n };\n}\n\n/** Free the warmed detector (an SPA leaving the flow). Never call while a capture is mounted. */\nexport function releaseLivenessAssets(assetBaseUrl?: string): void {\n const entry = registry().get(normalizeBase(assetBaseUrl));\n if (entry === undefined) return;\n entry.scheduled?.settle(\"idle\"); // cancel a pending idle-deferred load\n entry.detector?.destroy();\n entry.detector = null;\n entry.detectorPromise = null; // an in-flight load is destroyed when it lands (begin)\n entry.promise = null;\n setStatus(entry, \"idle\");\n}\n\nfunction nonOwningView(shared: Detector): Detector {\n return {\n get tier() {\n return shared.tier;\n },\n get fallbackReason() {\n return shared.fallbackReason;\n },\n detect: (...args: Parameters<Detector[\"detect\"]>) => shared.detect(...args),\n destroy: () => {\n // no-op: the preload registry owns the shared detector\n },\n };\n}\n\n/**\n * @internal The capture engine's detector source when a preload exists for\n * `assetBaseUrl`: a non-owning view of the warm instance (waiting for an\n * in-flight load), or — once that load failed — a cold detector OWNED by the\n * session (default ladder + deadline). `undefined` when nothing was preloaded.\n * Invoking it on an idle-scheduled preload starts that load now and joins it.\n */\nexport function preloadedDetectorFactory(assetBaseUrl?: string): (() => Promise<Detector>) | undefined {\n const base = normalizeBase(assetBaseUrl);\n const entry = registry().get(base);\n if (\n entry === undefined ||\n (entry.detector === null && entry.detectorPromise === null && entry.scheduled === null)\n ) {\n return undefined;\n }\n return async () => {\n if (entry.scheduled !== null) void begin(entry, base); // promote: the capture needs it now\n // Wait on the SETTLED preload (not the raw detector promise): a load that\n // blew the budget or was released lands destroyed and must not be wrapped.\n // Re-check after each settle — a release + new load meanwhile is joined.\n let waitingOn = entry.promise;\n while (entry.detector === null && waitingOn !== null) {\n await waitingOn;\n if (entry.promise === waitingOn) break;\n waitingOn = entry.promise;\n }\n return entry.detector !== null ? nonOwningView(entry.detector) : createDetector({ v3AssetBaseUrl: base });\n };\n}\n","// Tiny typed emitter (zero deps). Handlers are snapshotted before dispatch so an\n// unsubscribe from inside a handler never skips a sibling.\n\nexport interface Emitter<Events extends object> {\n on<K extends keyof Events>(event: K, handler: (payload: Events[K]) => void): () => void;\n emit<K extends keyof Events>(event: K, payload: Events[K]): void;\n clear(): void;\n}\n\nexport function createEmitter<Events extends object>(): Emitter<Events> {\n const handlers = new Map<keyof Events, Set<(payload: never) => void>>();\n return {\n on(event, handler) {\n let set = handlers.get(event);\n if (set === undefined) {\n set = new Set();\n handlers.set(event, set);\n }\n const h = handler as (payload: never) => void;\n set.add(h);\n return () => {\n set?.delete(h);\n };\n },\n emit(event, payload) {\n const set = handlers.get(event);\n if (set === undefined) return;\n for (const handler of [...set]) {\n (handler as (p: Events[typeof event]) => void)(payload);\n }\n },\n clear() {\n handlers.clear();\n },\n };\n}\n","// Screen Wake Lock for the live camera (starting / gating / recording): keeps a\n// phone from dimming or locking mid-capture. Best-effort by design — SSR-safe,\n// feature-detected at call time, and NEVER throws or rejects into the capture:\n// a refused request (NotAllowedError while hidden, `screen-wake-lock`\n// Permissions-Policy blocked in an iframe, insecure context) just means no lock.\n\n/** The wake-lock controller the capture handle drives from its state stream. */\nexport interface ScreenWakeLock {\n /** `true` while the camera is live: hold the lock (and re-take it on return to the page). */\n setActive(active: boolean): void;\n}\n\nfunction wakeLockApi(): WakeLock | null {\n if (typeof navigator === \"undefined\" || !(\"wakeLock\" in navigator)) return null;\n const api = navigator.wakeLock;\n return typeof api?.request === \"function\" ? api : null;\n}\n\nfunction isVisible(): boolean {\n return typeof document === \"undefined\" || document.visibilityState === \"visible\";\n}\n\nfunction releaseQuietly(sentinel: WakeLockSentinel): void {\n try {\n void sentinel.release().catch(() => undefined);\n } catch {\n // A throwing release (already released, odd polyfill) is not the capture's problem.\n }\n}\n\n/** `enabled: false` (`keepScreenAwake: false`) → a no-op controller that never requests. */\nexport function createScreenWakeLock(options: { enabled?: boolean } = {}): ScreenWakeLock {\n if (options.enabled === false) return { setActive: () => undefined };\n\n let wanted = false;\n let sentinel: WakeLockSentinel | null = null;\n // One request at a time: the state stream repeats live states (preflight →\n // acquiring both map to `starting`), and a visibility return may race them.\n let inFlight = false;\n let listening = false;\n\n const acquire = (): void => {\n if (!wanted || inFlight || (sentinel !== null && !sentinel.released)) return;\n const api = wakeLockApi();\n // Hidden → the request would reject with NotAllowedError; the\n // visibilitychange listener takes it once the page is visible again.\n if (api === null || !isVisible()) return;\n let request: Promise<WakeLockSentinel>;\n try {\n request = api.request(\"screen\");\n } catch {\n return;\n }\n inFlight = true;\n request.then(\n (granted) => {\n inFlight = false;\n // Released (captured / error / destroy) while the request was pending.\n if (!wanted) {\n releaseQuietly(granted);\n return;\n }\n sentinel = granted;\n // The browser auto-releases on page hide; forget it so a return re-acquires.\n granted.addEventListener?.(\"release\", () => {\n if (sentinel === granted) sentinel = null;\n });\n },\n () => {\n inFlight = false;\n },\n );\n };\n\n const onVisibilityChange = (): void => {\n if (isVisible()) acquire();\n };\n\n const listen = (on: boolean): void => {\n if (typeof document === \"undefined\" || on === listening) return;\n listening = on;\n if (on) document.addEventListener(\"visibilitychange\", onVisibilityChange);\n else document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n };\n\n return {\n setActive: (active) => {\n wanted = active;\n if (active) {\n // No API → nothing to re-acquire either; skip the listener.\n if (wakeLockApi() === null) return;\n listen(true);\n acquire();\n return;\n }\n listen(false);\n if (sentinel !== null) {\n const held = sentinel;\n sentinel = null;\n releaseQuietly(held);\n }\n },\n };\n}\n","// createLivenessCapture — the public, framework-free capture handle (spec\n// §3.1) over the calibration-pinned CaptureSessionV3 engine. It MAPS the\n// engine's event surface (statechange / checks / guidance / artifacts / error /\n// frozen) onto the partner contract (state / gate / captured / error) and owns\n// the NO_RECORDER degrade (photo mode → keyframe-only `captured`). Pinned\n// capture constants stay inside the engine — nothing here is configurable.\nimport { detectIsMobile } from \"../core/capture-constraints\";\nimport { isMediaRecorderSupported } from \"../core/media\";\nimport type { CaptureErrorKind as EngineErrorKind, CaptureState as EngineState } from \"../core/types\";\nimport {\n CaptureSessionV3,\n type CaptureSessionV3Events,\n type CaptureSessionV3Options,\n} from \"../core/v3/capture-session-v3\";\nimport { createDetector as createLadderDetector } from \"../core/v3/detector\";\nimport { createEmitter } from \"../verification/emitter\";\nimport { preloadedDetectorFactory } from \"./preload\";\nimport { createScreenWakeLock } from \"./wake-lock\";\nimport type {\n CaptureErrorKind,\n CaptureState,\n GateHint,\n LivenessCaptureEvents,\n LivenessCaptureHandle,\n LivenessCaptureOptions,\n} from \"../verification/types\";\n\n/** The engine surface the handle binds — CaptureSessionV3 or a test double. */\nexport interface CaptureEngine {\n readonly state: EngineState;\n on<K extends keyof CaptureSessionV3Events>(\n event: K,\n handler: (payload: CaptureSessionV3Events[K]) => void,\n ): () => void;\n /** `facingMode` sets the camera for this attempt and later retries. */\n start(options?: { facingMode?: \"user\" | \"environment\" }): Promise<void>;\n retry(): void;\n switchCamera(): void;\n destroy(): void;\n /** Device-profile chrome hint (LOW class → `'reduced'`); absent on doubles. */\n readonly animations?: \"full\" | \"reduced\";\n}\n\n/** Internal construction seams (tests; Sekyro's own hosted page). */\nexport interface LivenessCaptureInternals {\n /** Replace the engine constructor (default: `new CaptureSessionV3(options)`). */\n createEngine?: (options: CaptureSessionV3Options) => CaptureEngine;\n /** Extra engine options merged in (worker offload, warmed detector, telemetry). */\n engineOptions?: Partial<\n Pick<CaptureSessionV3Options, \"createDetector\" | \"createWorker\" | \"telemetry\" | \"deviceClass\">\n >;\n /** Clock (default `performance.now`). */\n now?: () => number;\n}\n\n/** fcv3.* guidance key → public gate hint code. */\nexport function mapGuidanceHint(key: string): GateHint | null {\n switch (key) {\n case \"fcv3.no_face\":\n case \"fcv3.center_face\":\n case \"fcv3.straighten\":\n return \"center\";\n case \"fcv3.come_closer\":\n return \"closer\";\n case \"fcv3.move_back\":\n return \"farther\";\n case \"fcv3.more_light\":\n case \"fcv3.too_bright\":\n return \"light\";\n case \"fcv3.hold_still\":\n case \"fcv3.steady_camera\":\n return \"still\";\n default:\n return null;\n }\n}\n\n/** Engine state → public state (`preflight|acquiring → starting`, `positioning → gating`). */\nexport function mapEngineState(state: EngineState): CaptureState {\n switch (state) {\n case \"preflight\":\n case \"acquiring\":\n return \"starting\";\n case \"positioning\":\n return \"gating\";\n case \"idle\":\n case \"recording\":\n case \"captured\":\n case \"error\":\n return state;\n default: {\n const unhandled: never = state;\n throw new Error(`mapEngineState: unhandled state ${unhandled as string}`);\n }\n }\n}\n\n/** Engine error kinds → the public six (`models|cliptoolarge|recorder|overconstrained → generic`). */\nexport function mapErrorKind(kind: EngineErrorKind): CaptureErrorKind {\n switch (kind) {\n case \"permission\":\n case \"notfound\":\n case \"inuse\":\n case \"insecure\":\n case \"unsupported\":\n return kind;\n case \"overconstrained\":\n case \"generic\":\n case \"models\":\n case \"cliptoolarge\":\n case \"recorder\":\n return \"generic\";\n default: {\n const unhandled: never = kind;\n throw new Error(`mapErrorKind: unhandled kind ${unhandled as string}`);\n }\n }\n}\n\n/** Handle + the hooks the verification layer uses to drive the flow states. */\nexport interface LivenessCaptureInternalHandle {\n handle: LivenessCaptureHandle;\n /** Push a post-capture state (`uploading | checking | done`). */\n setFlowState(state: Extract<CaptureState, \"uploading\" | \"checking\" | \"done\">): void;\n}\n\nexport function createLivenessCaptureInternal(\n options: LivenessCaptureOptions,\n internals: LivenessCaptureInternals = {},\n): LivenessCaptureInternalHandle {\n const now = internals.now ?? (() => performance.now());\n const emitter = createEmitter<LivenessCaptureEvents>();\n // NO_RECORDER degrade: no MediaRecorder → photo mode (keyframe only). Decided\n // ONCE at construction; the artifacts carry the flag.\n const degraded = !isMediaRecorderSupported();\n // Preloaded detector (spec D3): resolved at CALL time — the preload may start\n // after this handle is constructed (React mounts both in one commit). Host\n // seams win: an explicit createDetector, or worker offload (createWorker).\n // Without a preload this is the engine's own cold default, unchanged.\n const hostSeams = internals.engineOptions;\n const preloadAware: Pick<CaptureSessionV3Options, \"createDetector\"> =\n hostSeams?.createDetector === undefined && hostSeams?.createWorker === undefined\n ? {\n createDetector: () => {\n const warmed = preloadedDetectorFactory(options.assetBaseUrl);\n return warmed !== undefined\n ? warmed()\n : createLadderDetector(\n options.assetBaseUrl !== undefined ? { v3AssetBaseUrl: options.assetBaseUrl } : {},\n );\n },\n }\n : {};\n const engineOptions: CaptureSessionV3Options = {\n video: options.video,\n mode: degraded ? \"photo\" : \"clip\",\n ...(options.facing !== undefined ? { facingMode: options.facing } : {}),\n ...(options.assetBaseUrl !== undefined ? { v3AssetBaseUrl: options.assetBaseUrl } : {}),\n ...(internals.engineOptions ?? {}),\n ...preloadAware,\n };\n const engine: CaptureEngine =\n internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);\n\n let state: CaptureState = \"idle\";\n let hint: GateHint | null = null;\n let progress = 0;\n let faceDetected = false;\n let animations: \"full\" | \"reduced\" = \"full\";\n let recordingStartedAt: number | null = null;\n let frozenAt: number | null = null;\n // Frame size latched while the stream is LIVE: the engine stops the tracks\n // before it emits `artifacts`, and iOS Safari then reports videoWidth /\n // videoHeight as 0 (field: comparison records with width 0 / height 0).\n let frameWidth = 0;\n let frameHeight = 0;\n const latchFrameSize = (): void => {\n if (options.video.videoWidth > 0 && options.video.videoHeight > 0) {\n frameWidth = options.video.videoWidth;\n frameHeight = options.video.videoHeight;\n }\n };\n\n // Screen wake lock while the camera is live; every other state (captured,\n // the post-capture flow states, error, idle) releases it.\n const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });\n const setState = (next: CaptureState): void => {\n state = next;\n wakeLock.setActive(next === \"starting\" || next === \"gating\" || next === \"recording\");\n emitter.emit(\"state\", next);\n };\n const emitGate = (): void => emitter.emit(\"gate\", { progress, hint, faceDetected });\n // The LOW-profile hint settles once the engine probes (statechange) and may\n // flip mid-positioning on a dynamic demotion (per checks tick) — emit on change.\n const syncProfile = (): void => {\n const next = engine.animations ?? \"full\";\n if (next === animations) return;\n animations = next;\n emitter.emit(\"profile\", { animations });\n };\n\n const unsubscribes = [\n engine.on(\"statechange\", ({ state: engineState }) => {\n const next = mapEngineState(engineState);\n if (next === \"starting\" && state !== \"starting\") {\n // Fresh attempt: gate chrome resets.\n hint = null;\n progress = 0;\n faceDetected = false;\n recordingStartedAt = null;\n frozenAt = null;\n frameWidth = 0;\n frameHeight = 0;\n }\n setState(next);\n syncProfile();\n if (next === \"recording\") {\n latchFrameSize();\n recordingStartedAt = now();\n hint = \"hold\";\n progress = 1;\n emitGate();\n }\n }),\n engine.on(\"checks\", (payload) => {\n latchFrameSize(); // the engine only ticks on a live frame (videoWidth > 0)\n progress = payload.progress;\n faceDetected = payload.factors.presence > 0;\n emitGate();\n syncProfile();\n }),\n engine.on(\"guidance\", ({ key }) => {\n hint = mapGuidanceHint(key);\n emitGate();\n }),\n engine.on(\"frozen\", ({ previewDataUrl }) => {\n frozenAt = now();\n emitter.emit(\"frozen\", { previewDataUrl });\n }),\n engine.on(\"artifacts\", (artifacts) => {\n const clipMs =\n recordingStartedAt !== null && frozenAt !== null\n ? Math.max(0, Math.round(frozenAt - recordingStartedAt))\n : undefined;\n emitter.emit(\"captured\", {\n keyframe: artifacts.keyframe,\n ...(artifacts.clip !== undefined ? { clip: artifacts.clip } : {}),\n ...(artifacts.clipMimeType !== undefined ? { clipMime: artifacts.clipMimeType } : {}),\n width: options.video.videoWidth || frameWidth,\n height: options.video.videoHeight || frameHeight,\n ...(clipMs !== undefined ? { clipMs } : {}),\n captureChannel: detectIsMobile() ? \"mobile\" : \"desktop\",\n ...(degraded ? { degraded: \"NO_RECORDER\" as const } : {}),\n engine: \"v3\",\n // Face-capture timing: the engine's marks, forwarded flat (absent → absent).\n ...(artifacts.timing?.cameraReadyMs !== undefined\n ? { cameraReadyMs: artifacts.timing.cameraReadyMs }\n : {}),\n ...(artifacts.timing?.faceAcquireMs !== undefined\n ? { faceAcquireMs: artifacts.timing.faceAcquireMs }\n : {}),\n ...(artifacts.timing?.captureMs !== undefined\n ? { captureMs: artifacts.timing.captureMs }\n : {}),\n });\n }),\n engine.on(\"error\", ({ kind, retryable }) => {\n emitter.emit(\"error\", { kind: mapErrorKind(kind), retryable });\n }),\n ];\n\n const handle: LivenessCaptureHandle = {\n get state() {\n return state;\n },\n on: (event, handler) => emitter.on(event, handler),\n start: (startOptions) =>\n engine.start(startOptions?.facing !== undefined ? { facingMode: startOptions.facing } : {}),\n retry: () => engine.retry(),\n switchCamera: () => engine.switchCamera(),\n destroy: () => {\n for (const unsubscribe of unsubscribes) unsubscribe();\n wakeLock.setActive(false);\n engine.destroy();\n emitter.clear();\n },\n };\n\n return {\n handle,\n setFlowState: (next) => {\n if (state !== next) setState(next);\n },\n };\n}\n\n/**\n * Create a standalone liveness capture (no upload — pair it with your own\n * submission, or use {@link Verification.liveness} for the full flow).\n */\nexport function createLivenessCapture(options: LivenessCaptureOptions): LivenessCaptureHandle {\n return createLivenessCaptureInternal(options).handle;\n}\n","// Result mapping: server view → the public VerificationResult (spec §3.1) plus\n// the view helpers the flow needs (frontier, terminal, view merge).\nimport type {\n CheckReason,\n CheckType,\n HostedSessionView,\n HostedStepView,\n SessionView,\n} from \"../client/types\";\nimport type { ReasonCode, VerificationResult } from \"./types\";\n\n/**\n * Reasons the backend parks at `input_required` while attempts remain\n * (identity-backend/src/checks/check-reasons.ts isRetryable tables — the single\n * source of truth; identity-hosted/lib/reason-copy.ts mirrors the same rows).\n */\nexport const RETRYABLE_REASONS: ReadonlySet<CheckReason> = new Set<CheckReason>([\n \"no_face\",\n \"media_unreadable\",\n \"clip_too_short\",\n \"no_face_source\",\n \"side_missing\",\n \"unreadable\",\n \"registry_unavailable\",\n \"otp_mismatch\",\n \"otp_expired\",\n \"otp_send_failed\",\n]);\n\n/** The two checks one biometric capture feeds. */\nconst BIOMETRIC_CHECKS: ReadonlySet<CheckType> = new Set<CheckType>([\"liveness\", \"face_match\"]);\n\n/** A check the browser may still submit. */\nexport function isExecutable(step: HostedStepView | undefined): boolean {\n return step !== undefined && (step.state === \"pending\" || step.state === \"input_required\");\n}\n\n/** The session accepts no further submissions. */\nexport function isTerminalView(view: Pick<HostedSessionView, \"status\">): boolean {\n return view.status !== \"created\" && view.status !== \"in_progress\";\n}\n\n/** First step in `steps[]` order with state `pending | input_required` (the backend's frontier). */\nexport function frontierStep(view: HostedSessionView): HostedStepView | undefined {\n return view.steps.find((step) => isExecutable(step));\n}\n\nexport function stepOf(view: HostedSessionView, checkType: CheckType): HostedStepView | undefined {\n return view.steps.find((step) => step.checkType === checkType);\n}\n\n/**\n * Fold a submit response (partner `SessionView`: no attemptsRemaining) into the\n * hosted view: status/outcome/step states+reasons come from the response; the\n * submitted check's `attemptsRemaining` is decremented locally (every accepted\n * submit consumes exactly one attempt server-side — `max(0, cap - attempts)`).\n * This keeps the happy path free of extra `GET /hosted/session` round-trips.\n */\nexport function mergeSessionView(\n prev: HostedSessionView,\n next: SessionView,\n submitted: CheckType,\n): HostedSessionView {\n const byType = new Map(prev.steps.map((step) => [step.checkType, step] as const));\n const steps: HostedStepView[] = next.steps.map((step) => {\n const before = byType.get(step.checkType);\n const attemptsBefore = before?.attemptsRemaining ?? 0;\n const merged: HostedStepView = {\n ...(before ?? {}),\n checkType: step.checkType,\n state: step.state,\n attemptsRemaining:\n step.checkType === submitted ? Math.max(0, attemptsBefore - 1) : attemptsBefore,\n };\n if (step.reason !== undefined) merged.reason = step.reason;\n else delete merged.reason;\n return merged;\n });\n return { ...prev, status: next.status, outcome: next.outcome, steps, expiresAt: next.expiresAt };\n}\n\nexport function unavailable(sessionId: string, reason?: ReasonCode): VerificationResult {\n return reason === undefined\n ? { sessionId, status: \"unavailable\" }\n : { sessionId, status: \"unavailable\", reason };\n}\n\nfunction withReason(\n sessionId: string,\n status: VerificationResult[\"status\"],\n reason: CheckReason | undefined,\n): VerificationResult {\n return reason === undefined ? { sessionId, status } : { sessionId, status, reason };\n}\n\n/**\n * Map the session view to the public result:\n * - `completed/approved → approved`, `completed/review` or `review_pending → review`,\n * `failed` or `completed/rejected → rejected` (reason = the deciding step's);\n * - `expired → unavailable` (no reason);\n * - in flight: a biometric step parked at `input_required` with a retryable\n * reason → `retry` when `autoRetry` and attempts remain, else `rejected`\n * (the partner opted out of retries — this capture was refused); a step in\n * `review` with nothing executable left → `review`; otherwise `unavailable`\n * (a non-biometric frontier the SDK cannot drive, or still processing).\n */\nexport function mapSessionResult(\n view: HostedSessionView,\n opts: { autoRetry: boolean },\n): VerificationResult {\n const { sessionId } = view;\n switch (view.status) {\n case \"completed\":\n if (view.outcome === \"approved\") return { sessionId, status: \"approved\" };\n if (view.outcome === \"review\") return withReason(sessionId, \"review\", reasonIn(view, \"review\"));\n return withReason(sessionId, \"rejected\", reasonIn(view, \"failed\"));\n case \"failed\":\n return withReason(sessionId, \"rejected\", reasonIn(view, \"failed\"));\n case \"review_pending\":\n return withReason(sessionId, \"review\", reasonIn(view, \"review\"));\n case \"expired\":\n return unavailable(sessionId);\n case \"created\":\n case \"in_progress\":\n break;\n default: {\n const unhandled: never = view.status;\n throw new Error(`mapSessionResult: unhandled status ${unhandled as string}`);\n }\n }\n\n const parked = view.steps.find(\n (step) =>\n BIOMETRIC_CHECKS.has(step.checkType) &&\n step.state === \"input_required\" &&\n step.reason !== undefined,\n );\n if (parked?.reason !== undefined) {\n const canRetry =\n opts.autoRetry && RETRYABLE_REASONS.has(parked.reason) && parked.attemptsRemaining > 0;\n return {\n sessionId,\n status: canRetry ? \"retry\" : \"rejected\",\n reason: parked.reason,\n attemptsRemaining: parked.attemptsRemaining,\n };\n }\n if (frontierStep(view) === undefined && view.steps.some((step) => step.state === \"review\")) {\n return withReason(sessionId, \"review\", reasonIn(view, \"review\"));\n }\n return unavailable(sessionId);\n}\n\n/** Reason of the first step in the given state (the deciding check). */\nfunction reasonIn(view: HostedSessionView, state: HostedStepView[\"state\"]): CheckReason | undefined {\n return view.steps.find((step) => step.state === state && step.reason !== undefined)?.reason;\n}\n","// The post-capture flow, driven by the CURRENT hosted frontier: when `liveness`\n// is the frontier → upload the clip → attestation → submit `liveness`, then\n// `face_match` from this capture's keyframe; when liveness already settled and\n// `face_match` is the frontier → keyframe + `face_match` only; nothing the SDK\n// can drive → settled (the caller maps the view). A RESYNC 409\n// (check_already_finalized / check_out_of_order / stale frontier) re-GETs the\n// view and RE-ENTERS the same frontier logic (bounded) instead of giving up —\n// a liveness that passed under an earlier run still gets its face_match.\n// Logic ported (not the React) from\n// identity-hosted/lib/use-submit.ts (idempotency-key lifecycle, same-key\n// transport replay, one-shot consent_required recovery) and\n// lib/use-capture-submission.ts + components/steps/liveness-step.tsx (the\n// `buildSubmit` executor closures and the face_match chain).\n//\n// Per-submission caches (promoted media key, attestation) are CLOSURE LOCALS of\n// each executor: a same-key replay re-runs the executor with the caches intact\n// (byte-identical body — a re-presign or a fresh token under the same key would\n// be an idempotency_key_collision); fresh artifacts build fresh closures.\nimport { HostedApiError } from \"../client/errors\";\nimport { type HostedClient, stripMimeParams } from \"../client/hosted-client\";\nimport type {\n AttestationInput,\n CheckType,\n HostedSessionView,\n LivenessCaptureInput,\n SessionView,\n} from \"../client/types\";\nimport { frontierStep, isTerminalView, stepOf } from \"./result\";\nimport type { CapturedArtifacts, SdkFailureReason } from \"./types\";\n\n/** The client surface the flow needs — `HostedClient` or a test fake. */\nexport type HostedClientLike = Pick<\n HostedClient,\n \"getSession\" | \"recordConsent\" | \"presign\" | \"uploadToSpaces\" | \"completeMedia\" | \"submitCheck\"\n>;\n\n/** The presign policy allowlist for `kind: 'clip'` (media.policy.ts mirror). */\nexport const CLIP_MIME_ALLOWLIST: readonly string[] = [\"video/webm\", \"video/mp4\"];\n\n/** The engine's keyframe encode (captureMirroredKeyframe → canvas JPEG). */\nconst KEYFRAME_MIME = \"image/jpeg\";\n\n/** Same-key transport replays AFTER the first try, and the pause between them. */\nexport const TRANSPORT_REPLAY_ATTEMPTS = 2;\nexport const TRANSPORT_REPLAY_DELAY_MS = 1_000;\n/** Cap on honouring a 429's Retry-After before giving up on this capture. */\nconst MAX_RETRY_AFTER_MS = 10_000;\n/**\n * Bound on frontier re-entries after a RESYNC code per capture (each re-entry\n * re-GETs the view; each executable check is submitted at most once per capture).\n */\nexport const RESYNC_REENTRY_MAX = 3;\n\n/** No (usable) response arrived — a same-key replay can succeed. */\nconst TRANSPORT_CODES: ReadonlySet<string> = new Set([\n \"network_error\",\n \"timeout\",\n \"upload_failed\",\n \"unknown_error\",\n \"rate_limit_exceeded\",\n]);\n\n/** The server moved past what we hold — re-GET and map from truth. */\nconst RESYNC_CODES: ReadonlySet<string> = new Set([\n \"session_not_active\",\n \"check_not_found\",\n \"check_already_finalized\",\n \"check_out_of_order\",\n]);\n\n/** A Turnstile challenge failed client-side (load / render / error-callback / timeout). */\nexport class AttestationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AttestationError\";\n }\n}\n\n/** One logical submission: receives the client and ITS idempotency key. */\nexport type SubmitExecutor = (\n client: HostedClientLike,\n idempotencyKey: string,\n) => Promise<SessionView>;\n\nexport interface SubmitRuntime {\n client: HostedClientLike;\n /** Force-record consent (the server just said it is missing). */\n ensureConsent(opts: { force: true }): Promise<void>;\n sleep(ms: number): Promise<void>;\n uuid(): string;\n}\n\n/**\n * Run one logical submission (ported use-submit semantics):\n * - a fresh `crypto.randomUUID()` key per logical submission;\n * - a transport-class failure replays the SAME executor under the SAME key\n * (bounded) — the backend's IdempotencyInterceptor returns the first\n * response instead of burning another attempt;\n * - ONE `consent_required` recovery: force-record consent and re-run under a\n * FRESH key (the 409 envelope is idempotency-cached under the old one);\n * - ONE attestation retry (client-side Turnstile failure or a server\n * `attestation_invalid`): `onAttestationInvalid` drops the cached token and\n * the executor re-runs under a fresh key.\n * Throws the final error otherwise.\n */\nexport async function runSubmission(\n rt: SubmitRuntime,\n executor: SubmitExecutor,\n hooks: { onAttestationInvalid?: () => void } = {},\n): Promise<SessionView> {\n let key = rt.uuid();\n let consentRecovered = false;\n let attestationRetried = false;\n let transportReplays = 0;\n for (;;) {\n try {\n return await executor(rt.client, key);\n } catch (err) {\n if (err instanceof AttestationError) {\n if (attestationRetried) throw err;\n attestationRetried = true;\n hooks.onAttestationInvalid?.();\n key = rt.uuid();\n continue;\n }\n if (!(err instanceof HostedApiError)) throw err;\n if (err.code === \"consent_required\" && !consentRecovered) {\n consentRecovered = true;\n await rt.ensureConsent({ force: true });\n key = rt.uuid();\n continue;\n }\n if (err.code === \"attestation_invalid\" && !attestationRetried) {\n attestationRetried = true;\n hooks.onAttestationInvalid?.();\n key = rt.uuid();\n continue;\n }\n if (TRANSPORT_CODES.has(err.code) && transportReplays < TRANSPORT_REPLAY_ATTEMPTS) {\n transportReplays += 1;\n const retryAfterMs =\n err.retryAfterSeconds !== undefined ? err.retryAfterSeconds * 1000 : undefined;\n await rt.sleep(\n retryAfterMs !== undefined\n ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS)\n : TRANSPORT_REPLAY_DELAY_MS,\n );\n continue;\n }\n throw err;\n }\n }\n}\n\nexport interface LivenessFlowDeps extends SubmitRuntime {\n artifacts: CapturedArtifacts;\n /** Fresh attestation per call (Turnstile execute, or `{sandbox: true}`). */\n getAttestation(): Promise<AttestationInput>;\n /** Latest hosted view (frontier decisions). */\n getView(): HostedSessionView;\n /** Re-GET the view from the server (after a RESYNC code) and fold it in. */\n refreshView(): Promise<HostedSessionView>;\n /** Fold a submit response into the view. */\n applySubmit(view: SessionView, submitted: CheckType): void;\n /** Flow-state hook for the capture handle. */\n onPhase(phase: \"uploading\" | \"checking\"): void;\n /** The verification was destroyed (the only thing that cancels a chain). */\n isCancelled(): boolean;\n}\n\nexport type LivenessFlowOutcome =\n | { kind: \"settled\" }\n | { kind: \"resync\" }\n | { kind: \"unavailable\"; reason: SdkFailureReason }\n | { kind: \"cancelled\" };\n\n/** Liveness states after which THIS capture's keyframe may feed face_match. */\nconst LIVENESS_CHAINABLE: ReadonlySet<string> = new Set([\"passed\", \"review\", \"processing\", \"skipped\"]);\n\n/**\n * Run the biometric submissions the CURRENT frontier allows, re-evaluating\n * after every submit and after every RESYNC re-GET:\n * - frontier `liveness` → clip upload + attestation + `liveness` (once per capture);\n * - frontier `face_match` → keyframe upload + `face_match` (once per capture),\n * allowed when liveness was NOT part of this capture (it had already\n * settled — any state, incl. `failed`: the selfie-fallback case, where this\n * capture only feeds face_match) or settled chainable from this capture\n * (passed / review / processing / skipped — or absent);\n * - anything else (terminal view, a non-biometric frontier, a check this\n * capture already submitted) → `settled` for the caller to map the view.\n * A RESYNC code re-GETs the view and re-enters (at most {@link RESYNC_REENTRY_MAX}\n * times); the final RESYNC surfaces as `resync` for the caller to map.\n */\nexport async function runLivenessFlow(deps: LivenessFlowDeps): Promise<LivenessFlowOutcome> {\n const { artifacts } = deps;\n const clip = artifacts.clip;\n const bareMime = stripMimeParams(artifacts.clipMime ?? \"\");\n if (\n artifacts.degraded === \"NO_RECORDER\" ||\n clip === undefined ||\n !CLIP_MIME_ALLOWLIST.includes(bareMime)\n ) {\n // No clip, or a container outside the presign allowlist (never sign an\n // off-allowlist type): v2 cannot run THIS capture.\n return { kind: \"unavailable\", reason: \"no_recorder\" };\n }\n\n // Per-capture executors: their closure caches (promoted media key,\n // attestation) survive a same-key transport replay; each is run at most once.\n const submitLiveness = buildLivenessSubmit(deps, clip, bareMime);\n const submitFaceMatch = buildFaceMatchSubmit(deps, artifacts.keyframe);\n let livenessSubmitted = false;\n let faceMatchSubmitted = false;\n let livenessChainable = true;\n let resyncs = 0;\n\n for (;;) {\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n const view = deps.getView();\n if (isTerminalView(view)) return { kind: \"settled\" };\n const frontier = frontierStep(view)?.checkType;\n const next =\n frontier === \"liveness\" && !livenessSubmitted\n ? \"liveness\"\n : frontier === \"face_match\" && !faceMatchSubmitted && livenessChainable\n ? \"face_match\"\n : null;\n if (next === null) return { kind: \"settled\" };\n try {\n if (next === \"liveness\") {\n livenessSubmitted = true;\n const response = await submitLiveness();\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n deps.applySubmit(response, \"liveness\");\n const state = stepOf(deps.getView(), \"liveness\")?.state;\n livenessChainable = state === undefined || LIVENESS_CHAINABLE.has(state);\n } else {\n faceMatchSubmitted = true;\n const response = await submitFaceMatch();\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n deps.applySubmit(response, \"face_match\");\n }\n } catch (err) {\n if (deps.isCancelled()) return { kind: \"cancelled\" };\n const outcome = classifyFailure(err);\n if (outcome.kind !== \"resync\" || resyncs >= RESYNC_REENTRY_MAX) return outcome;\n resyncs += 1;\n // The server moved past what we hold: re-GET and re-enter from truth.\n await deps.refreshView();\n }\n }\n}\n\nfunction buildLivenessSubmit(\n deps: LivenessFlowDeps,\n clip: Blob,\n bareMime: string,\n): () => Promise<SessionView> {\n let clipKey: string | null = null;\n let attestation: AttestationInput | null = null;\n return () =>\n runSubmission(\n deps,\n async (client, idempotencyKey) => {\n if (clipKey === null) {\n deps.onPhase(\"uploading\");\n const presigned = await client.presign(\"clip\", bareMime, clip.size);\n await client.uploadToSpaces(presigned.uploadUrl, clip, bareMime);\n clipKey = (await client.completeMedia(presigned.key)).key;\n }\n // Attestation AFTER the upload settles (token stays fresh), cached\n // for the same-key replay.\n if (attestation === null) {\n try {\n attestation = await deps.getAttestation();\n } catch (err) {\n throw new AttestationError(err instanceof Error ? err.message : String(err));\n }\n }\n deps.onPhase(\"checking\");\n return client.submitCheck(\n \"liveness\",\n {\n clipKey,\n attestation,\n captureChannel: deps.artifacts.captureChannel,\n capture: captureInputOf(deps.artifacts),\n },\n { idempotencyKey },\n );\n },\n {\n onAttestationInvalid: () => {\n attestation = null;\n },\n },\n );\n}\n\n/**\n * The `input.capture` block of the liveness submission: the engine id, the\n * stream dimensions, the clip length and the face-capture timing from the\n * `captured` event — exactly the backend's capture-metadata allowlist\n * (identity-backend `capture-metadata.ts`). Never media, never tokens.\n */\nexport function captureInputOf(artifacts: CapturedArtifacts): LivenessCaptureInput {\n return {\n ...(artifacts.engine !== undefined ? { engine: artifacts.engine } : {}),\n width: artifacts.width,\n height: artifacts.height,\n ...(artifacts.clipMs !== undefined ? { clipMs: artifacts.clipMs } : {}),\n ...(artifacts.cameraReadyMs !== undefined ? { cameraReadyMs: artifacts.cameraReadyMs } : {}),\n ...(artifacts.faceAcquireMs !== undefined ? { faceAcquireMs: artifacts.faceAcquireMs } : {}),\n ...(artifacts.captureMs !== undefined ? { captureMs: artifacts.captureMs } : {}),\n };\n}\n\nfunction buildFaceMatchSubmit(deps: LivenessFlowDeps, keyframe: Blob): () => Promise<SessionView> {\n let imageKey: string | null = null;\n return () =>\n runSubmission(deps, async (client, idempotencyKey) => {\n if (imageKey === null) {\n deps.onPhase(\"uploading\");\n const presigned = await client.presign(\"image\", KEYFRAME_MIME, keyframe.size);\n await client.uploadToSpaces(presigned.uploadUrl, keyframe, KEYFRAME_MIME);\n imageKey = (await client.completeMedia(presigned.key)).key;\n }\n deps.onPhase(\"checking\");\n return client.submitCheck(\"face_match\", { imageKey }, { idempotencyKey });\n });\n}\n\nfunction classifyFailure(err: unknown): LivenessFlowOutcome {\n if (err instanceof AttestationError) return { kind: \"unavailable\", reason: \"attestation\" };\n if (err instanceof HostedApiError) {\n if (err.code === \"attestation_invalid\") return { kind: \"unavailable\", reason: \"attestation\" };\n if (RESYNC_CODES.has(err.code)) return { kind: \"resync\" };\n }\n return { kind: \"unavailable\", reason: \"network\" };\n}\n","// Cloudflare Turnstile: lazy script loader + execute-mode invisible challenge.\n// Ported from identity-hosted/lib/turnstile.ts (Plan 6 Task 10 AC4) — same\n// behaviour: the script loads ONCE, lazily, only when a liveness submit first\n// needs a token; each token request renders a fresh execute-mode widget,\n// resolves the callback token, and removes the widget. Hosts' CSP must allow\n// challenges.cloudflare.com.\n//\n// The site key is RUNTIME data (`HostedSessionView.turnstileSiteKey`); when it\n// is null the verification sends `{sandbox: true}` instead of calling this.\n\nexport const TURNSTILE_SCRIPT_SRC = \"https://challenges.cloudflare.com/turnstile/v0/api.js\";\n\n/** Bound on a single execute round-trip (challenge fetch + solve). */\nexport const TURNSTILE_EXECUTE_TIMEOUT_MS = 30_000;\n\nexport interface TurnstileRenderParams {\n sitekey: string;\n callback: (token: string) => void;\n \"error-callback\"?: (code?: string) => void;\n execution?: \"render\" | \"execute\";\n appearance?: \"always\" | \"execute\" | \"interaction-only\";\n}\n\n/** The subset of the Turnstile JS API this module uses. */\nexport interface TurnstileApi {\n render(container: HTMLElement, params: TurnstileRenderParams): string | undefined;\n execute(widgetId: string): void;\n remove(widgetId: string): void;\n}\n\n/** `window.turnstile` without augmenting the global Window type for consumers. */\nfunction installedApi(): TurnstileApi | undefined {\n return (window as unknown as { turnstile?: TurnstileApi }).turnstile;\n}\n\nlet loaderPromise: Promise<TurnstileApi> | null = null;\n\n/** Test-only: reset the module singleton between cases. */\nexport function resetTurnstileForTests(): void {\n loaderPromise = null;\n}\n\n/** Load the Turnstile script once (singleton; a load failure resets it so a\n * retry can re-attempt). */\nexport function loadTurnstile(): Promise<TurnstileApi> {\n loaderPromise ??= new Promise<TurnstileApi>((resolve, reject) => {\n const present = installedApi();\n if (present) {\n resolve(present);\n return;\n }\n const script = document.createElement(\"script\");\n script.src = TURNSTILE_SCRIPT_SRC;\n script.async = true;\n script.onload = () => {\n const api = installedApi();\n if (api) {\n resolve(api);\n } else {\n loaderPromise = null;\n reject(new Error(\"Turnstile script loaded but exposed no API\"));\n }\n };\n script.onerror = () => {\n loaderPromise = null;\n script.remove(); // a retry appends a fresh tag\n reject(new Error(\"Turnstile script failed to load\"));\n };\n document.head.appendChild(script);\n });\n return loaderPromise;\n}\n\n/** Options of {@link executeTurnstile}. */\nexport interface ExecuteTurnstileOptions {\n /** Bound on the challenge round-trip; default {@link TURNSTILE_EXECUTE_TIMEOUT_MS}. */\n timeoutMs?: number;\n /**\n * Aborts the challenge: the promise rejects and the widget, its container and\n * the timeout are cleaned up at once. `createVerification().destroy()` passes\n * its own signal so a torn-down verification never leaves a widget behind.\n */\n signal?: AbortSignal;\n}\n\nfunction abortError(): Error {\n return new Error(\"Turnstile challenge aborted\");\n}\n\n/**\n * Run one invisible execute-mode challenge and resolve its token. Rejects on\n * challenge error, on `signal` abort, or after {@link TURNSTILE_EXECUTE_TIMEOUT_MS}\n * — the verification maps a failure to `result {status:'unavailable',\n * reason:'attestation'}` after one retry.\n */\nexport async function executeTurnstile(\n siteKey: string,\n opts: ExecuteTurnstileOptions = {},\n): Promise<string> {\n const timeoutMs = opts.timeoutMs ?? TURNSTILE_EXECUTE_TIMEOUT_MS;\n const signal = opts.signal;\n if (signal?.aborted) throw abortError();\n const api = await loadTurnstile();\n if (signal?.aborted) throw abortError();\n const container = document.createElement(\"div\");\n document.body.appendChild(container);\n let widgetId: string | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n try {\n return await new Promise<string>((resolve, reject) => {\n timer = setTimeout(\n () => reject(new Error(`Turnstile challenge timed out (${timeoutMs} ms)`)),\n timeoutMs,\n );\n if (signal !== undefined) {\n onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n widgetId = api.render(container, {\n sitekey: siteKey,\n execution: \"execute\",\n appearance: \"execute\",\n callback: resolve,\n \"error-callback\": (code) => {\n reject(new Error(`Turnstile challenge failed${code ? ` (${code})` : \"\"}`));\n },\n });\n if (widgetId === undefined) {\n reject(new Error(\"Turnstile render returned no widget id\"));\n return;\n }\n api.execute(widgetId);\n });\n } finally {\n // ONE cleanup path for every outcome (token / challenge error / timeout /\n // abort / render failure): the challenge timer and the abort listener must\n // never leak past settlement.\n clearTimeout(timer);\n if (signal !== undefined && onAbort !== undefined) signal.removeEventListener(\"abort\", onAbort);\n if (widgetId !== undefined) {\n try {\n api.remove(widgetId);\n } catch {\n // widget already gone — nothing to clean\n }\n }\n container.remove();\n }\n}\n","// createVerification — composes the hosted client, consent, the early registry\n// submit, the capture handle and the post-capture flow behind the public\n// contract (spec §3.1). Framework-free; the only state is the latest hosted\n// view (re-emitted on every update via `on('view')`).\nimport { HostedClient } from \"../client/hosted-client\";\nimport type { AttestationInput, CheckType, HostedSessionView, SessionView } from \"../client/types\";\nimport {\n createLivenessCaptureInternal,\n type LivenessCaptureInternalHandle,\n type LivenessCaptureInternals,\n} from \"../capture/create-liveness-capture\";\nimport { createEmitter } from \"./emitter\";\nimport {\n type HostedClientLike,\n runLivenessFlow,\n runSubmission,\n type SubmitRuntime,\n} from \"./liveness-flow\";\nimport {\n frontierStep,\n isTerminalView,\n mapSessionResult,\n mergeSessionView,\n stepOf,\n unavailable,\n} from \"./result\";\nimport { executeTurnstile } from \"./turnstile\";\nimport type {\n CapturedArtifacts,\n LivenessCaptureHandle,\n LivenessCaptureOptions,\n Verification,\n VerificationEvents,\n VerificationOptions,\n VerificationResult,\n} from \"./types\";\n\n/** Production API origin — the `apiOrigin` default. */\nexport const DEFAULT_API_ORIGIN = \"https://api.sekyro.ai\";\n\n/** Pause before the single `registry_unavailable` re-submit. */\nexport const REGISTRY_RETRY_DELAY_MS = 5_000;\n\n/** Bounded poll while a biometric verdict is still `processing` after the submits. */\nexport const PROCESSING_POLL_DELAY_MS = 1_500;\nexport const PROCESSING_POLL_MAX = 10;\n\n/**\n * Construction seams — tests and Sekyro's own hosted page (`@sekyro/web/internal`).\n * Not part of the partner contract.\n */\nexport interface VerificationInternals {\n /** Replace the HTTP client (default: `new HostedClient({...})`). */\n client?: HostedClientLike;\n /**\n * Replace the Turnstile challenge (default: {@link executeTurnstile}). The\n * `signal` aborts when the verification is destroyed (M1).\n */\n getTurnstileToken?: (siteKey: string, opts: { signal: AbortSignal }) => Promise<string>;\n /** Capture-engine seams forwarded to {@link createLivenessCaptureInternal}. */\n capture?: LivenessCaptureInternals;\n sleep?: (ms: number) => Promise<void>;\n uuid?: () => string;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** `crypto.randomUUID()` with a v4 fallback for hosts that lack it. */\nexport function randomUuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c !== undefined && typeof c.randomUUID === \"function\") return c.randomUUID();\n const bytes = new Uint8Array(16);\n if (c !== undefined && typeof c.getRandomValues === \"function\") c.getRandomValues(bytes);\n else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Create a verification bound to one session (spec §3.1):\n * `consent()` → `startRegistry()` → `liveness({video})` → `on('result')`.\n */\nexport function createVerification(options: VerificationOptions): Verification {\n return createVerificationInternal(options, {});\n}\n\n/** {@link createVerification} with construction seams (tests / Sekyro-internal hosts). */\nexport function createVerificationInternal(\n options: VerificationOptions,\n internals: VerificationInternals,\n): Verification {\n const apiOrigin = options.apiOrigin ?? DEFAULT_API_ORIGIN;\n const autoRetry = options.autoRetry ?? true;\n const rearm = options.rearm ?? \"auto\";\n const client: HostedClientLike =\n internals.client ??\n new HostedClient({\n apiOrigin,\n sessionToken: options.clientToken,\n ...(options.fetch !== undefined ? { fetch: options.fetch } : {}),\n });\n const sleep = internals.sleep ?? defaultSleep;\n const uuid = internals.uuid ?? randomUuid;\n const getTurnstileToken = internals.getTurnstileToken ?? executeTurnstile;\n const emitter = createEmitter<VerificationEvents>();\n\n let destroyed = false;\n // Aborts an in-flight Turnstile challenge on destroy() (widget + timer clean up).\n const lifetime = new AbortController();\n\n // ── view ───────────────────────────────────────────────────────────────────\n let view: HostedSessionView | null = null;\n let bootstrap: Promise<HostedSessionView> | null = null;\n const setView = (next: HostedSessionView): void => {\n view = next;\n emitter.emit(\"view\", next);\n };\n const ensureView = (): Promise<HostedSessionView> => {\n if (view !== null) return Promise.resolve(view);\n bootstrap ??= client\n .getSession()\n .then((next) => {\n setView(next);\n return next;\n })\n .finally(() => {\n bootstrap = null;\n });\n return bootstrap;\n };\n const refreshView = async (): Promise<HostedSessionView> => {\n const next = await client.getSession();\n setView(next);\n return next;\n };\n const applySubmit = (response: SessionView, submitted: CheckType): void => {\n if (view !== null) setView(mergeSessionView(view, response, submitted));\n };\n\n // ── consent (idempotent; `force` after a server consent_required) ──────────\n let consentInFlight: Promise<void> | null = null;\n const recordConsent = async (force: boolean): Promise<void> => {\n const current = await ensureView();\n if (!force && current.consentRecorded) return;\n await client.recordConsent(options.locale);\n if (view !== null) setView({ ...view, consentRecorded: true });\n };\n const ensureConsent = (opts?: { force?: boolean }): Promise<void> => {\n if (opts?.force === true) return recordConsent(true);\n consentInFlight ??= recordConsent(false).finally(() => {\n consentInFlight = null;\n });\n return consentInFlight;\n };\n\n const runtime: SubmitRuntime = {\n client,\n ensureConsent: (opts) => ensureConsent(opts),\n sleep,\n uuid,\n };\n\n // ── result emission ────────────────────────────────────────────────────────\n // A session verdict is emitted ONCE (the registry may settle it before any\n // capture); `retry` / `unavailable` are per capture.\n let terminalEmitted = false;\n const emitResult = (result: VerificationResult): void => {\n if (result.status === \"approved\" || result.status === \"rejected\" || result.status === \"review\") {\n if (terminalEmitted) return;\n terminalEmitted = true;\n }\n emitter.emit(\"result\", result);\n };\n\n // ── early registry ─────────────────────────────────────────────────────────\n let registry: Promise<void> | null = null;\n let registryFailed = false;\n // The frontier is population_registry on a session minted WITHOUT a bound\n // NNI: the SDK has nothing to submit (and must never guess one).\n let registryUnbound = false;\n const submitRegistry = (): Promise<SessionView> =>\n runSubmission(runtime, (c, idempotencyKey) =>\n // `{}` input: the session is bound to a SERVER-provided NNI (Plan A Task 2).\n c.submitCheck(\"population_registry\", {}, { idempotencyKey }),\n );\n const runRegistry = async (): Promise<void> => {\n try {\n const current = await ensureView();\n if (isTerminalView(current) || frontierStep(current)?.checkType !== \"population_registry\") {\n return;\n }\n if (current.nniBound !== true) {\n registryUnbound = true;\n return;\n }\n await ensureConsent();\n applySubmit(await submitRegistry(), \"population_registry\");\n const step = view === null ? undefined : stepOf(view, \"population_registry\");\n if (\n step?.state === \"input_required\" &&\n step.reason === \"registry_unavailable\" &&\n step.attemptsRemaining > 0\n ) {\n await sleep(REGISTRY_RETRY_DELAY_MS);\n if (destroyed) return;\n applySubmit(await submitRegistry(), \"population_registry\");\n }\n } catch {\n // Never throws (incl. 409 nni_locked / transport failures): the flow\n // surfaces it through result mapping.\n registryFailed = true;\n return;\n }\n if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));\n };\n const startRegistry = (): Promise<void> => {\n registry ??= runRegistry();\n return registry;\n };\n\n // ── attestation ────────────────────────────────────────────────────────────\n // The SDK echoes the site key it rendered (`siteKey`) so the backend selects\n // the matching widget secret deterministically — Turnstile tokens are\n // single-use, so the verifier never retries with another secret (C1).\n const getAttestation = async (): Promise<AttestationInput> => {\n const current = await ensureView();\n const siteKey = current.turnstileSiteKey;\n if (siteKey === null) return { sandbox: true };\n return {\n channel: \"hosted_web\",\n siteKey,\n turnstileToken: await getTurnstileToken(siteKey, { signal: lifetime.signal }),\n };\n };\n\n // ── capture + flow ─────────────────────────────────────────────────────────\n // Submission chains are SERIALIZED: a new capture (incl. a host remount that\n // destroyed the old handle mid-upload) never abandons an in-flight chain — it\n // queues behind it and then evaluates the CURRENT frontier. Only destroy() of\n // the verification cancels. Handle side effects (flow states, auto re-arm)\n // reach the handle only while it is alive and still on the attempt that\n // produced the artifacts.\n interface BoundCapture {\n capture: LivenessCaptureInternalHandle;\n alive: boolean;\n /** Engine attempt counter (bumped on every `starting`). */\n attempt: number;\n }\n let active: BoundCapture | null = null;\n let chain: Promise<void> = Promise.resolve();\n let chainDepth = 0;\n\n const settleProcessing = async (): Promise<HostedSessionView> => {\n let current = await ensureView();\n for (\n let i = 0;\n i < PROCESSING_POLL_MAX &&\n !destroyed &&\n !isTerminalView(current) &&\n frontierStep(current) === undefined &&\n current.steps.some((step) => step.state === \"processing\");\n i += 1\n ) {\n await sleep(PROCESSING_POLL_DELAY_MS);\n if (destroyed) break;\n current = await refreshView();\n }\n return current;\n };\n\n const runFlow = async (\n bound: BoundCapture,\n artifacts: CapturedArtifacts,\n capturedAttempt: number,\n ): Promise<void> => {\n const { capture } = bound;\n const isCancelled = (): boolean => destroyed;\n // The handle still shows the attempt these artifacts came from (not\n // destroyed by a remount, no retry()/switchCamera() since).\n const ownsHandle = (): boolean => bound.alive && bound.attempt === capturedAttempt;\n const setPhase = (phase: \"uploading\" | \"checking\" | \"done\"): void => {\n if (ownsHandle()) capture.setFlowState(phase);\n };\n const finish = (result: VerificationResult): void => {\n if (isCancelled()) return;\n emitResult(result);\n if (!ownsHandle()) return;\n if (result.status === \"retry\" && rearm === \"auto\") {\n // Re-arm: the engine restarts and the next `captured` runs a new flow.\n capture.handle.retry();\n return;\n }\n // `done` (incl. a manual-rearm `retry`): the host calls `retry()` on the\n // handle when the user is ready — the engine restarts from there.\n capture.setFlowState(\"done\");\n };\n\n if (artifacts.degraded === \"NO_RECORDER\") {\n // No MediaRecorder: nothing is submitted (no liveness, no face_match).\n const sessionId = view?.sessionId ?? (await ensureView().catch(() => null))?.sessionId ?? \"\";\n finish(unavailable(sessionId, \"no_recorder\"));\n return;\n }\n\n setPhase(\"uploading\");\n try {\n // Registry first: it records consent itself only when it actually\n // submits — an unbound / failed registry frontier ends the flow below\n // without recording a consent nothing was submitted under.\n await startRegistry();\n if (isCancelled()) return;\n let current = await ensureView();\n if (!isTerminalView(current)) {\n if (frontierStep(current)?.checkType === \"population_registry\") {\n // The registry never passed (outage after the retry, or a request-\n // level failure): the biometric checks cannot be submitted.\n finish(\n unavailable(\n current.sessionId,\n registryUnbound ? \"nni_unbound\" : registryFailed ? \"network\" : \"registry_unavailable\",\n ),\n );\n return;\n }\n await ensureConsent();\n if (isCancelled()) return;\n const outcome = await runLivenessFlow({\n ...runtime,\n artifacts,\n getAttestation,\n getView: () => view ?? current,\n refreshView,\n applySubmit,\n onPhase: setPhase,\n isCancelled,\n });\n if (isCancelled()) return;\n if (outcome.kind === \"unavailable\") {\n finish(unavailable(current.sessionId, outcome.reason));\n return;\n }\n if (outcome.kind === \"resync\") await refreshView();\n current = await settleProcessing();\n }\n finish(mapSessionResult(current, { autoRetry }));\n } catch {\n if (isCancelled()) return;\n finish(unavailable(view?.sessionId ?? \"\", \"network\"));\n }\n };\n\n const enqueueFlow = (bound: BoundCapture, artifacts: CapturedArtifacts): void => {\n const capturedAttempt = bound.attempt;\n if (chainDepth > 0 && bound.alive) {\n // Queued behind an in-flight chain: the host sees `uploading` meanwhile.\n bound.capture.setFlowState(\"uploading\");\n }\n chainDepth += 1;\n chain = chain\n .then(() => runFlow(bound, artifacts, capturedAttempt))\n .finally(() => {\n chainDepth -= 1;\n })\n // runFlow never rejects (it maps failures to results); a rejection must\n // still never poison the chain for the captures queued after it.\n .catch(() => undefined);\n };\n\n const liveness = (captureOptions: LivenessCaptureOptions): LivenessCaptureHandle => {\n // Camera teardown of the previous handle (its chain, if any, survives).\n active?.capture.handle.destroy();\n if (active !== null) active.alive = false;\n const capture = createLivenessCaptureInternal(captureOptions, internals.capture);\n const bound: BoundCapture = { capture, alive: true, attempt: 0 };\n active = bound;\n capture.handle.on(\"state\", (state) => {\n // A fresh engine attempt: in-flight flows for older artifacts complete,\n // but no longer push states into (or re-arm) this handle.\n if (state === \"starting\") bound.attempt += 1;\n });\n capture.handle.on(\"captured\", (artifacts) => {\n // Partners' own `captured` handlers run first (the event fires BEFORE\n // any upload); the flow starts on the next microtask, after any\n // in-flight chain.\n void Promise.resolve().then(() => enqueueFlow(bound, artifacts));\n });\n const inner = capture.handle;\n return {\n get state() {\n return inner.state;\n },\n on: (event, handler) => inner.on(event, handler),\n start: (startOptions) => inner.start(startOptions),\n retry: () => inner.retry(),\n switchCamera: () => inner.switchCamera(),\n destroy: () => {\n bound.alive = false;\n if (active === bound) active = null;\n inner.destroy();\n },\n };\n };\n\n return {\n consent: () => ensureConsent(),\n startRegistry,\n liveness,\n voiceClips: async () => {\n if (destroyed) return null;\n try {\n const current = await ensureView();\n // destroy() may have run while the view was loading: honor it (null, like before).\n if (destroyed) return null;\n return current.voice ?? null;\n } catch {\n return null;\n }\n },\n on: (event, handler) => emitter.on(event, handler),\n destroy: () => {\n destroyed = true;\n lifetime.abort();\n if (active !== null) {\n active.alive = false;\n active.capture.handle.destroy();\n active = null;\n }\n emitter.clear();\n },\n };\n}\n"],"mappings":"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","names":["registry"]}
|