@sekyro/web 0.1.0-rc.4 → 0.1.0-rc.5

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/dist/index.d.cts CHANGED
@@ -1,6 +1,28 @@
1
- export { C as CaptureChannel, a as CaptureEngineId, b as CaptureError, c as CaptureErrorKind, d as CaptureState, e as CapturedArtifacts, D as DEFAULT_API_ORIGIN, G as GateHint, f as GateUpdate, L as LivenessCaptureEvents, g as LivenessCaptureHandle, h as LivenessCaptureOptions, R as ReasonCode, S as SdkFailureReason, V as Verification, i as VerificationEvents, j as VerificationLocale, k as VerificationOptions, l as VerificationResult, m as VerificationStatus, n as createLivenessCapture, o as createVerification } from './create-verification-Dkb2tXuY.cjs';
1
+ export { C as CameraFacing, a as CaptureChannel, b as CaptureEngineId, c as CaptureError, d as CaptureErrorKind, e as CaptureState, f as CapturedArtifacts, D as DEFAULT_API_ORIGIN, G as GateHint, g as GateUpdate, L as LivenessAssetsStatus, h as LivenessCaptureEvents, i as LivenessCaptureHandle, j as LivenessCaptureOptions, k as LivenessStartOptions, P as PreloadLivenessAssetsOptions, R as ReasonCode, S as SdkFailureReason, V as Verification, l as VerificationEvents, m as VerificationLocale, n as VerificationOptions, o as VerificationResult, p as VerificationStatus, q as createLivenessCapture, r as createVerification, s as getLivenessAssetsStatus, t as preloadLivenessAssets, u as releaseLivenessAssets, v as subscribeLivenessAssets } from './create-verification-CA-3szPI.cjs';
2
2
  export { OpenHostedOptions, PopupBlocked, PopupHandle, SEKYRO_EVENTS, SekyroEvent, SekyroEventName, openHosted } from './popup/index.cjs';
3
3
  export { HostedApiError, KNOWN_CODES, KnownCode, isKnownCode } from './client/index.cjs';
4
4
  export { C as CheckReason, a as CheckState, b as CheckType, D as DocReason, F as FaceMatchReason, H as HostedOtpView, c as HostedSessionView, d as HostedStepView, L as LivenessReason, P as PhoneOtpReason, e as PopulationReason, S as SessionOutcome, f as SessionStatus } from './hosted-client-DWzCRYJK.cjs';
5
- import './capture-session-v3-DEInCOUg.cjs';
6
5
  import './detect-worker-CKXHlXx9.cjs';
