@sekyro/web 0.1.0-rc.6 → 0.1.0-rc.8

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.
Files changed (118) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/LICENSE +25 -16
  3. package/README.md +6 -8
  4. package/THIRD-PARTY-NOTICES.txt +154 -0
  5. package/dist/background-monitor-A7FKJOAG.js +1 -0
  6. package/dist/capture-session-doc-YBE3UMJJ.js +1 -0
  7. package/dist/{capture-session-v3-Okq0EQHG.d.ts → capture-session-v3-CQK-G-qX.d.ts} +72 -3
  8. package/dist/{capture-session-v3-UQU_WjO2.d.cts → capture-session-v3-Dvob-uH2.d.cts} +72 -3
  9. package/dist/chunk-4RS2YIL4.js +1 -0
  10. package/dist/chunk-7V2M6Y2W.js +1 -0
  11. package/dist/chunk-7ZCZY3ZP.js +1 -0
  12. package/dist/chunk-A3FUGHAH.js +2 -0
  13. package/dist/chunk-BPE5NSQ2.js +1 -0
  14. package/dist/chunk-CJHMFPL6.js +1 -0
  15. package/dist/chunk-DFTCAWMP.js +1 -0
  16. package/dist/chunk-DK5YPBDQ.js +1 -0
  17. package/dist/chunk-DQWW4OCK.js +1 -0
  18. package/dist/chunk-FHJ7HO3M.js +1 -0
  19. package/dist/chunk-FPXL63KZ.js +1 -0
  20. package/dist/chunk-G6LKLN4U.js +1 -0
  21. package/dist/chunk-GI3UFGRQ.js +1 -0
  22. package/dist/chunk-LQ7C44JG.js +1 -0
  23. package/dist/chunk-M43ET4NJ.js +1 -0
  24. package/dist/chunk-MXWLCDFX.js +1 -0
  25. package/dist/chunk-MY6EXEL2.js +1 -0
  26. package/dist/chunk-O5WKNZ2C.js +1 -0
  27. package/dist/chunk-OKWG3PVH.js +1 -0
  28. package/dist/chunk-POIQGEZM.js +1 -0
  29. package/dist/chunk-RI3E7GBN.js +1 -0
  30. package/dist/chunk-RNRFHUJA.js +1 -0
  31. package/dist/chunk-RVHAKRAI.js +1 -0
  32. package/dist/chunk-SMKCBUJL.js +1 -0
  33. package/dist/chunk-SXZCL5NO.js +1 -0
  34. package/dist/chunk-TDLIXFDT.js +1 -0
  35. package/dist/chunk-XLN5OT25.js +1 -0
  36. package/dist/chunk-YDQBVWXG.js +1 -0
  37. package/dist/chunk-YPG7CB4O.js +1 -0
  38. package/dist/client/index.cjs +1 -357
  39. package/dist/client/index.d.cts +13 -3
  40. package/dist/client/index.d.ts +13 -3
  41. package/dist/client/index.js +1 -15
  42. package/dist/core/index.cjs +1 -1639
  43. package/dist/core/index.d.cts +3 -3
  44. package/dist/core/index.d.ts +3 -3
  45. package/dist/core/index.js +1 -139
  46. package/dist/core/ocr/worker.bundle.js +42 -0
  47. package/dist/core/ocr/worker.cjs +42 -0
  48. package/dist/core/ocr/worker.d.cts +84 -0
  49. package/dist/core/ocr/worker.d.ts +84 -0
  50. package/dist/core/ocr/worker.js +41 -0
  51. package/dist/core/v3/detect-worker.cjs +1 -392
  52. package/dist/core/v3/detect-worker.d.cts +1 -1
  53. package/dist/core/v3/detect-worker.d.ts +1 -1
  54. package/dist/core/v3/detect-worker.js +1 -13
  55. package/dist/create-document-capture-T4APHJMY.js +1 -0
  56. package/dist/create-portrait-capture-B57GE5FU.js +1 -0
  57. package/dist/create-verification-C0AVIcVW.d.ts +2787 -0
  58. package/dist/create-verification-DoFpgzok.d.cts +2787 -0
  59. package/dist/{detect-worker-CKXHlXx9.d.cts → detect-worker-DclNxkZT.d.cts} +6 -0
  60. package/dist/{detect-worker-CKXHlXx9.d.ts → detect-worker-DclNxkZT.d.ts} +6 -0
  61. package/dist/detector-scanic-YCDNLCGV.js +1 -0
  62. package/dist/icao-gate-D8qm1O2t.d.cts +1655 -0
  63. package/dist/icao-gate-D8qm1O2t.d.ts +1655 -0
  64. package/dist/icao-gate-PWBMKRGC.js +1 -0
  65. package/dist/icao-tuning-4GGVX6B4.js +1 -0
  66. package/dist/index.cjs +1 -3030
  67. package/dist/index.d.cts +52 -6
  68. package/dist/index.d.ts +52 -6
  69. package/dist/index.js +1 -85
  70. package/dist/internal/index.cjs +1 -4439
  71. package/dist/internal/index.d.cts +352 -312
  72. package/dist/internal/index.d.ts +352 -312
  73. package/dist/internal/index.js +1 -657
  74. package/dist/keyframe-rank-YAJLYJYF.js +1 -0
  75. package/dist/landmarker-KB7LVXAU.js +1 -0
  76. package/dist/mrz-band-VYHOTDHY.js +1 -0
  77. package/dist/plan-runner-UBK4B4CG.js +1 -0
  78. package/dist/popup/index.cjs +1 -76
  79. package/dist/popup/index.js +1 -11
  80. package/dist/scanic-ml-stub-KW7IZHUT.js +1 -0
  81. package/dist/segmenter-USLNBX65.js +1 -0
  82. package/dist/turnstile-FATNZ4AG.js +1 -0
  83. package/package.json +25 -10
  84. package/sbom.cdx.json +195 -0
  85. package/dist/chunk-MWNFSNMP.js +0 -52
  86. package/dist/chunk-MWNFSNMP.js.map +0 -1
  87. package/dist/chunk-NILNKLK6.js +0 -50
  88. package/dist/chunk-NILNKLK6.js.map +0 -1
  89. package/dist/chunk-NWF46H7K.js +0 -359
  90. package/dist/chunk-NWF46H7K.js.map +0 -1
  91. package/dist/chunk-R3SJQHYU.js +0 -1173
  92. package/dist/chunk-R3SJQHYU.js.map +0 -1
  93. package/dist/chunk-TG5IE552.js +0 -1128
  94. package/dist/chunk-TG5IE552.js.map +0 -1
  95. package/dist/chunk-WY6U7AIK.js +0 -193
  96. package/dist/chunk-WY6U7AIK.js.map +0 -1
  97. package/dist/chunk-YVFF4Q4G.js +0 -327
  98. package/dist/chunk-YVFF4Q4G.js.map +0 -1
  99. package/dist/client/index.cjs.map +0 -1
  100. package/dist/client/index.js.map +0 -1
  101. package/dist/core/index.cjs.map +0 -1
  102. package/dist/core/index.js.map +0 -1
  103. package/dist/core/v3/detect-worker.cjs.map +0 -1
  104. package/dist/core/v3/detect-worker.js.map +0 -1
  105. package/dist/create-verification-BKc9NFQj.d.cts +0 -455
  106. package/dist/create-verification-DjcMZHAQ.d.ts +0 -455
  107. package/dist/detector-scanic-4C6IEW2K.js +0 -787
  108. package/dist/detector-scanic-4C6IEW2K.js.map +0 -1
  109. package/dist/hosted-client-DJXq_CB9.d.cts +0 -449
  110. package/dist/hosted-client-DJXq_CB9.d.ts +0 -449
  111. package/dist/index.cjs.map +0 -1
  112. package/dist/index.js.map +0 -1
  113. package/dist/internal/index.cjs.map +0 -1
  114. package/dist/internal/index.js.map +0 -1
  115. package/dist/popup/index.cjs.map +0 -1
  116. package/dist/popup/index.js.map +0 -1
  117. package/dist/scanic-ml-stub-S2OZ6HEF.js +0 -10
  118. package/dist/scanic-ml-stub-S2OZ6HEF.js.map +0 -1