6
+ import './capture-session-v3-UQU_WjO2.cjs';
7
+
8
+ /** Camera permission as the Permissions API reports it; `'unknown'` where it cannot tell. */
9
+ type CameraPermissionState = "granted" | "prompt" | "denied" | "unknown";
10
+ /** One-shot camera permission read. */
11
+ declare function queryCameraPermission(): Promise<CameraPermissionState>;
12
+ /**
13
+ * Subscribe to camera-permission changes; returns the unsubscribe function.
14
+ * Unlike {@link subscribeLivenessAssets}, it DOES emit the current state first
15
+ * (as soon as the Permissions API answers — asynchronously, `'unknown'` where
16
+ * unsupported), then on every change. Not a `useSyncExternalStore` source:
17
+ * there is no synchronous snapshot — drive a `useState` from it.
18
+ */
19
+ declare function subscribeCameraPermission(listener: (state: CameraPermissionState) => void): () => void;
20
+ /**
21
+ * Whether a front/back choice makes sense: phones and tablets (UA, incl. iPadOS's
22
+ * desktop UA) always have both; otherwise ≥ 2 `videoinput` devices. Before a
23
+ * permission was ever granted some desktops report a single placeholder — the
24
+ * in-capture switch remains available either way.
25
+ */
26
+ declare function hasMultipleCameras(): Promise<boolean>;
27
+
28
+ export { type CameraPermissionState, hasMultipleCameras, queryCameraPermission, subscribeCameraPermission };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,28 @@
1
- export { C as CaptureChannel, a as CaptureEngineId, b as CaptureError, c as CaptureErrorKind, d as CaptureState, e as CapturedArtifacts, D as DEFAULT_API_ORIGIN, G as GateHint, f as GateUpdate, L as LivenessCaptureEvents, g as LivenessCaptureHandle, h as LivenessCaptureOptions, R as ReasonCode, S as SdkFailureReason, V as Verification, i as VerificationEvents, j as VerificationLocale, k as VerificationOptions, l as VerificationResult, m as VerificationStatus, n as createLivenessCapture, o as createVerification } from './create-verification-Ce1y5kJR.js';
1
+ export { C as CameraFacing, a as CaptureChannel, b as CaptureEngineId, c as CaptureError, d as CaptureErrorKind, e as CaptureState, f as CapturedArtifacts, D as DEFAULT_API_ORIGIN, G as GateHint, g as GateUpdate, L as LivenessAssetsStatus, h as LivenessCaptureEvents, i as LivenessCaptureHandle, j as LivenessCaptureOptions, k as LivenessStartOptions, P as PreloadLivenessAssetsOptions, R as ReasonCode, S as SdkFailureReason, V as Verification, l as VerificationEvents, m as VerificationLocale, n as VerificationOptions, o as VerificationResult, p as VerificationStatus, q as createLivenessCapture, r as createVerification, s as getLivenessAssetsStatus, t as preloadLivenessAssets, u as releaseLivenessAssets, v as subscribeLivenessAssets } from './create-verification-c98uAxbl.js';
2
2
  export { OpenHostedOptions, PopupBlocked, PopupHandle, SEKYRO_EVENTS, SekyroEvent, SekyroEventName, openHosted } from './popup/index.js';
3
3
  export { HostedApiError, KNOWN_CODES, KnownCode, isKnownCode } from './client/index.js';
4
4
  export { C as CheckReason, a as CheckState, b as CheckType, D as DocReason, F as FaceMatchReason, H as HostedOtpView, c as HostedSessionView, d as HostedStepView, L as LivenessReason, P as PhoneOtpReason, e as PopulationReason, S as SessionOutcome, f as SessionStatus } from './hosted-client-DWzCRYJK.js';
5
- import './capture-session-v3-uXJr1Wlm.js';
6
5
  import './detect-worker-CKXHlXx9.js';