@@ -1,1173 +0,0 @@
1
- import {
2
- CaptureSessionV3,
3
- detectIsMobile,
4
- isMediaRecorderSupported
5
- } from "./chunk-TG5IE552.js";
6
- import {
7
- HostedApiError,
8
- HostedClient,
9
- stripMimeParams
10
- } from "./chunk-YVFF4Q4G.js";
11
- import {
12
- DEFAULT_V3_ASSET_BASE_URL,
13
- createDetector
14
- } from "./chunk-NWF46H7K.js";
15
-
16
- // src/capture/preload.ts
17
- var PRELOAD_BLAZE_INIT_TIMEOUT_MS = 12e4;
18
- var PRELOAD_BUDGET_MS = 13e4;
19
- var IDLE_TIMEOUT_MS = 3e3;
20
- var IDLE_FALLBACK_MS = 1200;
21
- var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@sekyro/web.preload.v1");
22
- function registry() {
23
- const host = globalThis;
24
- host[REGISTRY_KEY] ?? (host[REGISTRY_KEY] = /* @__PURE__ */ new Map());
25
- return host[REGISTRY_KEY];
26
- }
27
- function normalizeBase(assetBaseUrl) {
28
- const base = (assetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL).replace(/\/+$/, "");
29
- return base === "" ? "/" : base;
30
- }
31
- function entryFor(base) {
32
- const map = registry();
33
- let entry = map.get(base);
34
- if (entry === void 0) {
35
- entry = { status: "idle", promise: null, scheduled: null, detectorPromise: null, detector: null, listeners: /* @__PURE__ */ new Set() };
36
- map.set(base, entry);
37
- }
38
- return entry;
39
- }
40
- function setStatus(entry, next) {
41
- if (entry.status === next) return;
42
- entry.status = next;
43
- for (const listener of [...entry.listeners]) listener(next);
44
- }
45
- var defaultLoader = (base) => createDetector({ preferred: ["blaze"], v3AssetBaseUrl: base, blazeInitTimeoutMs: PRELOAD_BLAZE_INIT_TIMEOUT_MS });
46
- var loader = defaultLoader;
47
- function setPreloadLoaderForTests(next) {
48
- loader = next ?? defaultLoader;
49
- }
50
- function resetLivenessAssetsForTests() {
51
- for (const entry of registry().values()) entry.detector?.destroy();
52
- registry().clear();
53
- loader = defaultLoader;
54
- }
55
- function canCapture() {
56
- return typeof window !== "undefined" && typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
57
- }
58
- function saveDataOn() {
59
- return navigator.connection?.saveData === true;
60
- }
61
- function scheduleIdle(run) {
62
- const w = window;
63
- if (typeof w.requestIdleCallback === "function") {
64
- const handle = w.requestIdleCallback(run, { timeout: IDLE_TIMEOUT_MS });
65
- return () => w.cancelIdleCallback?.(handle);
66
- }
67
- const timer = setTimeout(run, IDLE_FALLBACK_MS);
68
- return () => clearTimeout(timer);
69
- }
70
- function schedule(entry, base) {
71
- let resolve;
72
- const promise = new Promise((res) => {
73
- resolve = res;
74
- });
75
- let done = false;
76
- let cancel = () => void 0;
77
- const scheduled = {
78
- promise,
79
- settle: (outcome) => {
80
- if (done) return;
81
- done = true;
82
- cancel();
83
- if (entry.scheduled === scheduled) entry.scheduled = null;
84
- resolve(outcome);
85
- }
86
- };
87
- entry.scheduled = scheduled;
88
- cancel = scheduleIdle(() => {
89
- if (done) return;
90
- scheduled.settle(begin(entry, base));
91
- });
92
- return promise;
93
- }
94
- function begin(entry, base) {
95
- if ((entry.status === "loading" || entry.status === "ready") && entry.promise !== null) return entry.promise;
96
- setStatus(entry, "loading");
97
- const detectorPromise = new Promise((resolve) => resolve(loader(base)));
98
- entry.detectorPromise = detectorPromise;
99
- let timer;
100
- const budget = new Promise((_, reject) => {
101
- timer = setTimeout(() => {
102
- detectorPromise.then((late) => late.destroy(), () => void 0);
103
- reject(new Error(`detector preload exceeded ${PRELOAD_BUDGET_MS} ms`));
104
- }, PRELOAD_BUDGET_MS);
105
- });
106
- const settled = Promise.race([detectorPromise, budget]).then(
107
- (detector) => {
108
- if (entry.detectorPromise !== detectorPromise) {
109
- detector.destroy();
110
- return entry.status;
111
- }
112
- entry.detector = detector;
113
- setStatus(entry, "ready");
114
- return "ready";
115
- },
116
- (err) => {
117
- if (entry.detectorPromise === detectorPromise) {
118
- entry.detectorPromise = null;
119
- entry.detector = null;
120
- setStatus(entry, "failed");
121
- console.warn("[@sekyro/web] face-detector preload failed:", err);
122
- }
123
- return entry.status;
124
- }
125
- );
126
- const promise = settled.finally(() => clearTimeout(timer));
127
- entry.promise = promise;
128
- entry.scheduled?.settle(promise);
129
- return promise;
130
- }
131
- function preloadLivenessAssets(options = {}) {
132
- if (!canCapture()) return Promise.resolve("idle");
133
- const base = normalizeBase(options.assetBaseUrl);
134
- const entry = entryFor(base);
135
- if (options.respectSaveData === true && saveDataOn()) return Promise.resolve(entry.status);
136
- if ((entry.status === "loading" || entry.status === "ready") && entry.promise !== null) return entry.promise;
137
- if (options.idle === true) return entry.scheduled?.promise ?? schedule(entry, base);
138
- return begin(entry, base);
139
- }
140
- function getLivenessAssetsStatus(assetBaseUrl) {
141
- return registry().get(normalizeBase(assetBaseUrl))?.status ?? "idle";
142
- }
143
- function subscribeLivenessAssets(listener, assetBaseUrl) {
144
- const entry = entryFor(normalizeBase(assetBaseUrl));
145
- entry.listeners.add(listener);
146
- return () => {
147
- entry.listeners.delete(listener);
148
- };
149
- }
150
- function releaseLivenessAssets(assetBaseUrl) {
151
- const entry = registry().get(normalizeBase(assetBaseUrl));
152
- if (entry === void 0) return;
153
- entry.scheduled?.settle("idle");
154
- entry.detector?.destroy();
155
- entry.detector = null;
156
- entry.detectorPromise = null;
157
- entry.promise = null;
158
- setStatus(entry, "idle");
159
- }
160
- function nonOwningView(shared) {
161
- return {
162
- get tier() {
163
- return shared.tier;
164
- },
165
- get fallbackReason() {
166
- return shared.fallbackReason;
167
- },
168
- detect: (...args) => shared.detect(...args),
169
- destroy: () => {
170
- }
171
- };
172
- }
173
- function preloadedDetectorFactory(assetBaseUrl) {
174
- const base = normalizeBase(assetBaseUrl);
175
- const entry = registry().get(base);
176
- if (entry === void 0 || entry.detector === null && entry.detectorPromise === null && entry.scheduled === null) {
177
- return void 0;
178
- }
179
- return async () => {
180
- if (entry.scheduled !== null) void begin(entry, base);
181
- let waitingOn = entry.promise;
182
- while (entry.detector === null && waitingOn !== null) {
183
- await waitingOn;
184
- if (entry.promise === waitingOn) break;
185
- waitingOn = entry.promise;
186
- }
187
- return entry.detector !== null ? nonOwningView(entry.detector) : createDetector({ v3AssetBaseUrl: base });
188
- };
189
- }
190
-
191
- // src/verification/emitter.ts
192
- function createEmitter() {
193
- const handlers = /* @__PURE__ */ new Map();
194
- return {
195
- on(event, handler) {
196
- let set = handlers.get(event);
197
- if (set === void 0) {
198
- set = /* @__PURE__ */ new Set();
199
- handlers.set(event, set);
200
- }
201
- const h = handler;
202
- set.add(h);
203
- return () => {
204
- set?.delete(h);
205
- };
206
- },
207
- emit(event, payload) {
208
- const set = handlers.get(event);
209
- if (set === void 0) return;
210
- for (const handler of [...set]) {
211
- handler(payload);
212
- }
213
- },
214
- clear() {
215
- handlers.clear();
216
- }
217
- };
218
- }
219
-
220
- // src/capture/wake-lock.ts
221
- function wakeLockApi() {
222
- if (typeof navigator === "undefined" || !("wakeLock" in navigator)) return null;
223
- const api = navigator.wakeLock;
224
- return typeof api?.request === "function" ? api : null;
225
- }
226
- function isVisible() {
227
- return typeof document === "undefined" || document.visibilityState === "visible";
228
- }
229
- function releaseQuietly(sentinel) {
230
- try {
231
- void sentinel.release().catch(() => void 0);
232
- } catch {
233
- }
234
- }
235
- function createScreenWakeLock(options = {}) {
236
- if (options.enabled === false) return { setActive: () => void 0 };
237
- let wanted = false;
238
- let sentinel = null;
239
- let inFlight = false;
240
- let listening = false;
241
- const acquire = () => {
242
- if (!wanted || inFlight || sentinel !== null && !sentinel.released) return;
243
- const api = wakeLockApi();
244
- if (api === null || !isVisible()) return;
245
- let request;
246
- try {
247
- request = api.request("screen");
248
- } catch {
249
- return;
250
- }
251
- inFlight = true;
252
- request.then(
253
- (granted) => {
254
- inFlight = false;
255
- if (!wanted) {
256
- releaseQuietly(granted);
257
- return;
258
- }
259
- sentinel = granted;
260
- granted.addEventListener?.("release", () => {
261
- if (sentinel === granted) sentinel = null;
262
- });
263
- },
264
- () => {
265
- inFlight = false;
266
- }
267
- );
268
- };
269
- const onVisibilityChange = () => {
270
- if (isVisible()) acquire();
271
- };
272
- const listen = (on) => {
273
- if (typeof document === "undefined" || on === listening) return;
274
- listening = on;
275
- if (on) document.addEventListener("visibilitychange", onVisibilityChange);
276
- else document.removeEventListener("visibilitychange", onVisibilityChange);
277
- };
278
- return {
279
- setActive: (active) => {
280
- wanted = active;
281
- if (active) {
282
- if (wakeLockApi() === null) return;
283
- listen(true);
284
- acquire();
285
- return;
286
- }
287
- listen(false);
288
- if (sentinel !== null) {
289
- const held = sentinel;
290
- sentinel = null;
291
- releaseQuietly(held);
292
- }
293
- }
294
- };
295
- }
296
-
297
- // src/capture/create-liveness-capture.ts
298
- function mapGuidanceHint(key) {
299
- switch (key) {
300
- case "fcv3.no_face":
301
- case "fcv3.center_face":
302
- case "fcv3.straighten":
303
- return "center";
304
- case "fcv3.come_closer":
305
- return "closer";
306
- case "fcv3.move_back":
307
- return "farther";
308
- case "fcv3.more_light":
309
- case "fcv3.too_bright":
310
- return "light";
311
- case "fcv3.hold_still":
312
- case "fcv3.steady_camera":
313
- return "still";
314
- default:
315
- return null;
316
- }
317
- }
318
- function mapEngineState(state) {
319
- switch (state) {
320
- case "preflight":
321
- case "acquiring":
322
- return "starting";
323
- case "positioning":
324
- return "gating";
325
- case "idle":
326
- case "recording":
327
- case "captured":
328
- case "error":
329
- return state;
330
- default: {
331
- const unhandled = state;
332
- throw new Error(`mapEngineState: unhandled state ${unhandled}`);
333
- }
334
- }
335
- }
336
- function mapErrorKind(kind) {
337
- switch (kind) {
338
- case "permission":
339
- case "notfound":
340
- case "inuse":
341
- case "insecure":
342
- case "unsupported":
343
- return kind;
344
- case "overconstrained":
345
- case "generic":
346
- case "models":
347
- case "cliptoolarge":
348
- case "recorder":
349
- return "generic";
350
- default: {
351
- const unhandled = kind;
352
- throw new Error(`mapErrorKind: unhandled kind ${unhandled}`);
353
- }
354
- }
355
- }
356
- function createLivenessCaptureInternal(options, internals = {}) {
357
- const now = internals.now ?? (() => performance.now());
358
- const emitter = createEmitter();
359
- const degraded = !isMediaRecorderSupported();
360
- const hostSeams = internals.engineOptions;
361
- const preloadAware = hostSeams?.createDetector === void 0 && hostSeams?.createWorker === void 0 ? {
362
- createDetector: () => {
363
- const warmed = preloadedDetectorFactory(options.assetBaseUrl);
364
- return warmed !== void 0 ? warmed() : createDetector(
365
- options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {}
366
- );
367
- }
368
- } : {};
369
- const engineOptions = {
370
- video: options.video,
371
- mode: degraded ? "photo" : "clip",
372
- ...options.facing !== void 0 ? { facingMode: options.facing } : {},
373
- ...options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {},
374
- ...internals.engineOptions ?? {},
375
- ...preloadAware
376
- };
377
- const engine = internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);
378
- let state = "idle";
379
- let hint = null;
380
- let progress = 0;
381
- let faceDetected = false;
382
- let animations = "full";
383
- let recordingStartedAt = null;
384
- let frozenAt = null;
385
- let frameWidth = 0;
386
- let frameHeight = 0;
387
- const latchFrameSize = () => {
388
- if (options.video.videoWidth > 0 && options.video.videoHeight > 0) {
389
- frameWidth = options.video.videoWidth;
390
- frameHeight = options.video.videoHeight;
391
- }
392
- };
393
- const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });
394
- const setState = (next) => {
395
- state = next;
396
- wakeLock.setActive(next === "starting" || next === "gating" || next === "recording");
397
- emitter.emit("state", next);
398
- };
399
- const emitGate = () => emitter.emit("gate", { progress, hint, faceDetected });
400
- const syncProfile = () => {
401
- const next = engine.animations ?? "full";
402
- if (next === animations) return;
403
- animations = next;
404
- emitter.emit("profile", { animations });
405
- };
406
- const unsubscribes = [
407
- engine.on("statechange", ({ state: engineState }) => {
408
- const next = mapEngineState(engineState);
409
- if (next === "starting" && state !== "starting") {
410
- hint = null;
411
- progress = 0;
412
- faceDetected = false;
413
- recordingStartedAt = null;
414
- frozenAt = null;
415
- frameWidth = 0;
416
- frameHeight = 0;
417
- }
418
- setState(next);
419
- syncProfile();
420
- if (next === "recording") {
421
- latchFrameSize();
422
- recordingStartedAt = now();
423
- hint = "hold";
424
- progress = 1;
425
- emitGate();
426
- }
427
- }),
428
- engine.on("checks", (payload) => {
429
- latchFrameSize();
430
- progress = payload.progress;
431
- faceDetected = payload.factors.presence > 0;
432
- emitGate();
433
- syncProfile();
434
- }),
435
- engine.on("guidance", ({ key }) => {
436
- hint = mapGuidanceHint(key);
437
- emitGate();
438
- }),
439
- engine.on("frozen", ({ previewDataUrl }) => {
440
- frozenAt = now();
441
- emitter.emit("frozen", { previewDataUrl });
442
- }),
443
- engine.on("artifacts", (artifacts) => {
444
- const clipMs = recordingStartedAt !== null && frozenAt !== null ? Math.max(0, Math.round(frozenAt - recordingStartedAt)) : void 0;
445
- emitter.emit("captured", {
446
- keyframe: artifacts.keyframe,
447
- ...artifacts.clip !== void 0 ? { clip: artifacts.clip } : {},
448
- ...artifacts.clipMimeType !== void 0 ? { clipMime: artifacts.clipMimeType } : {},
449
- width: options.video.videoWidth || frameWidth,
450
- height: options.video.videoHeight || frameHeight,
451
- ...clipMs !== void 0 ? { clipMs } : {},
452
- captureChannel: detectIsMobile() ? "mobile" : "desktop",
453
- ...degraded ? { degraded: "NO_RECORDER" } : {},
454
- engine: "v3",
455
- // Face-capture timing: the engine's marks, forwarded flat (absent → absent).
456
- ...artifacts.timing?.cameraReadyMs !== void 0 ? { cameraReadyMs: artifacts.timing.cameraReadyMs } : {},
457
- ...artifacts.timing?.faceAcquireMs !== void 0 ? { faceAcquireMs: artifacts.timing.faceAcquireMs } : {},
458
- ...artifacts.timing?.captureMs !== void 0 ? { captureMs: artifacts.timing.captureMs } : {}
459
- });
460
- }),
461
- engine.on("error", ({ kind, retryable }) => {
462
- emitter.emit("error", { kind: mapErrorKind(kind), retryable });
463
- })
464
- ];
465
- const handle = {
466
- get state() {
467
- return state;
468
- },
469
- on: (event, handler) => emitter.on(event, handler),
470
- start: (startOptions) => engine.start(startOptions?.facing !== void 0 ? { facingMode: startOptions.facing } : {}),
471
- retry: () => engine.retry(),
472
- switchCamera: () => engine.switchCamera(),
473
- destroy: () => {
474
- for (const unsubscribe of unsubscribes) unsubscribe();
475
- wakeLock.setActive(false);
476
- engine.destroy();
477
- emitter.clear();
478
- }
479
- };
480
- return {
481
- handle,
482
- setFlowState: (next) => {
483
- if (state !== next) setState(next);
484
- }
485
- };
486
- }
487
- function createLivenessCapture(options) {
488
- return createLivenessCaptureInternal(options).handle;
489
- }
490
-
491
- // src/verification/result.ts
492
- var RETRYABLE_REASONS = /* @__PURE__ */ new Set([
493
- "no_face",
494
- "media_unreadable",
495
- "clip_too_short",
496
- "no_face_source",
497
- "side_missing",
498
- "unreadable",
499
- "registry_unavailable",
500
- "otp_mismatch",
501
- "otp_expired",
502
- "otp_send_failed"
503
- ]);
504
- var BIOMETRIC_CHECKS = /* @__PURE__ */ new Set(["liveness", "face_match"]);
505
- function isExecutable(step) {
506
- return step !== void 0 && (step.state === "pending" || step.state === "input_required");
507
- }
508
- function isTerminalView(view) {
509
- return view.status !== "created" && view.status !== "in_progress";
510
- }
511
- function frontierStep(view) {
512
- return view.steps.find((step) => isExecutable(step));
513
- }
514
- function stepOf(view, checkType) {
515
- return view.steps.find((step) => step.checkType === checkType);
516
- }
517
- function mergeSessionView(prev, next, submitted) {
518
- const byType = new Map(prev.steps.map((step) => [step.checkType, step]));
519
- const steps = next.steps.map((step) => {
520
- const before = byType.get(step.checkType);
521
- const attemptsBefore = before?.attemptsRemaining ?? 0;
522
- const merged = {
523
- ...before ?? {},
524
- checkType: step.checkType,
525
- state: step.state,
526
- attemptsRemaining: step.checkType === submitted ? Math.max(0, attemptsBefore - 1) : attemptsBefore
527
- };
528
- if (step.reason !== void 0) merged.reason = step.reason;
529
- else delete merged.reason;
530
- return merged;
531
- });
532
- return { ...prev, status: next.status, outcome: next.outcome, steps, expiresAt: next.expiresAt };
533
- }
534
- function unavailable(sessionId, reason) {
535
- return reason === void 0 ? { sessionId, status: "unavailable" } : { sessionId, status: "unavailable", reason };
536
- }
537
- function withReason(sessionId, status, reason) {
538
- return reason === void 0 ? { sessionId, status } : { sessionId, status, reason };
539
- }
540
- function mapSessionResult(view, opts) {
541
- const { sessionId } = view;
542
- switch (view.status) {
543
- case "completed":
544
- if (view.outcome === "approved") return { sessionId, status: "approved" };
545
- if (view.outcome === "review") return withReason(sessionId, "review", reasonIn(view, "review"));
546
- return withReason(sessionId, "rejected", reasonIn(view, "failed"));
547
- case "failed":
548
- return withReason(sessionId, "rejected", reasonIn(view, "failed"));
549
- case "review_pending":
550
- return withReason(sessionId, "review", reasonIn(view, "review"));
551
- case "expired":
552
- return unavailable(sessionId);
553
- case "created":
554
- case "in_progress":
555
- break;
556
- default: {
557
- const unhandled = view.status;
558
- throw new Error(`mapSessionResult: unhandled status ${unhandled}`);
559
- }
560
- }
561
- const parked = view.steps.find(
562
- (step) => BIOMETRIC_CHECKS.has(step.checkType) && step.state === "input_required" && step.reason !== void 0
563
- );
564
- if (parked?.reason !== void 0) {
565
- const canRetry = opts.autoRetry && RETRYABLE_REASONS.has(parked.reason) && parked.attemptsRemaining > 0;
566
- return {
567
- sessionId,
568
- status: canRetry ? "retry" : "rejected",
569
- reason: parked.reason,
570
- attemptsRemaining: parked.attemptsRemaining
571
- };
572
- }
573
- if (frontierStep(view) === void 0 && view.steps.some((step) => step.state === "review")) {
574
- return withReason(sessionId, "review", reasonIn(view, "review"));
575
- }
576
- return unavailable(sessionId);
577
- }
578
- function reasonIn(view, state) {
579
- return view.steps.find((step) => step.state === state && step.reason !== void 0)?.reason;
580
- }
581
-
582
- // src/verification/liveness-flow.ts
583
- var CLIP_MIME_ALLOWLIST = ["video/webm", "video/mp4"];
584
- var KEYFRAME_MIME = "image/jpeg";
585
- var TRANSPORT_REPLAY_ATTEMPTS = 2;
586
- var TRANSPORT_REPLAY_DELAY_MS = 1e3;
587
- var MAX_RETRY_AFTER_MS = 1e4;
588
- var RESYNC_REENTRY_MAX = 3;
589
- var TRANSPORT_CODES = /* @__PURE__ */ new Set([
590
- "network_error",
591
- "timeout",
592
- "upload_failed",
593
- "unknown_error",
594
- "rate_limit_exceeded"
595
- ]);
596
- var RESYNC_CODES = /* @__PURE__ */ new Set([
597
- "session_not_active",
598
- "check_not_found",
599
- "check_already_finalized",
600
- "check_out_of_order"
601
- ]);
602
- var AttestationError = class extends Error {
603
- constructor(message) {
604
- super(message);
605
- this.name = "AttestationError";
606
- }
607
- };
608
- async function runSubmission(rt, executor, hooks = {}) {
609
- let key = rt.uuid();
610
- let consentRecovered = false;
611
- let attestationRetried = false;
612
- let transportReplays = 0;
613
- for (; ; ) {
614
- try {
615
- return await executor(rt.client, key);
616
- } catch (err) {
617
- if (err instanceof AttestationError) {
618
- if (attestationRetried) throw err;
619
- attestationRetried = true;
620
- hooks.onAttestationInvalid?.();
621
- key = rt.uuid();
622
- continue;
623
- }
624
- if (!(err instanceof HostedApiError)) throw err;
625
- if (err.code === "consent_required" && !consentRecovered) {
626
- consentRecovered = true;
627
- await rt.ensureConsent({ force: true });
628
- key = rt.uuid();
629
- continue;
630
- }
631
- if (err.code === "attestation_invalid" && !attestationRetried) {
632
- attestationRetried = true;
633
- hooks.onAttestationInvalid?.();
634
- key = rt.uuid();
635
- continue;
636
- }
637
- if (TRANSPORT_CODES.has(err.code) && transportReplays < TRANSPORT_REPLAY_ATTEMPTS) {
638
- transportReplays += 1;
639
- const retryAfterMs = err.retryAfterSeconds !== void 0 ? err.retryAfterSeconds * 1e3 : void 0;
640
- await rt.sleep(
641
- retryAfterMs !== void 0 ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS) : TRANSPORT_REPLAY_DELAY_MS
642
- );
643
- continue;
644
- }
645
- throw err;
646
- }
647
- }
648
- }
649
- var LIVENESS_CHAINABLE = /* @__PURE__ */ new Set(["passed", "review", "processing", "skipped"]);
650
- async function runLivenessFlow(deps) {
651
- const { artifacts } = deps;
652
- const clip = artifacts.clip;
653
- const bareMime = stripMimeParams(artifacts.clipMime ?? "");
654
- if (artifacts.degraded === "NO_RECORDER" || clip === void 0 || !CLIP_MIME_ALLOWLIST.includes(bareMime)) {
655
- return { kind: "unavailable", reason: "no_recorder" };
656
- }
657
- const submitLiveness = buildLivenessSubmit(deps, clip, bareMime);
658
- const submitFaceMatch = buildFaceMatchSubmit(deps, artifacts.keyframe);
659
- let livenessSubmitted = false;
660
- let faceMatchSubmitted = false;
661
- let livenessChainable = true;
662
- let resyncs = 0;
663
- for (; ; ) {
664
- if (deps.isCancelled()) return { kind: "cancelled" };
665
- const view = deps.getView();
666
- if (isTerminalView(view)) return { kind: "settled" };
667
- const frontier = frontierStep(view)?.checkType;
668
- const next = frontier === "liveness" && !livenessSubmitted ? "liveness" : frontier === "face_match" && !faceMatchSubmitted && livenessChainable ? "face_match" : null;
669
- if (next === null) return { kind: "settled" };
670
- try {
671
- if (next === "liveness") {
672
- livenessSubmitted = true;
673
- const response = await submitLiveness();
674
- if (deps.isCancelled()) return { kind: "cancelled" };
675
- deps.applySubmit(response, "liveness");
676
- const state = stepOf(deps.getView(), "liveness")?.state;
677
- livenessChainable = state === void 0 || LIVENESS_CHAINABLE.has(state);
678
- } else {
679
- faceMatchSubmitted = true;
680
- const response = await submitFaceMatch();
681
- if (deps.isCancelled()) return { kind: "cancelled" };
682
- deps.applySubmit(response, "face_match");
683
- }
684
- } catch (err) {
685
- if (deps.isCancelled()) return { kind: "cancelled" };
686
- const outcome = classifyFailure(err);
687
- if (outcome.kind !== "resync" || resyncs >= RESYNC_REENTRY_MAX) return outcome;
688
- resyncs += 1;
689
- await deps.refreshView();
690
- }
691
- }
692
- }
693
- function buildLivenessSubmit(deps, clip, bareMime) {
694
- let clipKey = null;
695
- let attestation = null;
696
- return () => runSubmission(
697
- deps,
698
- async (client, idempotencyKey) => {
699
- if (clipKey === null) {
700
- deps.onPhase("uploading");
701
- const presigned = await client.presign("clip", bareMime, clip.size);
702
- await client.uploadToSpaces(presigned.uploadUrl, clip, bareMime);
703
- clipKey = (await client.completeMedia(presigned.key)).key;
704
- }
705
- if (attestation === null) {
706
- try {
707
- attestation = await deps.getAttestation();
708
- } catch (err) {
709
- throw new AttestationError(err instanceof Error ? err.message : String(err));
710
- }
711
- }
712
- deps.onPhase("checking");
713
- return client.submitCheck(
714
- "liveness",
715
- {
716
- clipKey,
717
- attestation,
718
- captureChannel: deps.artifacts.captureChannel,
719
- capture: captureInputOf(deps.artifacts)
720
- },
721
- { idempotencyKey }
722
- );
723
- },
724
- {
725
- onAttestationInvalid: () => {
726
- attestation = null;
727
- }
728
- }
729
- );
730
- }
731
- function captureInputOf(artifacts) {
732
- return {
733
- ...artifacts.engine !== void 0 ? { engine: artifacts.engine } : {},
734
- width: artifacts.width,
735
- height: artifacts.height,
736
- ...artifacts.clipMs !== void 0 ? { clipMs: artifacts.clipMs } : {},
737
- ...artifacts.cameraReadyMs !== void 0 ? { cameraReadyMs: artifacts.cameraReadyMs } : {},
738
- ...artifacts.faceAcquireMs !== void 0 ? { faceAcquireMs: artifacts.faceAcquireMs } : {},
739
- ...artifacts.captureMs !== void 0 ? { captureMs: artifacts.captureMs } : {}
740
- };
741
- }
742
- function buildFaceMatchSubmit(deps, keyframe) {
743
- let imageKey = null;
744
- return () => runSubmission(deps, async (client, idempotencyKey) => {
745
- if (imageKey === null) {
746
- deps.onPhase("uploading");
747
- const presigned = await client.presign("image", KEYFRAME_MIME, keyframe.size);
748
- await client.uploadToSpaces(presigned.uploadUrl, keyframe, KEYFRAME_MIME);
749
- imageKey = (await client.completeMedia(presigned.key)).key;
750
- }
751
- deps.onPhase("checking");
752
- return client.submitCheck("face_match", { imageKey }, { idempotencyKey });
753
- });
754
- }
755
- function classifyFailure(err) {
756
- if (err instanceof AttestationError) return { kind: "unavailable", reason: "attestation" };
757
- if (err instanceof HostedApiError) {
758
- if (err.code === "attestation_invalid") return { kind: "unavailable", reason: "attestation" };
759
- if (RESYNC_CODES.has(err.code)) return { kind: "resync" };
760
- }
761
- return { kind: "unavailable", reason: "network" };
762
- }
763
-
764
- // src/verification/turnstile.ts
765
- var TURNSTILE_SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js";
766
- var TURNSTILE_EXECUTE_TIMEOUT_MS = 3e4;
767
- function installedApi() {
768
- return window.turnstile;
769
- }
770
- var loaderPromise = null;
771
- function resetTurnstileForTests() {
772
- loaderPromise = null;
773
- }
774
- function loadTurnstile() {
775
- loaderPromise ?? (loaderPromise = new Promise((resolve, reject) => {
776
- const present = installedApi();
777
- if (present) {
778
- resolve(present);
779
- return;
780
- }
781
- const script = document.createElement("script");
782
- script.src = TURNSTILE_SCRIPT_SRC;
783
- script.async = true;
784
- script.onload = () => {
785
- const api = installedApi();
786
- if (api) {
787
- resolve(api);
788
- } else {
789
- loaderPromise = null;
790
- reject(new Error("Turnstile script loaded but exposed no API"));
791
- }
792
- };
793
- script.onerror = () => {
794
- loaderPromise = null;
795
- script.remove();
796
- reject(new Error("Turnstile script failed to load"));
797
- };
798
- document.head.appendChild(script);
799
- }));
800
- return loaderPromise;
801
- }
802
- function abortError() {
803
- return new Error("Turnstile challenge aborted");
804
- }
805
- async function executeTurnstile(siteKey, opts = {}) {
806
- const timeoutMs = opts.timeoutMs ?? TURNSTILE_EXECUTE_TIMEOUT_MS;
807
- const signal = opts.signal;
808
- if (signal?.aborted) throw abortError();
809
- const api = await loadTurnstile();
810
- if (signal?.aborted) throw abortError();
811
- const container = document.createElement("div");
812
- document.body.appendChild(container);
813
- let widgetId;
814
- let timer;
815
- let onAbort;
816
- try {
817
- return await new Promise((resolve, reject) => {
818
- timer = setTimeout(
819
- () => reject(new Error(`Turnstile challenge timed out (${timeoutMs} ms)`)),
820
- timeoutMs
821
- );
822
- if (signal !== void 0) {
823
- onAbort = () => reject(abortError());
824
- signal.addEventListener("abort", onAbort, { once: true });
825
- }
826
- widgetId = api.render(container, {
827
- sitekey: siteKey,
828
- execution: "execute",
829
- appearance: "execute",
830
- callback: resolve,
831
- "error-callback": (code) => {
832
- reject(new Error(`Turnstile challenge failed${code ? ` (${code})` : ""}`));
833
- }
834
- });
835
- if (widgetId === void 0) {
836
- reject(new Error("Turnstile render returned no widget id"));
837
- return;
838
- }
839
- api.execute(widgetId);
840
- });
841
- } finally {
842
- clearTimeout(timer);
843
- if (signal !== void 0 && onAbort !== void 0) signal.removeEventListener("abort", onAbort);
844
- if (widgetId !== void 0) {
845
- try {
846
- api.remove(widgetId);
847
- } catch {
848
- }
849
- }
850
- container.remove();
851
- }
852
- }
853
-
854
- // src/verification/create-verification.ts
855
- var DEFAULT_API_ORIGIN = "https://api.sekyro.ai";
856
- var REGISTRY_RETRY_DELAY_MS = 5e3;
857
- var PROCESSING_POLL_DELAY_MS = 1500;
858
- var PROCESSING_POLL_MAX = 10;
859
- function defaultSleep(ms) {
860
- return new Promise((resolve) => setTimeout(resolve, ms));
861
- }
862
- function randomUuid() {
863
- const c = globalThis.crypto;
864
- if (c !== void 0 && typeof c.randomUUID === "function") return c.randomUUID();
865
- const bytes = new Uint8Array(16);
866
- if (c !== void 0 && typeof c.getRandomValues === "function") c.getRandomValues(bytes);
867
- else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
868
- bytes[6] = (bytes[6] ?? 0) & 15 | 64;
869
- bytes[8] = (bytes[8] ?? 0) & 63 | 128;
870
- const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
871
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
872
- }
873
- function createVerification(options) {
874
- return createVerificationInternal(options, {});
875
- }
876
- function createVerificationInternal(options, internals) {
877
- const apiOrigin = options.apiOrigin ?? DEFAULT_API_ORIGIN;
878
- const autoRetry = options.autoRetry ?? true;
879
- const rearm = options.rearm ?? "auto";
880
- const client = internals.client ?? new HostedClient({
881
- apiOrigin,
882
- sessionToken: options.clientToken,
883
- ...options.fetch !== void 0 ? { fetch: options.fetch } : {}
884
- });
885
- const sleep = internals.sleep ?? defaultSleep;
886
- const uuid = internals.uuid ?? randomUuid;
887
- const getTurnstileToken = internals.getTurnstileToken ?? executeTurnstile;
888
- const emitter = createEmitter();
889
- let destroyed = false;
890
- const lifetime = new AbortController();
891
- let view = null;
892
- let bootstrap = null;
893
- const setView = (next) => {
894
- view = next;
895
- emitter.emit("view", next);
896
- };
897
- const ensureView = () => {
898
- if (view !== null) return Promise.resolve(view);
899
- bootstrap ?? (bootstrap = client.getSession().then((next) => {
900
- setView(next);
901
- return next;
902
- }).finally(() => {
903
- bootstrap = null;
904
- }));
905
- return bootstrap;
906
- };
907
- const refreshView = async () => {
908
- const next = await client.getSession();
909
- setView(next);
910
- return next;
911
- };
912
- const applySubmit = (response, submitted) => {
913
- if (view !== null) setView(mergeSessionView(view, response, submitted));
914
- };
915
- let consentInFlight = null;
916
- const recordConsent = async (force) => {
917
- const current = await ensureView();
918
- if (!force && current.consentRecorded) return;
919
- await client.recordConsent(options.locale);
920
- if (view !== null) setView({ ...view, consentRecorded: true });
921
- };
922
- const ensureConsent = (opts) => {
923
- if (opts?.force === true) return recordConsent(true);
924
- consentInFlight ?? (consentInFlight = recordConsent(false).finally(() => {
925
- consentInFlight = null;
926
- }));
927
- return consentInFlight;
928
- };
929
- const runtime = {
930
- client,
931
- ensureConsent: (opts) => ensureConsent(opts),
932
- sleep,
933
- uuid
934
- };
935
- let terminalEmitted = false;
936
- const emitResult = (result) => {
937
- if (result.status === "approved" || result.status === "rejected" || result.status === "review") {
938
- if (terminalEmitted) return;
939
- terminalEmitted = true;
940
- }
941
- emitter.emit("result", result);
942
- };
943
- let registry2 = null;
944
- let registryFailed = false;
945
- let registryUnbound = false;
946
- const submitRegistry = () => runSubmission(
947
- runtime,
948
- (c, idempotencyKey) => (
949
- // `{}` input: the session is bound to a SERVER-provided NNI (Plan A Task 2).
950
- c.submitCheck("population_registry", {}, { idempotencyKey })
951
- )
952
- );
953
- const runRegistry = async () => {
954
- try {
955
- const current = await ensureView();
956
- if (isTerminalView(current) || frontierStep(current)?.checkType !== "population_registry") {
957
- return;
958
- }
959
- if (current.nniBound !== true) {
960
- registryUnbound = true;
961
- return;
962
- }
963
- await ensureConsent();
964
- applySubmit(await submitRegistry(), "population_registry");
965
- const step = view === null ? void 0 : stepOf(view, "population_registry");
966
- if (step?.state === "input_required" && step.reason === "registry_unavailable" && step.attemptsRemaining > 0) {
967
- await sleep(REGISTRY_RETRY_DELAY_MS);
968
- if (destroyed) return;
969
- applySubmit(await submitRegistry(), "population_registry");
970
- }
971
- } catch {
972
- registryFailed = true;
973
- return;
974
- }
975
- if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));
976
- };
977
- const startRegistry = () => {
978
- registry2 ?? (registry2 = runRegistry());
979
- return registry2;
980
- };
981
- const getAttestation = async () => {
982
- const current = await ensureView();
983
- const siteKey = current.turnstileSiteKey;
984
- if (siteKey === null) return { sandbox: true };
985
- return {
986
- channel: "hosted_web",
987
- siteKey,
988
- turnstileToken: await getTurnstileToken(siteKey, { signal: lifetime.signal })
989
- };
990
- };
991
- let active = null;
992
- let chain = Promise.resolve();
993
- let chainDepth = 0;
994
- const settleProcessing = async () => {
995
- let current = await ensureView();
996
- for (let i = 0; i < PROCESSING_POLL_MAX && !destroyed && !isTerminalView(current) && frontierStep(current) === void 0 && current.steps.some((step) => step.state === "processing"); i += 1) {
997
- await sleep(PROCESSING_POLL_DELAY_MS);
998
- if (destroyed) break;
999
- current = await refreshView();
1000
- }
1001
- return current;
1002
- };
1003
- const runFlow = async (bound, artifacts, capturedAttempt) => {
1004
- const { capture } = bound;
1005
- const isCancelled = () => destroyed;
1006
- const ownsHandle = () => bound.alive && bound.attempt === capturedAttempt;
1007
- const setPhase = (phase) => {
1008
- if (ownsHandle()) capture.setFlowState(phase);
1009
- };
1010
- const finish = (result) => {
1011
- if (isCancelled()) return;
1012
- emitResult(result);
1013
- if (!ownsHandle()) return;
1014
- if (result.status === "retry" && rearm === "auto") {
1015
- capture.handle.retry();
1016
- return;
1017
- }
1018
- capture.setFlowState("done");
1019
- };
1020
- if (artifacts.degraded === "NO_RECORDER") {
1021
- const sessionId = view?.sessionId ?? (await ensureView().catch(() => null))?.sessionId ?? "";
1022
- finish(unavailable(sessionId, "no_recorder"));
1023
- return;
1024
- }
1025
- setPhase("uploading");
1026
- try {
1027
- await startRegistry();
1028
- if (isCancelled()) return;
1029
- let current = await ensureView();
1030
- if (!isTerminalView(current)) {
1031
- if (frontierStep(current)?.checkType === "population_registry") {
1032
- finish(
1033
- unavailable(
1034
- current.sessionId,
1035
- registryUnbound ? "nni_unbound" : registryFailed ? "network" : "registry_unavailable"
1036
- )
1037
- );
1038
- return;
1039
- }
1040
- await ensureConsent();
1041
- if (isCancelled()) return;
1042
- const outcome = await runLivenessFlow({
1043
- ...runtime,
1044
- artifacts,
1045
- getAttestation,
1046
- getView: () => view ?? current,
1047
- refreshView,
1048
- applySubmit,
1049
- onPhase: setPhase,
1050
- isCancelled
1051
- });
1052
- if (isCancelled()) return;
1053
- if (outcome.kind === "unavailable") {
1054
- finish(unavailable(current.sessionId, outcome.reason));
1055
- return;
1056
- }
1057
- if (outcome.kind === "resync") await refreshView();
1058
- current = await settleProcessing();
1059
- }
1060
- finish(mapSessionResult(current, { autoRetry }));
1061
- } catch {
1062
- if (isCancelled()) return;
1063
- finish(unavailable(view?.sessionId ?? "", "network"));
1064
- }
1065
- };
1066
- const enqueueFlow = (bound, artifacts) => {
1067
- const capturedAttempt = bound.attempt;
1068
- if (chainDepth > 0 && bound.alive) {
1069
- bound.capture.setFlowState("uploading");
1070
- }
1071
- chainDepth += 1;
1072
- chain = chain.then(() => runFlow(bound, artifacts, capturedAttempt)).finally(() => {
1073
- chainDepth -= 1;
1074
- }).catch(() => void 0);
1075
- };
1076
- const liveness = (captureOptions) => {
1077
- active?.capture.handle.destroy();
1078
- if (active !== null) active.alive = false;
1079
- const capture = createLivenessCaptureInternal(captureOptions, internals.capture);
1080
- const bound = { capture, alive: true, attempt: 0 };
1081
- active = bound;
1082
- capture.handle.on("state", (state) => {
1083
- if (state === "starting") bound.attempt += 1;
1084
- });
1085
- capture.handle.on("captured", (artifacts) => {
1086
- void Promise.resolve().then(() => enqueueFlow(bound, artifacts));
1087
- });
1088
- const inner = capture.handle;
1089
- return {
1090
- get state() {
1091
- return inner.state;
1092
- },
1093
- on: (event, handler) => inner.on(event, handler),
1094
- start: (startOptions) => inner.start(startOptions),
1095
- retry: () => inner.retry(),
1096
- switchCamera: () => inner.switchCamera(),
1097
- destroy: () => {
1098
- bound.alive = false;
1099
- if (active === bound) active = null;
1100
- inner.destroy();
1101
- }
1102
- };
1103
- };
1104
- return {
1105
- consent: () => ensureConsent(),
1106
- startRegistry,
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
- },
1118
- on: (event, handler) => emitter.on(event, handler),
1119
- destroy: () => {
1120
- destroyed = true;
1121
- lifetime.abort();
1122
- if (active !== null) {
1123
- active.alive = false;
1124
- active.capture.handle.destroy();
1125
- active = null;
1126
- }
1127
- emitter.clear();
1128
- }
1129
- };
1130
- }
1131
-
1132
- export {
1133
- PRELOAD_BLAZE_INIT_TIMEOUT_MS,
1134
- PRELOAD_BUDGET_MS,
1135
- setPreloadLoaderForTests,
1136
- resetLivenessAssetsForTests,
1137
- preloadLivenessAssets,
1138
- getLivenessAssetsStatus,
1139
- subscribeLivenessAssets,
1140
- releaseLivenessAssets,
1141
- preloadedDetectorFactory,
1142
- mapGuidanceHint,
1143
- mapEngineState,
1144
- mapErrorKind,
1145
- createLivenessCaptureInternal,
1146
- createLivenessCapture,
1147
- RETRYABLE_REASONS,
1148
- isExecutable,
1149
- isTerminalView,
1150
- frontierStep,
1151
- stepOf,
1152
- mergeSessionView,
1153
- mapSessionResult,
1154
- CLIP_MIME_ALLOWLIST,
1155
- TRANSPORT_REPLAY_ATTEMPTS,
1156
- TRANSPORT_REPLAY_DELAY_MS,
1157
- AttestationError,
1158
- runSubmission,
1159
- runLivenessFlow,
1160
- TURNSTILE_SCRIPT_SRC,
1161
- TURNSTILE_EXECUTE_TIMEOUT_MS,
1162
- resetTurnstileForTests,
1163
- loadTurnstile,
1164
- executeTurnstile,
1165
- DEFAULT_API_ORIGIN,
1166
- REGISTRY_RETRY_DELAY_MS,
1167
- PROCESSING_POLL_DELAY_MS,
1168
- PROCESSING_POLL_MAX,
1169
- randomUuid,
1170
- createVerification,
1171
- createVerificationInternal
1172
- };
1173
- //# sourceMappingURL=chunk-R3SJQHYU.js.map