6
+ import './capture-session-v3-Okq0EQHG.js';
7
+
8
+ /** Camera permission as the Permissions API reports it; `'unknown'` where it cannot tell. */
9
+ type CameraPermissionState = "granted" | "prompt" | "denied" | "unknown";
10
+ /** One-shot camera permission read. */
11
+ declare function queryCameraPermission(): Promise<CameraPermissionState>;
12
+ /**
13
+ * Subscribe to camera-permission changes; returns the unsubscribe function.
14
+ * Unlike {@link subscribeLivenessAssets}, it DOES emit the current state first
15
+ * (as soon as the Permissions API answers — asynchronously, `'unknown'` where
16
+ * unsupported), then on every change. Not a `useSyncExternalStore` source:
17
+ * there is no synchronous snapshot — drive a `useState` from it.
18
+ */
19
+ declare function subscribeCameraPermission(listener: (state: CameraPermissionState) => void): () => void;
20
+ /**
21
+ * Whether a front/back choice makes sense: phones and tablets (UA, incl. iPadOS's
22
+ * desktop UA) always have both; otherwise ≥ 2 `videoinput` devices. Before a
23
+ * permission was ever granted some desktops report a single placeholder — the
24
+ * in-capture switch remains available either way.
25
+ */
26
+ declare function hasMultipleCameras(): Promise<boolean>;
27
+
28
+ export { type CameraPermissionState, hasMultipleCameras, queryCameraPermission, subscribeCameraPermission };
package/dist/index.js CHANGED
@@ -5,9 +5,13 @@ import {
5
5
  import {
6
6
  DEFAULT_API_ORIGIN,
7
7
  createLivenessCapture,
8
- createVerification
9
- } from "./chunk-TBANUT3X.js";
10
- import "./chunk-EWRNPKTR.js";
8
+ createVerification,
9
+ getLivenessAssetsStatus,
10
+ preloadLivenessAssets,
11
+ releaseLivenessAssets,
12
+ subscribeLivenessAssets
13
+ } from "./chunk-SGDXLAXR.js";
14
+ import "./chunk-TG5IE552.js";
11
15
  import {
12
16
  HostedApiError,
13
17
  KNOWN_CODES,
@@ -15,6 +19,52 @@ import {
15
19
  } from "./chunk-YVFF4Q4G.js";
16
20
  import "./chunk-NWF46H7K.js";
17
21
  import "./chunk-MWNFSNMP.js";
22
+
23
+ // src/capture/camera-probe.ts
24
+ async function permissionStatus() {
25
+ try {
26
+ if (typeof navigator === "undefined" || typeof navigator.permissions?.query !== "function") return null;
27
+ return await navigator.permissions.query({ name: "camera" });
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function asState(state) {
33
+ return state === "granted" || state === "prompt" || state === "denied" ? state : "unknown";
34
+ }
35
+ async function queryCameraPermission() {
36
+ const status = await permissionStatus();
37
+ return status === null ? "unknown" : asState(status.state);
38
+ }
39
+ function subscribeCameraPermission(listener) {
40
+ let stopped = false;
41
+ let status = null;
42
+ const onChange = () => {
43
+ if (!stopped && status !== null) listener(asState(status.state));
44
+ };
45
+ void permissionStatus().then((resolved) => {
46
+ if (stopped) return;
47
+ status = resolved;
48
+ listener(resolved === null ? "unknown" : asState(resolved.state));
49
+ resolved?.addEventListener?.("change", onChange);
50
+ });
51
+ return () => {
52
+ stopped = true;
53
+ status?.removeEventListener?.("change", onChange);
54
+ };
55
+ }
56
+ async function hasMultipleCameras() {
57
+ if (typeof navigator === "undefined") return false;
58
+ const ua = String(navigator.userAgent ?? "");
59
+ if (/Android|iPhone|iPad|iPod/i.test(ua)) return true;
60
+ if (/Macintosh/.test(ua) && (navigator.maxTouchPoints ?? 0) > 1) return true;
61
+ try {
62
+ const devices = await navigator.mediaDevices?.enumerateDevices?.() ?? [];
63
+ return devices.filter((device) => device.kind === "videoinput").length >= 2;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
18
68
  export {
19
69
  DEFAULT_API_ORIGIN,
20
70
  HostedApiError,
@@ -22,7 +72,14 @@ export {
22
72
  SEKYRO_EVENTS,
23
73
  createLivenessCapture,
24
74
  createVerification,
75
+ getLivenessAssetsStatus,
76
+ hasMultipleCameras,
25
77
  isKnownCode,
26
- openHosted
78
+ openHosted,
79
+ preloadLivenessAssets,
80
+ queryCameraPermission,
81
+ releaseLivenessAssets,
82
+ subscribeCameraPermission,
83
+ subscribeLivenessAssets
27
84
  };
28
85
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/capture/camera-probe.ts"],"sourcesContent":["// Camera probes for pre-capture UI (spec D6, D7). Every function is SSR-safe and\n// never throws: a missing / throwing API (Safari < 16 on {name:'camera'},\n// Firefox) degrades to 'unknown' / false.\n\n/** Camera permission as the Permissions API reports it; `'unknown'` where it cannot tell. */\nexport type CameraPermissionState = \"granted\" | \"prompt\" | \"denied\" | \"unknown\";\n\nasync function permissionStatus(): Promise<PermissionStatus | null> {\n try {\n if (typeof navigator === \"undefined\" || typeof navigator.permissions?.query !== \"function\") return null;\n return await navigator.permissions.query({ name: \"camera\" as PermissionName });\n } catch {\n return null;\n }\n}\n\nfunction asState(state: string): CameraPermissionState {\n return state === \"granted\" || state === \"prompt\" || state === \"denied\" ? state : \"unknown\";\n}\n\n/** One-shot camera permission read. */\nexport async function queryCameraPermission(): Promise<CameraPermissionState> {\n const status = await permissionStatus();\n return status === null ? \"unknown\" : asState(status.state);\n}\n\n/**\n * Subscribe to camera-permission changes; returns the unsubscribe function.\n * Unlike {@link subscribeLivenessAssets}, it DOES emit the current state first\n * (as soon as the Permissions API answers — asynchronously, `'unknown'` where\n * unsupported), then on every change. Not a `useSyncExternalStore` source:\n * there is no synchronous snapshot — drive a `useState` from it.\n */\nexport function subscribeCameraPermission(listener: (state: CameraPermissionState) => void): () => void {\n let stopped = false;\n let status: PermissionStatus | null = null;\n const onChange = (): void => {\n if (!stopped && status !== null) listener(asState(status.state));\n };\n void permissionStatus().then((resolved) => {\n if (stopped) return;\n status = resolved;\n listener(resolved === null ? \"unknown\" : asState(resolved.state));\n resolved?.addEventListener?.(\"change\", onChange);\n });\n return () => {\n stopped = true;\n status?.removeEventListener?.(\"change\", onChange);\n };\n}\n\n/**\n * Whether a front/back choice makes sense: phones and tablets (UA, incl. iPadOS's\n * desktop UA) always have both; otherwise ≥ 2 `videoinput` devices. Before a\n * permission was ever granted some desktops report a single placeholder — the\n * in-capture switch remains available either way.\n */\nexport async function hasMultipleCameras(): Promise<boolean> {\n if (typeof navigator === \"undefined\") return false;\n const ua = String(navigator.userAgent ?? \"\");\n if (/Android|iPhone|iPad|iPod/i.test(ua)) return true;\n if (/Macintosh/.test(ua) && (navigator.maxTouchPoints ?? 0) > 1) return true;\n try {\n const devices = (await navigator.mediaDevices?.enumerateDevices?.()) ?? [];\n return devices.filter((device) => device.kind === \"videoinput\").length >= 2;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAOA,eAAe,mBAAqD;AAClE,MAAI;AACF,QAAI,OAAO,cAAc,eAAe,OAAO,UAAU,aAAa,UAAU,WAAY,QAAO;AACnG,WAAO,MAAM,UAAU,YAAY,MAAM,EAAE,MAAM,SAA2B,CAAC;AAAA,EAC/E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,OAAsC;AACrD,SAAO,UAAU,aAAa,UAAU,YAAY,UAAU,WAAW,QAAQ;AACnF;AAGA,eAAsB,wBAAwD;AAC5E,QAAM,SAAS,MAAM,iBAAiB;AACtC,SAAO,WAAW,OAAO,YAAY,QAAQ,OAAO,KAAK;AAC3D;AASO,SAAS,0BAA0B,UAA8D;AACtG,MAAI,UAAU;AACd,MAAI,SAAkC;AACtC,QAAM,WAAW,MAAY;AAC3B,QAAI,CAAC,WAAW,WAAW,KAAM,UAAS,QAAQ,OAAO,KAAK,CAAC;AAAA,EACjE;AACA,OAAK,iBAAiB,EAAE,KAAK,CAAC,aAAa;AACzC,QAAI,QAAS;AACb,aAAS;AACT,aAAS,aAAa,OAAO,YAAY,QAAQ,SAAS,KAAK,CAAC;AAChE,cAAU,mBAAmB,UAAU,QAAQ;AAAA,EACjD,CAAC;AACD,SAAO,MAAM;AACX,cAAU;AACV,YAAQ,sBAAsB,UAAU,QAAQ;AAAA,EAClD;AACF;AAQA,eAAsB,qBAAuC;AAC3D,MAAI,OAAO,cAAc,YAAa,QAAO;AAC7C,QAAM,KAAK,OAAO,UAAU,aAAa,EAAE;AAC3C,MAAI,4BAA4B,KAAK,EAAE,EAAG,QAAO;AACjD,MAAI,YAAY,KAAK,EAAE,MAAM,UAAU,kBAAkB,KAAK,EAAG,QAAO;AACxE,MAAI;AACF,UAAM,UAAW,MAAM,UAAU,cAAc,mBAAmB,KAAM,CAAC;AACzE,WAAO,QAAQ,OAAO,CAAC,WAAW,OAAO,SAAS,YAAY,EAAE,UAAU;AAAA,EAC5E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1105,6 +1105,8 @@ __export(internal_exports, {
1105
1105
  IN_CIRCLE_FACTOR: () => IN_CIRCLE_FACTOR,
1106
1106
  MRZ_BAND_DEFAULTS: () => MRZ_BAND_DEFAULTS,
1107
1107
  MR_DOC_CATALOG: () => MR_DOC_CATALOG,
1108
+ PRELOAD_BLAZE_INIT_TIMEOUT_MS: () => PRELOAD_BLAZE_INIT_TIMEOUT_MS,
1109
+ PRELOAD_BUDGET_MS: () => PRELOAD_BUDGET_MS,
1108
1110
  PROCESSING_POLL_DELAY_MS: () => PROCESSING_POLL_DELAY_MS,
1109
1111
  PROCESSING_POLL_MAX: () => PROCESSING_POLL_MAX,
1110
1112
  REGISTRY_RETRY_DELAY_MS: () => REGISTRY_RETRY_DELAY_MS,
@@ -1129,14 +1131,17 @@ __export(internal_exports, {
1129
1131
  mapGuidanceHint: () => mapGuidanceHint,
1130
1132
  mapSessionResult: () => mapSessionResult,
1131
1133
  mergeSessionView: () => mergeSessionView,
1134
+ preloadedDetectorFactory: () => preloadedDetectorFactory,
1132
1135
  quadAreaFrac: () => quadAreaFrac,
1133
1136
  quadBounds: () => quadBounds,
1134
1137
  quadIou: () => quadIou,
1135
1138
  quadTiltRatio: () => quadTiltRatio,
1136
1139
  randomUuid: () => randomUuid,
1140
+ resetLivenessAssetsForTests: () => resetLivenessAssetsForTests,
1137
1141
  resetTurnstileForTests: () => resetTurnstileForTests,
1138
1142
  runLivenessFlow: () => runLivenessFlow,
1139
1143
  runSubmission: () => runSubmission,
1144
+ setPreloadLoaderForTests: () => setPreloadLoaderForTests,
1140
1145
  specularFraction: () => specularFraction,
1141
1146
  stepOf: () => stepOf,
1142
1147
  warmDocDetector: () => warmDocDetector
@@ -2707,10 +2712,15 @@ var CaptureSessionV3 = class {
2707
2712
  handler(payload);
2708
2713
  }
2709
2714
  }
2710
- async start() {
2715
+ async start(options = {}) {
2716
+ if (options.facingMode !== void 0) this.facing = options.facingMode;
2711
2717
  this.attempt += 1;
2712
2718
  await this.run(this.attempt);
2713
2719
  }
2720
+ /** The camera the current/next attempt requests (start option, switchCamera toggles). */
2721
+ get facingMode() {
2722
+ return this.facing;
2723
+ }
2714
2724
  /** Full reset + camera restart — the documented recovery path (v1 parity). */
2715
2725
  retry() {
2716
2726
  this.teardownAttempt();
@@ -3118,6 +3128,179 @@ function createEmitter() {
3118
3128
  };
3119
3129
  }
3120
3130
 
3131
+ // src/capture/preload.ts
3132
+ var PRELOAD_BLAZE_INIT_TIMEOUT_MS = 12e4;
3133
+ var PRELOAD_BUDGET_MS = 13e4;
3134
+ var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@sekyro/web.preload.v1");
3135
+ function registry() {
3136
+ const host = globalThis;
3137
+ host[REGISTRY_KEY] ?? (host[REGISTRY_KEY] = /* @__PURE__ */ new Map());
3138
+ return host[REGISTRY_KEY];
3139
+ }
3140
+ function normalizeBase(assetBaseUrl) {
3141
+ const base = (assetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL).replace(/\/+$/, "");
3142
+ return base === "" ? "/" : base;
3143
+ }
3144
+ function setStatus(entry, next) {
3145
+ if (entry.status === next) return;
3146
+ entry.status = next;
3147
+ for (const listener of [...entry.listeners]) listener(next);
3148
+ }
3149
+ var defaultLoader = (base) => createDetector({ preferred: ["blaze"], v3AssetBaseUrl: base, blazeInitTimeoutMs: PRELOAD_BLAZE_INIT_TIMEOUT_MS });
3150
+ var loader = defaultLoader;
3151
+ function setPreloadLoaderForTests(next) {
3152
+ loader = next ?? defaultLoader;
3153
+ }
3154
+ function resetLivenessAssetsForTests() {
3155
+ for (const entry of registry().values()) entry.detector?.destroy();
3156
+ registry().clear();
3157
+ loader = defaultLoader;
3158
+ }
3159
+ function begin(entry, base) {
3160
+ if ((entry.status === "loading" || entry.status === "ready") && entry.promise !== null) return entry.promise;
3161
+ setStatus(entry, "loading");
3162
+ const detectorPromise = new Promise((resolve) => resolve(loader(base)));
3163
+ entry.detectorPromise = detectorPromise;
3164
+ let timer;
3165
+ const budget = new Promise((_2, reject) => {
3166
+ timer = setTimeout(() => {
3167
+ detectorPromise.then((late) => late.destroy(), () => void 0);
3168
+ reject(new Error(`detector preload exceeded ${PRELOAD_BUDGET_MS} ms`));
3169
+ }, PRELOAD_BUDGET_MS);
3170
+ });
3171
+ const settled = Promise.race([detectorPromise, budget]).then(
3172
+ (detector) => {
3173
+ if (entry.detectorPromise !== detectorPromise) {
3174
+ detector.destroy();
3175
+ return entry.status;
3176
+ }
3177
+ entry.detector = detector;
3178
+ setStatus(entry, "ready");
3179
+ return "ready";
3180
+ },
3181
+ (err) => {
3182
+ if (entry.detectorPromise === detectorPromise) {
3183
+ entry.detectorPromise = null;
3184
+ entry.detector = null;
3185
+ setStatus(entry, "failed");
3186
+ console.warn("[@sekyro/web] face-detector preload failed:", err);
3187
+ }
3188
+ return entry.status;
3189
+ }
3190
+ );
3191
+ const promise = settled.finally(() => clearTimeout(timer));
3192
+ entry.promise = promise;
3193
+ entry.scheduled?.settle(promise);
3194
+ return promise;
3195
+ }
3196
+ function nonOwningView(shared) {
3197
+ return {
3198
+ get tier() {
3199
+ return shared.tier;
3200
+ },
3201
+ get fallbackReason() {
3202
+ return shared.fallbackReason;
3203
+ },
3204
+ detect: (...args) => shared.detect(...args),
3205
+ destroy: () => {
3206
+ }
3207
+ };
3208
+ }
3209
+ function preloadedDetectorFactory(assetBaseUrl) {
3210
+ const base = normalizeBase(assetBaseUrl);
3211
+ const entry = registry().get(base);
3212
+ if (entry === void 0 || entry.detector === null && entry.detectorPromise === null && entry.scheduled === null) {
3213
+ return void 0;
3214
+ }
3215
+ return async () => {
3216
+ if (entry.scheduled !== null) void begin(entry, base);
3217
+ let waitingOn = entry.promise;
3218
+ while (entry.detector === null && waitingOn !== null) {
3219
+ await waitingOn;
3220
+ if (entry.promise === waitingOn) break;
3221
+ waitingOn = entry.promise;
3222
+ }
3223
+ return entry.detector !== null ? nonOwningView(entry.detector) : createDetector({ v3AssetBaseUrl: base });
3224
+ };
3225
+ }
3226
+
3227
+ // src/capture/wake-lock.ts
3228
+ function wakeLockApi() {
3229
+ if (typeof navigator === "undefined" || !("wakeLock" in navigator)) return null;
3230
+ const api = navigator.wakeLock;
3231
+ return typeof api?.request === "function" ? api : null;
3232
+ }
3233
+ function isVisible() {
3234
+ return typeof document === "undefined" || document.visibilityState === "visible";
3235
+ }
3236
+ function releaseQuietly(sentinel) {
3237
+ try {
3238
+ void sentinel.release().catch(() => void 0);
3239
+ } catch {
3240
+ }
3241
+ }
3242
+ function createScreenWakeLock(options = {}) {
3243
+ if (options.enabled === false) return { setActive: () => void 0 };
3244
+ let wanted = false;
3245
+ let sentinel = null;
3246
+ let inFlight = false;
3247
+ let listening = false;
3248
+ const acquire = () => {
3249
+ if (!wanted || inFlight || sentinel !== null && !sentinel.released) return;
3250
+ const api = wakeLockApi();
3251
+ if (api === null || !isVisible()) return;
3252
+ let request;
3253
+ try {
3254
+ request = api.request("screen");
3255
+ } catch {
3256
+ return;
3257
+ }
3258
+ inFlight = true;
3259
+ request.then(
3260
+ (granted) => {
3261
+ inFlight = false;
3262
+ if (!wanted) {
3263
+ releaseQuietly(granted);
3264
+ return;
3265
+ }
3266
+ sentinel = granted;
3267
+ granted.addEventListener?.("release", () => {
3268
+ if (sentinel === granted) sentinel = null;
3269
+ });
3270
+ },
3271
+ () => {
3272
+ inFlight = false;
3273
+ }
3274
+ );
3275
+ };
3276
+ const onVisibilityChange = () => {
3277
+ if (isVisible()) acquire();
3278
+ };
3279
+ const listen = (on) => {
3280
+ if (typeof document === "undefined" || on === listening) return;
3281
+ listening = on;
3282
+ if (on) document.addEventListener("visibilitychange", onVisibilityChange);
3283
+ else document.removeEventListener("visibilitychange", onVisibilityChange);
3284
+ };
3285
+ return {
3286
+ setActive: (active) => {
3287
+ wanted = active;
3288
+ if (active) {
3289
+ if (wakeLockApi() === null) return;
3290
+ listen(true);
3291
+ acquire();
3292
+ return;
3293
+ }
3294
+ listen(false);
3295
+ if (sentinel !== null) {
3296
+ const held = sentinel;
3297
+ sentinel = null;
3298
+ releaseQuietly(held);
3299
+ }
3300
+ }
3301
+ };
3302
+ }
3303
+
3121
3304
  // src/capture/create-liveness-capture.ts
3122
3305
  function mapGuidanceHint(key) {
3123
3306
  switch (key) {
@@ -3181,12 +3364,22 @@ function createLivenessCaptureInternal(options, internals = {}) {
3181
3364
  const now = internals.now ?? (() => performance.now());
3182
3365
  const emitter = createEmitter();
3183
3366
  const degraded = !isMediaRecorderSupported();
3367
+ const hostSeams = internals.engineOptions;
3368
+ const preloadAware = hostSeams?.createDetector === void 0 && hostSeams?.createWorker === void 0 ? {
3369
+ createDetector: () => {
3370
+ const warmed = preloadedDetectorFactory(options.assetBaseUrl);
3371
+ return warmed !== void 0 ? warmed() : createDetector(
3372
+ options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {}
3373
+ );
3374
+ }
3375
+ } : {};
3184
3376
  const engineOptions = {
3185
3377
  video: options.video,
3186
3378
  mode: degraded ? "photo" : "clip",
3187
3379
  ...options.facing !== void 0 ? { facingMode: options.facing } : {},
3188
3380
  ...options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {},
3189
- ...internals.engineOptions ?? {}
3381
+ ...internals.engineOptions ?? {},
3382
+ ...preloadAware
3190
3383
  };
3191
3384
  const engine = internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);
3192
3385
  let state = "idle";
@@ -3204,8 +3397,10 @@ function createLivenessCaptureInternal(options, internals = {}) {
3204
3397
  frameHeight = options.video.videoHeight;
3205
3398
  }
3206
3399
  };
3400
+ const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });
3207
3401
  const setState = (next) => {
3208
3402
  state = next;
3403
+ wakeLock.setActive(next === "starting" || next === "gating" || next === "recording");
3209
3404
  emitter.emit("state", next);
3210
3405
  };
3211
3406
  const emitGate = () => emitter.emit("gate", { progress, hint, faceDetected });
@@ -3279,11 +3474,12 @@ function createLivenessCaptureInternal(options, internals = {}) {
3279
3474
  return state;
3280
3475
  },
3281
3476
  on: (event, handler) => emitter.on(event, handler),
3282
- start: () => engine.start(),
3477
+ start: (startOptions) => engine.start(startOptions?.facing !== void 0 ? { facingMode: startOptions.facing } : {}),
3283
3478
  retry: () => engine.retry(),
3284
3479
  switchCamera: () => engine.switchCamera(),
3285
3480
  destroy: () => {
3286
3481
  for (const unsubscribe of unsubscribes) unsubscribe();
3482
+ wakeLock.setActive(false);
3287
3483
  engine.destroy();
3288
3484
  emitter.clear();
3289
3485
  }
@@ -3991,7 +4187,7 @@ function createVerificationInternal(options, internals) {
3991
4187
  }
3992
4188
  emitter.emit("result", result);
3993
4189
  };
3994
- let registry = null;
4190
+ let registry2 = null;
3995
4191
  let registryFailed = false;
3996
4192
  let registryUnbound = false;
3997
4193
  const submitRegistry = () => runSubmission(
@@ -4026,8 +4222,8 @@ function createVerificationInternal(options, internals) {
4026
4222
  if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));
4027
4223
  };
4028
4224
  const startRegistry = () => {
4029
- registry ?? (registry = runRegistry());
4030
- return registry;
4225
+ registry2 ?? (registry2 = runRegistry());
4226
+ return registry2;
4031
4227
  };
4032
4228
  const getAttestation = async () => {
4033
4229
  const current = await ensureView();
@@ -4142,7 +4338,7 @@ function createVerificationInternal(options, internals) {
4142
4338
  return inner.state;
4143
4339
  },
4144
4340
  on: (event, handler) => inner.on(event, handler),
4145
- start: () => inner.start(),
4341
+ start: (startOptions) => inner.start(startOptions),
4146
4342
  retry: () => inner.retry(),
4147
4343
  switchCamera: () => inner.switchCamera(),
4148
4344
  destroy: () => {
@@ -4189,6 +4385,8 @@ function createVerificationInternal(options, internals) {
4189
4385
  IN_CIRCLE_FACTOR,
4190
4386
  MRZ_BAND_DEFAULTS,
4191
4387
  MR_DOC_CATALOG,
4388
+ PRELOAD_BLAZE_INIT_TIMEOUT_MS,
4389
+ PRELOAD_BUDGET_MS,
4192
4390
  PROCESSING_POLL_DELAY_MS,
4193
4391
  PROCESSING_POLL_MAX,
4194
4392
  REGISTRY_RETRY_DELAY_MS,
@@ -4213,14 +4411,17 @@ function createVerificationInternal(options, internals) {
4213
4411
  mapGuidanceHint,
4214
4412
  mapSessionResult,
4215
4413
  mergeSessionView,
4414
+ preloadedDetectorFactory,
4216
4415
  quadAreaFrac,
4217
4416
  quadBounds,
4218
4417
  quadIou,
4219
4418
  quadTiltRatio,
4220
4419
  randomUuid,
4420
+ resetLivenessAssetsForTests,
4221
4421
  resetTurnstileForTests,
4222
4422
  runLivenessFlow,
4223
4423
  runSubmission,
4424
+ setPreloadLoaderForTests,
4224
4425
  specularFraction,
4225
4426
  stepOf,
4226
4427
  warmDocDetector