@incodetech/core 0.0.0-dev-20260127-938ff95 → 0.0.0-dev-20260130-5bb04b9

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 (33) hide show
  1. package/dist/OpenViduLogger-Cu0BjUgP.esm.js +3 -0
  2. package/dist/{deepsightLoader-Ct3HSIhk.esm.js → deepsightLoader-CGdK3U_s.esm.js} +7 -8
  3. package/dist/{deepsightService-B8c8aZje.esm.js → deepsightService-BqN04Xvz.esm.js} +53 -5
  4. package/dist/{types-CRVSv38Q.d.ts → deepsightService-C-8SR9TZ.d.ts} +91 -2
  5. package/dist/email.d.ts +1 -1
  6. package/dist/email.esm.js +3 -3
  7. package/dist/{endpoints-BUsSVoJV.esm.js → endpoints-CmGlc9UG.esm.js} +7 -1
  8. package/dist/{events-B8ZkhAZo.esm.js → events-CLVaKe2J.esm.js} +1 -1
  9. package/dist/flow.d.ts +2 -2
  10. package/dist/flow.esm.js +3 -3
  11. package/dist/id-DE76bgjS.esm.js +2498 -0
  12. package/dist/id.d.ts +5 -5
  13. package/dist/id.esm.js +7 -6
  14. package/dist/{index-CJMK8K5u.d.ts → index-aadmCg5s.d.ts} +84 -62
  15. package/dist/index.d.ts +78 -6
  16. package/dist/index.esm.js +12 -46
  17. package/dist/{lib-CbAibJlt.esm.js → lib-Cmee0CBZ.esm.js} +1 -1
  18. package/dist/phone.d.ts +1 -1
  19. package/dist/phone.esm.js +3 -3
  20. package/dist/selfie.d.ts +8 -87
  21. package/dist/selfie.esm.js +39 -19
  22. package/dist/{src-XSoNGEQW.esm.js → src-BEaVRVtC.esm.js} +6 -2
  23. package/dist/stats.d.ts +4 -0
  24. package/dist/stats.esm.js +1 -1
  25. package/dist/{streamingEvents-J6ffKmJL.esm.js → streamingEvents-EcGvh3bl.esm.js} +13 -4
  26. package/package.json +1 -1
  27. package/dist/OpenViduLogger-DyqID_-7.esm.js +0 -3
  28. package/dist/getDeviceClass-DkfbtsIJ.esm.js +0 -41
  29. package/dist/id-GPFS1Wo_.esm.js +0 -1827
  30. /package/dist/{Manager-Co-PsiG9.d.ts → Manager-WTb99jnh.d.ts} +0 -0
  31. /package/dist/{OpenViduLogger-BLxxXoyF.esm.js → OpenViduLogger-XKcjntVs.esm.js} +0 -0
  32. /package/dist/{stats-DnU4uUFv.esm.js → stats-onWUN0tY.esm.js} +0 -0
  33. /package/dist/{types-CMR6NkxW.d.ts → types-CflhN94Q.d.ts} +0 -0
@@ -0,0 +1,2498 @@
1
+ import { d as addEvent, m as revokeObjectURL, n as eventModuleNames } from "./events-CLVaKe2J.esm.js";
2
+ import { C as createManager, D as isIOS, E as isDesktop, O as isIPhone14OrHigher, S as stopCameraStream, T as isAndroid, b as enumerateVideoDevices, c as OpenViduRecordingProvider, d as BrowserTimerProvider, f as BrowserStorageProvider, h as StreamCanvasCapture, k as isSafari, m as StreamCanvasProcessingSession, n as DEFAULT_ID_CAPTURE_THRESHOLDS, p as BrowserEnvironmentProvider, t as DEFAULT_ID_CAPTURE_MODEL_VERSION, v as IncodeCanvas, x as requestCameraAccess, y as applyTrackConstraints } from "./src-BEaVRVtC.esm.js";
3
+ import { i as getUserAgent, n as getDeviceInfo, r as getWindowDimensions } from "./deepsightService-BqN04Xvz.esm.js";
4
+ import { a as fromPromise, i as fromCallback, n as setup, o as createActor, r as assign, t as endpoints } from "./endpoints-CmGlc9UG.esm.js";
5
+ import { c as getDeviceClass, i as stopRecording$1, n as createRecordingSession, o as checkPermission, r as startRecording, s as requestPermission, t as streamingEvents } from "./streamingEvents-EcGvh3bl.esm.js";
6
+ import { n as getApi, r as getToken, t as api } from "./api-DfRLAneb.esm.js";
7
+ import { t as addDeviceStats } from "./stats-onWUN0tY.esm.js";
8
+
9
+ //#region ../infra/src/capabilities/ITimerCapability.ts
10
+ function sleep(ms) {
11
+ return new Promise((resolve) => setTimeout(resolve, ms));
12
+ }
13
+
14
+ //#endregion
15
+ //#region src/internal/session/sessionService.ts
16
+ /**
17
+ * Creates a new onboarding session.
18
+ *
19
+ * @param apiKey - The API key from the Incode dashboard
20
+ * @param options - Session creation options
21
+ * @param signal - Optional AbortSignal for request cancellation
22
+ * @returns The created session with token
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const session = await createSession('your-api-key', {
27
+ * configurationId: 'your-flow-id',
28
+ * language: 'en-US',
29
+ * });
30
+ * console.log(session.token); // Use this token for subsequent API calls
31
+ * ```
32
+ */
33
+ async function createSession(apiKey, options, signal) {
34
+ const res = await getApi().post(endpoints.createSession, {
35
+ configurationId: options.configurationId,
36
+ externalId: options.externalId,
37
+ externalCustomerId: options.externalCustomerId,
38
+ language: options.language ?? "en-US",
39
+ customFields: options.customFields,
40
+ uuid: options.uuid ?? null,
41
+ interviewId: options.interviewId ?? null
42
+ }, {
43
+ headers: {
44
+ "x-api-key": apiKey,
45
+ "api-version": "1.0"
46
+ },
47
+ signal
48
+ });
49
+ if (!res.ok) throw new Error(`POST ${endpoints.createSession} failed: ${res.status} ${res.statusText}`);
50
+ return res.data;
51
+ }
52
+
53
+ //#endregion
54
+ //#region src/internal/featureConfig/featureConfigService.ts
55
+ let cachedFeatures$1 = null;
56
+ /**
57
+ * Checks if a feature is enabled in the feature config.
58
+ */
59
+ function isFeatureEnabled(feature, features) {
60
+ return features?.find((f) => f.feature === feature)?.enabled ?? false;
61
+ }
62
+ /**
63
+ * Fetches feature configuration from the backend.
64
+ * Results are cached for the session lifetime.
65
+ */
66
+ async function fetchFeatureConfig(signal) {
67
+ if (cachedFeatures$1) return cachedFeatures$1;
68
+ const response = await api.get(endpoints.featureConfig, { signal });
69
+ if (!response.ok) throw new Error(`Failed to fetch feature config: ${response.status} ${response.statusText}`);
70
+ cachedFeatures$1 = response.data;
71
+ return cachedFeatures$1;
72
+ }
73
+ /**
74
+ * Resets the cached feature config (useful for testing).
75
+ */
76
+ function resetFeatureConfigCache() {
77
+ cachedFeatures$1 = null;
78
+ }
79
+
80
+ //#endregion
81
+ //#region src/internal/fingerprint/fingerprintService.ts
82
+ const IP_PROVIDER_URL = "https://api.ipify.org?format=json";
83
+ const AUTOMATION_MARKERS = {
84
+ window: [
85
+ "callPhantom",
86
+ "_phantom",
87
+ "phantom",
88
+ "__nightmare",
89
+ "domAutomation",
90
+ "domAutomationController"
91
+ ],
92
+ navigator: ["webdriver"]
93
+ };
94
+ function isBrowserSimulation(browserEnv) {
95
+ for (const prop of AUTOMATION_MARKERS.window) if (browserEnv.getWindowProperty(prop)) return true;
96
+ for (const prop of AUTOMATION_MARKERS.navigator) if (browserEnv.getNavigatorProperty(prop)) return true;
97
+ return false;
98
+ }
99
+ async function fetchPublicIp(browserEnv) {
100
+ try {
101
+ return (await browserEnv.fetchJson(IP_PROVIDER_URL, 3e3))?.ip || "";
102
+ } catch {
103
+ return "";
104
+ }
105
+ }
106
+ function parseBrowserInfo(ua) {
107
+ const uaLower = ua.toLowerCase();
108
+ if (uaLower.includes("edg/")) {
109
+ const match = ua.match(/edg\/(\d+)/i);
110
+ return {
111
+ name: "Edge",
112
+ version: match ? match[1] : "Unknown"
113
+ };
114
+ }
115
+ if (uaLower.includes("chrome/") && !uaLower.includes("edg/")) {
116
+ const match = ua.match(/chrome\/(\d+)/i);
117
+ return {
118
+ name: "Chrome",
119
+ version: match ? match[1] : "Unknown"
120
+ };
121
+ }
122
+ if (uaLower.includes("firefox/")) {
123
+ const match = ua.match(/firefox\/(\d+)/i);
124
+ return {
125
+ name: "Firefox",
126
+ version: match ? match[1] : "Unknown"
127
+ };
128
+ }
129
+ if (uaLower.includes("safari/") && !uaLower.includes("chrome/")) {
130
+ const match = ua.match(/version\/(\d+)/i);
131
+ return {
132
+ name: "Safari",
133
+ version: match ? match[1] : "Unknown"
134
+ };
135
+ }
136
+ return {
137
+ name: "Unknown",
138
+ version: "Unknown"
139
+ };
140
+ }
141
+ function parseOSInfo(ua, platform) {
142
+ if (/iphone|ipad|ipod/i.test(ua)) {
143
+ const match = ua.match(/os (\d+)[._](\d+)/i);
144
+ if (match) return {
145
+ name: "iOS",
146
+ version: `${match[1]}.${match[2]}`
147
+ };
148
+ return {
149
+ name: "iOS",
150
+ version: "Unknown"
151
+ };
152
+ }
153
+ if (/android/i.test(ua)) {
154
+ const match = ua.match(/android (\d+(?:\.\d+)?)/i);
155
+ return {
156
+ name: "Android",
157
+ version: match ? match[1] : "Unknown"
158
+ };
159
+ }
160
+ if (/windows/i.test(ua)) {
161
+ const match = ua.match(/windows nt (\d+\.\d+)/i);
162
+ return {
163
+ name: "Windows",
164
+ version: match ? match[1] : "Unknown"
165
+ };
166
+ }
167
+ if (/macintosh|mac os x/i.test(ua)) {
168
+ const match = ua.match(/mac os x (\d+)[._](\d+)/i);
169
+ if (match) return {
170
+ name: "macOS",
171
+ version: `${match[1]}.${match[2]}`
172
+ };
173
+ return {
174
+ name: "macOS",
175
+ version: "Unknown"
176
+ };
177
+ }
178
+ if (/linux/i.test(ua)) return {
179
+ name: "Linux",
180
+ version: "Unknown"
181
+ };
182
+ return {
183
+ name: platform || "Unknown",
184
+ version: "Unknown"
185
+ };
186
+ }
187
+ function isWebview(ua) {
188
+ const uaLower = ua.toLowerCase();
189
+ return uaLower.includes("wv") || uaLower.includes("webview") || uaLower.includes("android") && !uaLower.includes("chrome");
190
+ }
191
+ function getDeviceModel(ua, platform) {
192
+ if (/iphone/i.test(ua)) {
193
+ const match = ua.match(/iphone\s*(\w+)/i);
194
+ return match ? `iPhone ${match[1]}` : "iPhone";
195
+ }
196
+ if (/ipad/i.test(ua)) return ua.match(/ipad/i) ? "iPad" : "iPad";
197
+ if (/android/i.test(ua)) {
198
+ const match = ua.match(/android.*;\s*([^)]+)\)/i);
199
+ return match ? match[1].trim() : "Android Device";
200
+ }
201
+ return platform || "Unknown";
202
+ }
203
+ /**
204
+ * Collects device fingerprint data and submits it to the backend.
205
+ */
206
+ async function submitDeviceFingerprint(options) {
207
+ const { browserEnv, sdkVersion, disableIpify = false, hostingApp, signal } = options;
208
+ const ua = getUserAgent();
209
+ const deviceInfo = getDeviceInfo();
210
+ const hash = browserEnv.generateCanvasFingerprint();
211
+ const ip = disableIpify ? "" : await fetchPublicIp(browserEnv);
212
+ const browserInfo = parseBrowserInfo(ua);
213
+ const osInfo = parseOSInfo(ua, deviceInfo.platform);
214
+ const deviceModel = getDeviceModel(ua, deviceInfo.platform);
215
+ const browserString = isWebview(ua) ? "webview" : `${browserInfo.name} ${browserInfo.version}`;
216
+ const fingerprintData = {
217
+ hash,
218
+ ip,
219
+ deviceType: "WEBAPP",
220
+ data: JSON.stringify({
221
+ visitorId: hash,
222
+ ip
223
+ }),
224
+ osVersion: `${osInfo.name} ${osInfo.version}`,
225
+ deviceModel,
226
+ browser: browserString,
227
+ sdkVersion,
228
+ hasLiedBrowser: isBrowserSimulation(browserEnv),
229
+ hostingApp
230
+ };
231
+ const response = await api.post(endpoints.deviceFingerprint, {
232
+ hash: fingerprintData.hash,
233
+ ip: fingerprintData.ip,
234
+ deviceType: fingerprintData.deviceType,
235
+ data: fingerprintData.data,
236
+ osVersion: fingerprintData.osVersion,
237
+ deviceModel: fingerprintData.deviceModel,
238
+ browser: fingerprintData.browser,
239
+ hasLiedBrowser: fingerprintData.hasLiedBrowser,
240
+ sdkVersion: fingerprintData.sdkVersion,
241
+ hostingApp: fingerprintData.hostingApp,
242
+ ipLocation: fingerprintData.ipLocation
243
+ }, { signal });
244
+ if (!response.ok) throw new Error(`Failed to submit device fingerprint: ${response.status} ${response.statusText}`);
245
+ return response.data;
246
+ }
247
+
248
+ //#endregion
249
+ //#region src/internal/session/sessionInitializer.ts
250
+ const SDK_VERSION = "2.0.0";
251
+ let sessionInitialized = false;
252
+ let cachedFeatures = null;
253
+ let cachedDisableIpify = false;
254
+ /**
255
+ * Initializes session-level services after the token is set.
256
+ * This should be called once after createSession() or setToken().
257
+ *
258
+ * Performs:
259
+ * 1. Fetches feature configuration from backend
260
+ * 2. Submits device fingerprint
261
+ *
262
+ * Results are cached for the session lifetime.
263
+ *
264
+ * @param options - Optional configuration
265
+ * @returns Session initialization result with feature config
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * // After creating a session
270
+ * const session = await createSession('api-key', options);
271
+ * setup({ apiURL, token: session.token });
272
+ * const { features } = await initializeSession();
273
+ *
274
+ * // Check feature flags
275
+ * if (isFeatureEnabled('DISABLE_IPIFY', features.features)) {
276
+ * // Handle disabled ipify
277
+ * }
278
+ * ```
279
+ */
280
+ async function initializeSession(options = {}) {
281
+ const { hostingApp, signal } = options;
282
+ if (sessionInitialized && cachedFeatures) return {
283
+ features: cachedFeatures,
284
+ disableIpify: cachedDisableIpify,
285
+ fingerprintSuccess: true
286
+ };
287
+ let features;
288
+ let disableIpify = false;
289
+ try {
290
+ features = await fetchFeatureConfig(signal);
291
+ disableIpify = isFeatureEnabled("DISABLE_IPIFY", features.features);
292
+ } catch (error) {
293
+ console.warn("Failed to fetch feature config:", error);
294
+ features = { sessionIdentifier: "" };
295
+ }
296
+ let fingerprintSuccess = false;
297
+ try {
298
+ await submitDeviceFingerprint({
299
+ browserEnv: new BrowserEnvironmentProvider(),
300
+ sdkVersion: SDK_VERSION,
301
+ disableIpify,
302
+ hostingApp,
303
+ signal
304
+ });
305
+ fingerprintSuccess = true;
306
+ } catch (error) {
307
+ console.warn("Failed to submit device fingerprint:", error);
308
+ }
309
+ sessionInitialized = true;
310
+ cachedFeatures = features;
311
+ cachedDisableIpify = disableIpify;
312
+ return {
313
+ features,
314
+ disableIpify,
315
+ fingerprintSuccess
316
+ };
317
+ }
318
+ /**
319
+ * Gets the cached feature configuration.
320
+ * Returns undefined if session hasn't been initialized.
321
+ */
322
+ function getSessionFeatures() {
323
+ return cachedFeatures ?? void 0;
324
+ }
325
+ /**
326
+ * Gets the cached disableIpify flag.
327
+ * Returns false if session hasn't been initialized.
328
+ */
329
+ function getDisableIpify() {
330
+ return cachedDisableIpify;
331
+ }
332
+ /**
333
+ * Checks if the session has been initialized.
334
+ */
335
+ function isSessionInitialized() {
336
+ return sessionInitialized;
337
+ }
338
+ /**
339
+ * Resets session initialization state.
340
+ * Useful for testing or when starting a new session.
341
+ */
342
+ function resetSessionInit() {
343
+ sessionInitialized = false;
344
+ cachedFeatures = null;
345
+ cachedDisableIpify = false;
346
+ resetFeatureConfigCache();
347
+ }
348
+
349
+ //#endregion
350
+ //#region src/modules/id/types.ts
351
+ const ID_ERROR_CODES = {
352
+ UPLOAD_ERROR: "UPLOAD_ERROR",
353
+ CLASSIFICATION_FAILED: "CLASSIFICATION_FAILED",
354
+ LOW_SHARPNESS: "LOW_SHARPNESS",
355
+ GLARE_DETECTED: "GLARE_DETECTED",
356
+ WRONG_DOCUMENT_SIDE: "WRONG_DOCUMENT_SIDE",
357
+ ID_TYPE_UNACCEPTABLE: "ID_TYPE_UNACCEPTABLE",
358
+ READABILITY_ISSUE: "READABILITY_ISSUE",
359
+ RETRY_EXHAUSTED_CONTINUE_TO_BACK: "RETRY_EXHAUSTED_CONTINUE_TO_BACK",
360
+ RETRY_EXHAUSTED_SKIP_BACK: "RETRY_EXHAUSTED_SKIP_BACK",
361
+ NO_MORE_TRIES: "NO_MORE_TRIES",
362
+ UNEXPECTED_ERROR: "UNEXPECTED_ERROR",
363
+ NO_TOKEN: "NO_TOKEN",
364
+ PERMISSION_DENIED: "PERMISSION_DENIED",
365
+ USER_CANCELLED: "USER_CANCELLED",
366
+ SERVER: "SERVER_ERROR"
367
+ };
368
+
369
+ //#endregion
370
+ //#region src/internal/analytics/deviceStats.ts
371
+ async function postDeviceStats(stats) {
372
+ try {
373
+ await api.post(endpoints.deviceStats, stats);
374
+ } catch {}
375
+ }
376
+
377
+ //#endregion
378
+ //#region src/modules/id/idCameraStream.ts
379
+ const BACK_CAMERA_KEYWORDS = [
380
+ "rear",
381
+ "back",
382
+ "rück",
383
+ "arrière",
384
+ "trasera",
385
+ "trás",
386
+ "traseira",
387
+ "posteriore",
388
+ "后面",
389
+ "後面",
390
+ "背面",
391
+ "后置",
392
+ "後置",
393
+ "背置",
394
+ "задней",
395
+ "الخلفية",
396
+ "후",
397
+ "arka",
398
+ "achterzijde",
399
+ "หลัง",
400
+ "baksidan",
401
+ "bagside",
402
+ "sau",
403
+ "bak",
404
+ "tylny",
405
+ "takakamera",
406
+ "belakang",
407
+ "אחורית",
408
+ "πίσω",
409
+ "spate",
410
+ "hátsó",
411
+ "zadní",
412
+ "darrere",
413
+ "zadná",
414
+ "задня",
415
+ "stražnja",
416
+ "बैक"
417
+ ];
418
+ function isBackCameraLabel(label) {
419
+ const lowercaseLabel = label.toLowerCase();
420
+ return BACK_CAMERA_KEYWORDS.some((keyword) => lowercaseLabel.includes(keyword));
421
+ }
422
+ function classifyCamera(device, index, totalDevices) {
423
+ let cameraType;
424
+ if (device.label === "") cameraType = totalDevices === 1 || index + 1 <= totalDevices / 2 ? "front" : "back";
425
+ else cameraType = isBackCameraLabel(device.label) ? "back" : "front";
426
+ return {
427
+ deviceId: device.deviceId,
428
+ label: device.label,
429
+ cameraType
430
+ };
431
+ }
432
+ async function getCameras() {
433
+ const videoDevices = await enumerateVideoDevices();
434
+ const cameras = videoDevices.map((d, i) => classifyCamera(d, i, videoDevices.length));
435
+ if (cameras.length > 1 && !cameras.some((c) => c.cameraType === "back")) {
436
+ const resolutions = cameras.map((c) => {
437
+ const match = c.label.match(/\b([0-9]+)MP?\b/i);
438
+ return match ? parseInt(match[1], 10) : NaN;
439
+ });
440
+ let backCameraIndex = cameras.length - 1;
441
+ if (!resolutions.some(isNaN)) backCameraIndex = resolutions.lastIndexOf(Math.max(...resolutions));
442
+ cameras[backCameraIndex].cameraType = "back";
443
+ }
444
+ return cameras;
445
+ }
446
+ function selectMainCameraFromStream(track, cameras) {
447
+ const settings = track.getSettings();
448
+ const activeCamera = cameras.find((c) => c.deviceId === settings.deviceId || c.label !== "" && c.label === track.label);
449
+ if (!activeCamera) return void 0;
450
+ if ((settings.facingMode === "environment" || isBackCameraLabel(track.label)) && cameras.length > 1) {
451
+ cameras.forEach((camera) => {
452
+ if (camera.deviceId === activeCamera.deviceId) camera.cameraType = "back";
453
+ else if (!isBackCameraLabel(camera.label)) camera.cameraType = "front";
454
+ });
455
+ return cameras.filter((c) => c.cameraType === "back").sort((a, b) => a.label.localeCompare(b.label))[0];
456
+ }
457
+ if (cameras.length === 1) return activeCamera;
458
+ }
459
+ function getAndroidVideoConstraints(level) {
460
+ const base = {
461
+ resizeMode: "none",
462
+ facingMode: "environment"
463
+ };
464
+ switch (level) {
465
+ case 0: return {
466
+ ...base,
467
+ height: { ideal: 720 },
468
+ aspectRatio: { ideal: 19.5 / 9 }
469
+ };
470
+ case 1: return {
471
+ ...base,
472
+ width: {
473
+ min: 3200,
474
+ ideal: 3840,
475
+ max: 4096
476
+ },
477
+ height: {
478
+ min: 1800,
479
+ ideal: 2160,
480
+ max: 2400
481
+ }
482
+ };
483
+ case 2: return {
484
+ ...base,
485
+ width: {
486
+ min: 1400,
487
+ ideal: 1920,
488
+ max: 2160
489
+ },
490
+ height: {
491
+ min: 900,
492
+ ideal: 1080,
493
+ max: 1440
494
+ }
495
+ };
496
+ case 3: return {
497
+ ...base,
498
+ width: {
499
+ min: 640,
500
+ ideal: 640,
501
+ max: 800
502
+ },
503
+ height: {
504
+ min: 480,
505
+ ideal: 480,
506
+ max: 600
507
+ }
508
+ };
509
+ case 4: return {
510
+ ...base,
511
+ width: {
512
+ min: 640,
513
+ ideal: 800,
514
+ max: 960
515
+ },
516
+ height: {
517
+ min: 480,
518
+ ideal: 480,
519
+ max: 480
520
+ }
521
+ };
522
+ default: return {};
523
+ }
524
+ }
525
+ async function getAndroidBackCameraStream(fallbackLevel = 0) {
526
+ if (fallbackLevel > 4) throw new Error("Failed to get camera after all fallback attempts");
527
+ try {
528
+ const initialStream = await requestCameraAccess({ video: getAndroidVideoConstraints(fallbackLevel) });
529
+ const track = initialStream.getVideoTracks()[0];
530
+ const mainCamera = selectMainCameraFromStream(track, await getCameras());
531
+ stopCameraStream(initialStream);
532
+ if (!mainCamera) throw new Error("Could not identify main camera");
533
+ let idealWidth = 1280;
534
+ let idealHeight = 720;
535
+ if (fallbackLevel > 1) {
536
+ const constraints = getAndroidVideoConstraints(fallbackLevel);
537
+ const width = constraints.width;
538
+ const height = constraints.height;
539
+ idealWidth = width?.ideal ?? 1280;
540
+ idealHeight = height?.ideal ?? 720;
541
+ }
542
+ return await requestCameraAccess({ video: {
543
+ deviceId: { exact: mainCamera.deviceId },
544
+ width: { ideal: idealWidth },
545
+ height: { ideal: idealHeight }
546
+ } });
547
+ } catch (error) {
548
+ const errorName = error instanceof Error ? error.name : "UnknownError";
549
+ const nextLevel = Math.min(fallbackLevel + 1, 4);
550
+ if (errorName === "NotReadableError") {
551
+ await sleep(300);
552
+ return getAndroidBackCameraStream(nextLevel);
553
+ }
554
+ if (errorName === "AbortError") {
555
+ await sleep(300);
556
+ return getAndroidBackCameraStream(fallbackLevel);
557
+ }
558
+ return getAndroidBackCameraStream(nextLevel);
559
+ }
560
+ }
561
+ async function applyIOSFocusHack(stream) {
562
+ const videoTrack = stream.getVideoTracks()[0];
563
+ try {
564
+ await applyTrackConstraints(videoTrack, { advanced: [{ focusDistance: 1 }] });
565
+ } catch {}
566
+ }
567
+ function getIOSConstraints() {
568
+ return {
569
+ audio: false,
570
+ video: {
571
+ resizeMode: "none",
572
+ facingMode: "environment",
573
+ height: { ideal: isIPhone14OrHigher() ? 1080 : 720 },
574
+ aspectRatio: { ideal: 19.5 / 9 }
575
+ }
576
+ };
577
+ }
578
+ async function getIOSCameraStream() {
579
+ const stream = await requestCameraAccess(getIOSConstraints());
580
+ await applyIOSFocusHack(stream);
581
+ return stream;
582
+ }
583
+ function getDesktopConstraints(options) {
584
+ const { deviceId } = options;
585
+ return {
586
+ audio: isSafari(),
587
+ video: {
588
+ facingMode: "user",
589
+ deviceId: deviceId ? { exact: deviceId } : void 0,
590
+ height: { ideal: 1080 },
591
+ width: { ideal: 1920 }
592
+ }
593
+ };
594
+ }
595
+ async function getDesktopCameraStream(options) {
596
+ return requestCameraAccess(getDesktopConstraints(options));
597
+ }
598
+ async function getIdCameraStream(deviceId) {
599
+ if (isIOS()) return getIOSCameraStream();
600
+ if (isAndroid()) return getAndroidBackCameraStream(0);
601
+ if (isDesktop()) return getDesktopCameraStream({ deviceId });
602
+ return getDesktopCameraStream({ deviceId });
603
+ }
604
+
605
+ //#endregion
606
+ //#region src/modules/id/idCaptureServices.ts
607
+ /**
608
+ * Camera constraints used for PRC warmup before ID capture.
609
+ * Uses environment-facing camera on mobile devices (for back camera ID capture)
610
+ * and user-facing camera on desktop.
611
+ */
612
+ function getPrcCameraConstraints() {
613
+ if (isDesktop()) return { video: {
614
+ facingMode: "user",
615
+ height: { ideal: 480 },
616
+ width: { ideal: 640 }
617
+ } };
618
+ return { video: {
619
+ facingMode: "environment",
620
+ height: { ideal: 720 }
621
+ } };
622
+ }
623
+ const SHARPNESS_THRESHOLD = 10;
624
+ const GLARE_THRESHOLD = 10;
625
+ const DEFAULT_ID_CAPTURE_THRESHOLDS$1 = {
626
+ blurThreshold: .2,
627
+ blurChangeThreshold: .2,
628
+ glareThreshold: .3,
629
+ clsThreshold: .98,
630
+ sideThreshold: .8,
631
+ iouThreshold: .8,
632
+ framesAggregationInterval: 3e3,
633
+ minFaceIdQualityScore: .62
634
+ };
635
+ const DEFAULT_ID_CAPTURE_SETTINGS = {
636
+ isFixedMask: false,
637
+ isIPhone14OrHigher: false,
638
+ idType: "",
639
+ blurCheckEnabled: false,
640
+ glareCheckEnabled: false,
641
+ faceQualityCheckEnabled: true,
642
+ iouCheckEnabled: true
643
+ };
644
+ async function initializeIdCapture(params) {
645
+ const { provider, config, deepsightService } = params;
646
+ await provider.initialize({});
647
+ provider.setThresholds({
648
+ ...DEFAULT_ID_CAPTURE_THRESHOLDS$1,
649
+ ...config.thresholds,
650
+ idDetectedTimeout: config.thresholds?.idDetectedTimeout ?? config.deviceIdleTimeout * 1e3,
651
+ autocaptureTimeout: config.thresholds?.autocaptureTimeout ?? config.autoCaptureTimeout * 1e3
652
+ });
653
+ if (config.settings) provider.setSettings({
654
+ ...DEFAULT_ID_CAPTURE_SETTINGS,
655
+ ...config.settings
656
+ });
657
+ if (deepsightService) try {
658
+ await deepsightService.performPrcCheck({ constraints: getPrcCameraConstraints() });
659
+ } catch (error) {
660
+ console.warn("PRC check failed during ID capture initialization:", error);
661
+ }
662
+ const stream = await getIdCameraStream();
663
+ if (deepsightService) {
664
+ const videoTrack = stream.getVideoTracks()[0];
665
+ if (videoTrack) deepsightService.metadata.updateCameraInfo(videoTrack);
666
+ }
667
+ return {
668
+ stream,
669
+ provider
670
+ };
671
+ }
672
+ function stopStream(stream) {
673
+ for (const track of stream.getTracks()) track.stop();
674
+ }
675
+ function validateUploadResponse(response, sessionState) {
676
+ if (response.failReason === "ID_TYPE_UNACCEPTABLE") return {
677
+ error: true,
678
+ message: "ID type is not acceptable",
679
+ messageDescription: "Please use a valid ID type",
680
+ errorKey: ID_ERROR_CODES.ID_TYPE_UNACCEPTABLE
681
+ };
682
+ if (response.failReason === "WRONG_DOCUMENT_SIDE") return {
683
+ error: true,
684
+ message: "Wrong side of document",
685
+ messageDescription: response.side === "back" ? "Please show the back side of your ID" : "Please show the front side of your ID",
686
+ errorKey: ID_ERROR_CODES.WRONG_DOCUMENT_SIDE
687
+ };
688
+ if (!response.classification) return {
689
+ error: true,
690
+ message: "ID classification failed",
691
+ errorKey: ID_ERROR_CODES.CLASSIFICATION_FAILED
692
+ };
693
+ const sharpnessThreshold = getDeviceClass() === "desktop" ? -1 : SHARPNESS_THRESHOLD;
694
+ if (response.sharpness !== void 0 && sharpnessThreshold >= 0 && response.sharpness < sharpnessThreshold) return {
695
+ error: true,
696
+ message: "Image is not sharp enough",
697
+ messageDescription: "Please ensure the image is clear and well-focused",
698
+ errorKey: ID_ERROR_CODES.LOW_SHARPNESS
699
+ };
700
+ if (response.glare !== void 0 && response.glare < GLARE_THRESHOLD && !sessionState?.skipGlareFront && !sessionState?.skipGlareBack) return {
701
+ error: true,
702
+ message: "Glare detected on ID",
703
+ messageDescription: "Please avoid bright reflections on your ID",
704
+ errorKey: ID_ERROR_CODES.GLARE_DETECTED
705
+ };
706
+ }
707
+ async function getExtraImages(params) {
708
+ const extraImages = params.ageAssurance ? [params.type === "back" ? "croppedBackID" : "croppedFrontID"] : [];
709
+ try {
710
+ const res = await api.post(endpoints.getImages, { images: ["croppedIDFace", ...extraImages] }, { signal: params.signal });
711
+ if (!res.ok) throw new Error(`Failed to get extra images: ${res.status}`);
712
+ return res.data ?? {
713
+ croppedIDFace: "",
714
+ croppedFrontID: "",
715
+ croppedBackID: ""
716
+ };
717
+ } catch {
718
+ return {
719
+ croppedIDFace: "",
720
+ croppedFrontID: "",
721
+ croppedBackID: ""
722
+ };
723
+ }
724
+ }
725
+ const getRealQualityValue = (value) => (1 - value) * 100;
726
+ async function uploadIdImage(params) {
727
+ const { type, image, onProgress, signal, metadata, ageAssurance, glare, sharpness, shouldSkipGlareBack, analyticsProvider, imageData } = params;
728
+ addEvent({
729
+ code: "captureAttemptFinished",
730
+ module: eventModuleNames.id,
731
+ payload: { logs: [] }
732
+ });
733
+ let finalMetadata = metadata;
734
+ if (analyticsProvider && imageData) try {
735
+ await analyticsProvider.analyzeFrame(imageData);
736
+ analyticsProvider.update();
737
+ const analysisStatus = analyticsProvider.getAnalysisStatus();
738
+ const motionStatus = analyticsProvider.getMotionStatus();
739
+ await postDeviceStats({
740
+ frontIdStatsAnalysisStatus: type === "front" ? analysisStatus : void 0,
741
+ backIdStatsAnalysisStatus: type === "back" ? analysisStatus : void 0,
742
+ motionStatus
743
+ });
744
+ finalMetadata = analyticsProvider.getMetadata();
745
+ } catch (error) {
746
+ console.warn("[IdCapture] Analytics failed:", error);
747
+ }
748
+ const endpoint = type === "front" ? endpoints.frontId : endpoints.backId;
749
+ const body = {
750
+ base64Image: image,
751
+ metadata: finalMetadata
752
+ };
753
+ const queryParams = { imageType: "id" };
754
+ if (shouldSkipGlareBack && type === "back") queryParams.glare = 0;
755
+ else if (glare !== void 0) queryParams.glare = getRealQualityValue(glare);
756
+ if (sharpness !== void 0) queryParams.sharpness = getRealQualityValue(sharpness);
757
+ try {
758
+ const res = await api.post(endpoint, body, {
759
+ signal,
760
+ query: queryParams,
761
+ onUploadProgress: onProgress
762
+ });
763
+ if (!res.ok) throw new Error(`POST ${endpoint} failed: ${res.status} ${res.statusText}`);
764
+ const response = res.data;
765
+ const extraImages = await getExtraImages({
766
+ type,
767
+ ageAssurance,
768
+ signal
769
+ });
770
+ const fullResponse = {
771
+ ...response,
772
+ originalImage: image,
773
+ frontIdImage: type === "front" ? image : void 0,
774
+ backIdImage: extraImages.croppedBackID,
775
+ ...extraImages
776
+ };
777
+ onProgress?.(100);
778
+ return fullResponse;
779
+ } catch (error) {
780
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
781
+ throw new Error(`${ID_ERROR_CODES.UPLOAD_ERROR}: ${errorMessage}`);
782
+ }
783
+ }
784
+ function buildStandardResolution(width, height) {
785
+ return height > width ? {
786
+ width: 1080,
787
+ height: 1920
788
+ } : {
789
+ width: 1920,
790
+ height: 1080
791
+ };
792
+ }
793
+ function buildResolutionFromStream(stream) {
794
+ const track = stream.getVideoTracks()[0];
795
+ if (!track) return "1080x1920";
796
+ const settings = track.getSettings();
797
+ const width = settings.width;
798
+ const height = settings.height;
799
+ if (typeof width === "number" && typeof height === "number") {
800
+ const standard = buildStandardResolution(width, height);
801
+ return `${standard.width}x${standard.height}`;
802
+ }
803
+ return "1080x1920";
804
+ }
805
+ async function startRecordingSession(params) {
806
+ if (params.config.enableIdRecording !== true) return;
807
+ if (params.existing) return params.existing;
808
+ const provider = params.config.recording?.capability ?? new OpenViduRecordingProvider();
809
+ const clonedStream = params.stream.clone();
810
+ const hasAudio = clonedStream.getAudioTracks().length > 0;
811
+ const resolution = buildResolutionFromStream(clonedStream);
812
+ const session = await createRecordingSession(params.type);
813
+ const connection = await provider.connect({
814
+ sessionToken: session.token,
815
+ stream: clonedStream,
816
+ events: {
817
+ onSessionConnected: (sessionId) => {
818
+ addEvent({
819
+ code: streamingEvents.strSessionDidConnect,
820
+ module: eventModuleNames.id,
821
+ payload: {
822
+ message: "Recording session connected",
823
+ sessionId
824
+ }
825
+ });
826
+ },
827
+ onSessionDisconnected: (sessionId) => {
828
+ addEvent({
829
+ code: streamingEvents.strSessionDidDisconnect,
830
+ module: eventModuleNames.id,
831
+ payload: {
832
+ message: "Recording session disconnected",
833
+ sessionId
834
+ }
835
+ });
836
+ },
837
+ onSessionException: (event) => {
838
+ addEvent({
839
+ code: streamingEvents.strSessionDidFailWithError,
840
+ module: eventModuleNames.id,
841
+ payload: {
842
+ message: "Recording session failed due to an error",
843
+ eventName: event.name,
844
+ type: "OpenViduException",
845
+ errorMessage: event.message,
846
+ sessionId: event.sessionId
847
+ }
848
+ });
849
+ },
850
+ onPublisherCreated: (p) => {
851
+ addEvent({
852
+ code: streamingEvents.strStreamPublisherCreated,
853
+ module: eventModuleNames.id,
854
+ payload: {
855
+ message: "Recording publisher created",
856
+ sessionId: p.sessionId,
857
+ streamId: p.streamId
858
+ }
859
+ });
860
+ },
861
+ onPublisherError: (p) => {
862
+ addEvent({
863
+ code: streamingEvents.strStreamPublisherDidFailWithError,
864
+ module: eventModuleNames.id,
865
+ payload: {
866
+ message: "Recording publisher failed due to an error",
867
+ sessionId: p.sessionId,
868
+ streamId: p.streamId,
869
+ error: { message: p.message ?? "Unknown error" }
870
+ }
871
+ });
872
+ }
873
+ }
874
+ });
875
+ await startRecording({
876
+ videoRecordingId: session.videoRecordingId,
877
+ type: params.type,
878
+ resolution,
879
+ hasAudio
880
+ });
881
+ addEvent({
882
+ code: streamingEvents.strStreamVideoCaptureStart,
883
+ module: eventModuleNames.id,
884
+ payload: {
885
+ message: "Recording capture started",
886
+ resolution,
887
+ videoRecordingId: session.videoRecordingId,
888
+ sessionId: session.sessionId,
889
+ streamId: connection.publisher.getStreamId()
890
+ }
891
+ });
892
+ return {
893
+ token: session.token,
894
+ sessionId: session.sessionId,
895
+ videoRecordingId: session.videoRecordingId,
896
+ connection,
897
+ resolution,
898
+ hasAudio
899
+ };
900
+ }
901
+ function stopRecording(session) {
902
+ (async () => {
903
+ try {
904
+ addEvent({
905
+ code: streamingEvents.strStreamVideoCaptureStop,
906
+ module: eventModuleNames.id,
907
+ payload: {
908
+ message: "Recording capture stopped",
909
+ videoRecordingId: session.videoRecordingId,
910
+ sessionId: session.sessionId,
911
+ streamId: session.connection.publisher.getStreamId()
912
+ }
913
+ });
914
+ await stopRecording$1(session.videoRecordingId);
915
+ addEvent({
916
+ code: streamingEvents.strStreamPublisherDestroyed,
917
+ module: eventModuleNames.id,
918
+ payload: {
919
+ message: "Recording publisher destroyed",
920
+ sessionId: session.sessionId,
921
+ streamId: session.connection.publisher.getStreamId()
922
+ }
923
+ });
924
+ } finally {
925
+ await session.connection.disconnect();
926
+ addEvent({
927
+ code: streamingEvents.strSessionDidDisconnect,
928
+ module: eventModuleNames.id,
929
+ payload: {
930
+ message: "Recording session disconnected",
931
+ sessionId: session.sessionId
932
+ }
933
+ });
934
+ }
935
+ })();
936
+ }
937
+ async function processId(isSecondId = false, queueName = "", signal) {
938
+ const endpoint = isSecondId ? endpoints.processSecondId : endpoints.processId;
939
+ const url = queueName ? `${endpoint}?queueName=${queueName}` : endpoint;
940
+ return { isDocumentExpired: (await api.post(url, {}, { signal })).data?.isDocumentExpired ?? false };
941
+ }
942
+
943
+ //#endregion
944
+ //#region src/modules/id/idCaptureActors.ts
945
+ const checkPermissionActor = fromPromise(async () => {
946
+ return checkPermission();
947
+ });
948
+ const requestPermissionActor = fromPromise(async () => {
949
+ return requestPermission();
950
+ });
951
+ const initializeCameraActor = fromPromise(async ({ input }) => {
952
+ return initializeIdCapture(input);
953
+ });
954
+ const runDetectionActor = fromCallback(({ input, sendBack }) => {
955
+ if (!input.frameCapturer || !input.provider) {
956
+ sendBack({
957
+ type: "DETECTION_UPDATE",
958
+ status: "error"
959
+ });
960
+ return () => {};
961
+ }
962
+ const provider = input.provider;
963
+ let currentCanvas = null;
964
+ let bestCanvas = null;
965
+ let storedQualityElements = {};
966
+ let isCapturing = false;
967
+ const timer = BrowserTimerProvider.getInstance();
968
+ let notificationTimeout = null;
969
+ let currentNotificationStatus = null;
970
+ const NOTIFICATION_DURATION_MS = 500;
971
+ const clearNotificationTimeout = () => {
972
+ if (notificationTimeout) {
973
+ timer.clearTimeout(notificationTimeout);
974
+ notificationTimeout = null;
975
+ currentNotificationStatus = null;
976
+ }
977
+ };
978
+ const sendNotification = (status) => {
979
+ if (notificationTimeout && currentNotificationStatus !== status) return;
980
+ if (notificationTimeout && currentNotificationStatus === status) timer.clearTimeout(notificationTimeout);
981
+ currentNotificationStatus = status;
982
+ sendBack({
983
+ type: "DETECTION_UPDATE",
984
+ status
985
+ });
986
+ notificationTimeout = timer.setTimeout(() => {
987
+ notificationTimeout = null;
988
+ currentNotificationStatus = null;
989
+ sendBack({
990
+ type: "DETECTION_UPDATE",
991
+ status: "detecting"
992
+ });
993
+ }, NOTIFICATION_DURATION_MS);
994
+ };
995
+ const canvas = input.frameCapturer.getLatestCanvas();
996
+ const windowDimensions = getWindowDimensions(canvas?.width() ?? 1280, canvas?.height() ?? 720);
997
+ const DEFAULT_GEOMETRY = {
998
+ areaDown: 25e3,
999
+ areaUp: 55e3,
1000
+ areaIOSPassportUp: 3e4,
1001
+ areaIOSPassportDown: 2e4,
1002
+ widthIOSUp: 160,
1003
+ widthIOSDown: 85,
1004
+ widthDown: 110,
1005
+ widthUp: 205
1006
+ };
1007
+ if (input.config.geometry) provider.setGeometry({
1008
+ ...input.config.geometry,
1009
+ windowOuterWidth: windowDimensions.outerWidth,
1010
+ windowOuterHeight: windowDimensions.outerHeight,
1011
+ windowInnerWidth: windowDimensions.innerWidth,
1012
+ windowInnerHeight: windowDimensions.innerHeight
1013
+ });
1014
+ else provider.setGeometry({
1015
+ ...DEFAULT_GEOMETRY,
1016
+ windowOuterWidth: windowDimensions.outerWidth,
1017
+ windowOuterHeight: windowDimensions.outerHeight,
1018
+ windowInnerWidth: windowDimensions.innerWidth,
1019
+ windowInnerHeight: windowDimensions.innerHeight
1020
+ });
1021
+ const idType = input.currentMode === "back" ? "BackId" : input.currentMode === "passport" || input.currentMode === "front" && input.config.onlyFront ? "Passport" : "FrontId";
1022
+ provider.setSettings({
1023
+ isFixedMask: input.config.settings?.isFixedMask ?? false,
1024
+ isIPhone14OrHigher: input.config.settings?.isIPhone14OrHigher ?? false,
1025
+ idType,
1026
+ blurCheckEnabled: input.config.settings?.blurCheckEnabled ?? false,
1027
+ glareCheckEnabled: input.config.settings?.glareCheckEnabled ?? false,
1028
+ faceQualityCheckEnabled: input.config.settings?.faceQualityCheckEnabled ?? true,
1029
+ iouCheckEnabled: input.config.settings?.iouCheckEnabled ?? true
1030
+ });
1031
+ const thresholds = {
1032
+ ...DEFAULT_ID_CAPTURE_THRESHOLDS,
1033
+ ...input.config.thresholds,
1034
+ idDetectedTimeout: (input.config.deviceIdleTimeout ?? 10) * 1e3,
1035
+ autocaptureTimeout: (input.config.autoCaptureTimeout ?? 5) * 1e3
1036
+ };
1037
+ provider.setThresholds(thresholds);
1038
+ const modelType = input.config.modelVersion ?? DEFAULT_ID_CAPTURE_MODEL_VERSION;
1039
+ provider.setModelType(modelType);
1040
+ provider.setCallbacks({
1041
+ onFarAway: () => {
1042
+ if (!isCapturing) sendNotification("farAway");
1043
+ },
1044
+ onDetectionStarted: () => {},
1045
+ onMaskChange: (_show, _mask, _top, orientation) => {
1046
+ sendBack({
1047
+ type: "ORIENTATION_CHANGE",
1048
+ orientation
1049
+ });
1050
+ },
1051
+ onBlur: () => {
1052
+ if (!isCapturing) sendNotification("blur");
1053
+ },
1054
+ onGlare: () => {
1055
+ if (!isCapturing) sendNotification("glare");
1056
+ },
1057
+ onIdNotDetected: () => {
1058
+ if (!isCapturing) sendBack({
1059
+ type: "DETECTION_UPDATE",
1060
+ status: "idNotDetected"
1061
+ });
1062
+ },
1063
+ onSwitchToManualCapture: () => {
1064
+ isCapturing = false;
1065
+ sendBack({
1066
+ type: "DETECTION_UPDATE",
1067
+ status: "manualCapture"
1068
+ });
1069
+ },
1070
+ onCapturing: () => {
1071
+ clearNotificationTimeout();
1072
+ isCapturing = true;
1073
+ sendBack({
1074
+ type: "DETECTION_UPDATE",
1075
+ status: "capturing"
1076
+ });
1077
+ },
1078
+ onBestFrame: (blur, glare, orientation) => {
1079
+ if (currentCanvas) {
1080
+ bestCanvas = currentCanvas.clone();
1081
+ storedQualityElements = {
1082
+ glare,
1083
+ sharpness: blur
1084
+ };
1085
+ }
1086
+ if (orientation === "horizontal" || orientation === "vertical") sendBack({
1087
+ type: "ORIENTATION_CHANGE",
1088
+ orientation
1089
+ });
1090
+ },
1091
+ onCapture: () => {
1092
+ isCapturing = false;
1093
+ if (bestCanvas) sendBack({
1094
+ type: "DETECTION_SUCCESS",
1095
+ canvas: bestCanvas,
1096
+ qualityElements: storedQualityElements
1097
+ });
1098
+ else if (currentCanvas) sendBack({
1099
+ type: "DETECTION_SUCCESS",
1100
+ canvas: currentCanvas.clone(),
1101
+ qualityElements: {}
1102
+ });
1103
+ },
1104
+ onIdTypeChange: (idType$1) => {
1105
+ sendBack({
1106
+ type: "ID_TYPE_CHANGE",
1107
+ idType: idType$1
1108
+ });
1109
+ },
1110
+ onIdSideChange: (side) => {
1111
+ if (isCapturing) return;
1112
+ sendBack({
1113
+ type: "ID_SIDE_CHANGE",
1114
+ side
1115
+ });
1116
+ },
1117
+ onCapturingCounterValueChange: (value) => {
1118
+ sendBack({
1119
+ type: "COUNTER_VALUE_CHANGE",
1120
+ value
1121
+ });
1122
+ }
1123
+ });
1124
+ sendBack({
1125
+ type: "DETECTION_UPDATE",
1126
+ status: "detecting"
1127
+ });
1128
+ const session = new StreamCanvasProcessingSession({
1129
+ capturer: input.frameCapturer,
1130
+ provider: {
1131
+ processFrame: async (frame) => {
1132
+ currentCanvas = IncodeCanvas.fromImageData(frame);
1133
+ sendBack({
1134
+ type: "DETECTION_FRAME",
1135
+ frame
1136
+ });
1137
+ await provider.processFrame(frame);
1138
+ },
1139
+ reset: () => {
1140
+ if (notificationTimeout) {
1141
+ timer.clearTimeout(notificationTimeout);
1142
+ notificationTimeout = null;
1143
+ currentNotificationStatus = null;
1144
+ }
1145
+ currentCanvas = null;
1146
+ bestCanvas = null;
1147
+ storedQualityElements = {};
1148
+ isCapturing = false;
1149
+ provider.reset();
1150
+ }
1151
+ },
1152
+ onFrame: (frame) => sendBack({
1153
+ type: "DETECTION_FRAME",
1154
+ frame
1155
+ })
1156
+ });
1157
+ sendBack({
1158
+ type: "DETECTION_RESET_READY",
1159
+ reset: () => {
1160
+ provider.reset();
1161
+ }
1162
+ });
1163
+ return () => {
1164
+ clearNotificationTimeout();
1165
+ session?.dispose();
1166
+ };
1167
+ });
1168
+ const uploadIdImageActor = fromPromise(async ({ input, signal }) => {
1169
+ const image = input.canvas.getBase64Image();
1170
+ if (!image) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
1171
+ let metadata;
1172
+ if (input.deepsightService) try {
1173
+ const sessionToken = getToken();
1174
+ const frameSource = input.type === "back" ? "BACK_ID" : "FRONT_ID";
1175
+ await input.deepsightService.performVirtualCameraCheck(sessionToken, frameSource);
1176
+ const imageData$1 = input.canvas.getImageData();
1177
+ if (imageData$1) await input.deepsightService.analyzeFrame(imageData$1);
1178
+ const analysisStatus = input.deepsightService.getAnalysisStatus();
1179
+ const motionStatus = input.deepsightService.getMotionStatus();
1180
+ if (input.type === "front") await addDeviceStats({
1181
+ frontIdStatsAnalysisStatus: analysisStatus,
1182
+ backIdStatsAnalysisStatus: "",
1183
+ selfieStatsAnalysisStatus: "",
1184
+ motionStatus
1185
+ });
1186
+ else await addDeviceStats({
1187
+ frontIdStatsAnalysisStatus: "",
1188
+ backIdStatsAnalysisStatus: analysisStatus,
1189
+ selfieStatsAnalysisStatus: "",
1190
+ motionStatus
1191
+ });
1192
+ metadata = input.deepsightService.getMetadata();
1193
+ } catch {}
1194
+ const imageData = input.canvas?.getImageData() ?? void 0;
1195
+ return uploadIdImage({
1196
+ image,
1197
+ type: input.type,
1198
+ sendBase64: true,
1199
+ glare: input.qualityElements?.glare,
1200
+ sharpness: input.qualityElements?.sharpness,
1201
+ ageAssurance: false,
1202
+ signal,
1203
+ onProgress: input.onProgress,
1204
+ metadata,
1205
+ analyticsProvider: input.analyticsProvider,
1206
+ imageData
1207
+ });
1208
+ });
1209
+ const processIdActor = fromPromise(async ({ input, signal }) => {
1210
+ return processId(input.isSecondId, "", signal);
1211
+ });
1212
+ const startRecordingActor = fromPromise(async ({ input }) => {
1213
+ if (!input.stream) return;
1214
+ const type = input.currentMode === "back" ? "backId" : "frontId";
1215
+ return startRecordingSession({
1216
+ config: input.config,
1217
+ stream: input.stream,
1218
+ existing: input.existing,
1219
+ type
1220
+ });
1221
+ });
1222
+ const checkMotionSensorActor = fromCallback(({ input, sendBack }) => {
1223
+ if (!input.motionProvider) {
1224
+ sendBack({
1225
+ type: "MOTION_STATUS",
1226
+ status: "UNCLEAR"
1227
+ });
1228
+ return () => {};
1229
+ }
1230
+ const timer = BrowserTimerProvider.getInstance();
1231
+ const interval = timer.setInterval(() => {
1232
+ sendBack({
1233
+ type: "MOTION_STATUS",
1234
+ status: input.motionProvider.check()
1235
+ });
1236
+ }, 500);
1237
+ return () => timer.clearInterval(interval);
1238
+ });
1239
+ const initializeDeepsightSessionActor = fromPromise(async ({ input }) => {
1240
+ if (!input.dependencies) return;
1241
+ const { loadDeepsightSession } = await import("./deepsightLoader-CGdK3U_s.esm.js");
1242
+ return loadDeepsightSession({
1243
+ ds: input.ds,
1244
+ storage: input.dependencies.storage,
1245
+ disableIpify: input.disableIpify
1246
+ });
1247
+ });
1248
+ const checkVirtualCameraActor = fromPromise(async ({ input }) => {
1249
+ if (!input.deepsightService || !input.stream) return false;
1250
+ const videoTrack = input.stream.getVideoTracks()[0];
1251
+ if (!videoTrack) return false;
1252
+ return input.deepsightService.checkVirtualCamera(videoTrack);
1253
+ });
1254
+
1255
+ //#endregion
1256
+ //#region src/modules/id/idCaptureActions.ts
1257
+ function performStopMediaStream(context) {
1258
+ context.frameCapturer?.dispose();
1259
+ if (context.stream) stopStream(context.stream);
1260
+ }
1261
+ function performDisposeProvider(context) {
1262
+ context.provider?.dispose?.();
1263
+ }
1264
+ function performCleanupDeepsight(context) {
1265
+ context.deepsightService?.cleanup();
1266
+ }
1267
+ function performResetForBackCapture(context) {
1268
+ context.frameCapturer?.dispose();
1269
+ if (context.stream) stopStream(context.stream);
1270
+ context.provider?.reset();
1271
+ }
1272
+ function performResetDetection(context) {
1273
+ context.resetDetection?.();
1274
+ }
1275
+ function performTrackTutorialId() {
1276
+ addEvent({
1277
+ code: "tutorialVideoStarted",
1278
+ module: eventModuleNames.id,
1279
+ payload: { tutorialFrontID: true }
1280
+ });
1281
+ }
1282
+ function performTrackContinue() {}
1283
+ function performStopMediaRecording(context) {
1284
+ if (context.recordingSession) stopRecording(context.recordingSession);
1285
+ }
1286
+ function getStreamFromEvent(event) {
1287
+ if ("output" in event) return event.output.stream;
1288
+ }
1289
+ function getProviderFromEvent(event) {
1290
+ if ("output" in event) return event.output.provider;
1291
+ }
1292
+ function getFrameCapturerFromEvent(event) {
1293
+ if ("output" in event) {
1294
+ const output = event.output;
1295
+ if (output.stream) return new StreamCanvasCapture(output.stream);
1296
+ }
1297
+ }
1298
+ function getResetContextValues(context) {
1299
+ context.provider?.reset();
1300
+ const currentMode = context.config.onlyBack ? "back" : !context.config.enableId && context.config.enablePassport ? "passport" : "front";
1301
+ return {
1302
+ stream: void 0,
1303
+ provider: context.provider,
1304
+ frameCapturer: void 0,
1305
+ error: void 0,
1306
+ detectionStatus: "idle",
1307
+ counterValue: 0,
1308
+ orientation: void 0,
1309
+ capturedImages: {},
1310
+ uploadResponse: void 0,
1311
+ recordingSession: void 0,
1312
+ attemptsRemaining: context.config.captureAttempts,
1313
+ uploadError: void 0,
1314
+ permissionResult: void 0,
1315
+ resetDetection: void 0,
1316
+ idType: void 0,
1317
+ qualityElements: void 0,
1318
+ previewImageUrl: void 0,
1319
+ uploadProgress: void 0,
1320
+ currentMode,
1321
+ selectedDocumentType: void 0,
1322
+ manualCaptureTriggered: false
1323
+ };
1324
+ }
1325
+ function getClearUploadFailureValues(context) {
1326
+ if (context.previewImageUrl) revokeObjectURL(context.previewImageUrl);
1327
+ return {
1328
+ uploadError: void 0,
1329
+ detectionStatus: "idle",
1330
+ previewImageUrl: void 0,
1331
+ uploadProgress: void 0
1332
+ };
1333
+ }
1334
+ function getUploadValidationError(context) {
1335
+ if (!context.uploadResponse) return ID_ERROR_CODES.SERVER;
1336
+ return validateUploadResponse(context.uploadResponse, {
1337
+ skipGlareFront: context.uploadResponse.skipGlareFront,
1338
+ skipGlareBack: context.uploadResponse.skipGlareBack
1339
+ })?.errorKey ?? ID_ERROR_CODES.SERVER;
1340
+ }
1341
+ function storeCapturedCanvasInProviderLogic(context, event) {
1342
+ let canvas = null;
1343
+ if ("canvas" in event && event.canvas) canvas = event.canvas;
1344
+ else canvas = context.frameCapturer?.getLatestCanvas() ?? null;
1345
+ if (!canvas || !context.provider) return;
1346
+ const canvasWidth = canvas.width();
1347
+ const canvasHeight = canvas.height();
1348
+ if (!canvasWidth || !canvasHeight) return;
1349
+ const originalCanvas = canvas;
1350
+ let frameRect;
1351
+ const { innerWidth: viewportWidth, innerHeight: viewportHeight } = getWindowDimensions();
1352
+ if (context.detectionArea) {
1353
+ const scaleX = viewportWidth / canvasWidth;
1354
+ const scaleY = viewportHeight / canvasHeight;
1355
+ const scale = Math.max(scaleX, scaleY);
1356
+ const displayedWidth = canvasWidth * scale;
1357
+ const displayedHeight = canvasHeight * scale;
1358
+ const offsetX = (viewportWidth - displayedWidth) / 2;
1359
+ const offsetY = (viewportHeight - displayedHeight) / 2;
1360
+ frameRect = {
1361
+ x: (context.detectionArea.x - offsetX) / scale,
1362
+ y: (context.detectionArea.y - offsetY) / scale,
1363
+ w: context.detectionArea.width / scale,
1364
+ h: context.detectionArea.height / scale
1365
+ };
1366
+ } else if (context.frameRect) {
1367
+ const scaleX = viewportWidth / canvasWidth;
1368
+ const scaleY = viewportHeight / canvasHeight;
1369
+ const scale = Math.max(scaleX, scaleY);
1370
+ const displayedWidth = canvasWidth * scale;
1371
+ const displayedHeight = canvasHeight * scale;
1372
+ const offsetX = (viewportWidth - displayedWidth) / 2;
1373
+ const offsetY = (viewportHeight - displayedHeight) / 2;
1374
+ frameRect = {
1375
+ x: (context.frameRect.x - offsetX) / scale,
1376
+ y: (context.frameRect.y - offsetY) / scale,
1377
+ w: context.frameRect.w / scale,
1378
+ h: context.frameRect.h / scale
1379
+ };
1380
+ } else {
1381
+ const quadValue = (context.provider.getLastProcessResult?.())?.quad;
1382
+ const hasQuad = !!quadValue;
1383
+ const quadSize = quadValue?.size ? quadValue.size() : quadValue?.length ?? 0;
1384
+ if (hasQuad && quadSize >= 4 && quadValue.get) {
1385
+ const p0 = quadValue.get(0);
1386
+ const p1 = quadValue.get(1);
1387
+ const p2 = quadValue.get(2);
1388
+ const p3 = quadValue.get(3);
1389
+ const minX = Math.min(p0.x, p1.x, p2.x, p3.x);
1390
+ const maxX = Math.max(p0.x, p1.x, p2.x, p3.x);
1391
+ const minY = Math.min(p0.y, p1.y, p2.y, p3.y);
1392
+ const maxY = Math.max(p0.y, p1.y, p2.y, p3.y);
1393
+ frameRect = {
1394
+ x: minX,
1395
+ y: minY,
1396
+ w: maxX - minX,
1397
+ h: maxY - minY
1398
+ };
1399
+ } else {
1400
+ const frameViewportWidth = Math.min(387, viewportWidth * .9);
1401
+ const frameViewportHeight = frameViewportWidth / (35 / 22);
1402
+ const frameViewportX = (viewportWidth - frameViewportWidth) / 2;
1403
+ const frameViewportY = (viewportHeight - frameViewportHeight) / 2;
1404
+ frameRect = {
1405
+ x: canvasWidth * frameViewportX / viewportWidth,
1406
+ y: canvasHeight * frameViewportY / viewportHeight,
1407
+ w: canvasWidth * frameViewportWidth / viewportWidth,
1408
+ h: canvasHeight * frameViewportHeight / viewportHeight
1409
+ };
1410
+ }
1411
+ }
1412
+ const transformedCanvas = context.provider.transformPerspective(originalCanvas, frameRect);
1413
+ context.provider.setCapturedCanvases(originalCanvas, transformedCanvas);
1414
+ }
1415
+ function getStoreCapturedImageValues(context, event) {
1416
+ if (!context.currentMode) return {
1417
+ capturedImages: context.capturedImages,
1418
+ previewImageUrl: context.previewImageUrl
1419
+ };
1420
+ const transformedCanvas = context.provider?.getCapturedCanvas();
1421
+ const transformedImage = transformedCanvas?.getBase64Image(1, true);
1422
+ let fallbackImage = "";
1423
+ if ("output" in event) fallbackImage = event.output.originalImage ?? "";
1424
+ const imageData = { imageBase64: transformedImage ?? fallbackImage };
1425
+ let capturedImages = context.capturedImages;
1426
+ if (context.currentMode === "front" || context.currentMode === "passport") capturedImages = {
1427
+ ...context.capturedImages,
1428
+ front: imageData
1429
+ };
1430
+ else capturedImages = {
1431
+ ...context.capturedImages,
1432
+ back: imageData
1433
+ };
1434
+ let previewImageUrl = context.previewImageUrl;
1435
+ if (transformedCanvas) {
1436
+ transformedCanvas.updateBlob();
1437
+ const blobData = transformedCanvas.getBlobData();
1438
+ if (blobData?.url) previewImageUrl = blobData.url;
1439
+ } else if ("canvas" in event && event.canvas) {
1440
+ const canvas = event.canvas;
1441
+ canvas.updateBlob();
1442
+ const blobData = canvas.getBlobData();
1443
+ if (blobData?.url) previewImageUrl = blobData.url;
1444
+ }
1445
+ return {
1446
+ capturedImages,
1447
+ previewImageUrl
1448
+ };
1449
+ }
1450
+ function getDetectionStatusFromUpdate(context, event) {
1451
+ if (event.type === "DETECTION_UPDATE") {
1452
+ const newStatus = event.status;
1453
+ const currentStatus = context.detectionStatus;
1454
+ if ((newStatus === "blur" || newStatus === "glare") && (currentStatus === "wrongSide" || currentStatus === "farAway")) return currentStatus;
1455
+ if ((newStatus === "wrongSide" || newStatus === "farAway") && (currentStatus === "blur" || currentStatus === "glare")) return newStatus;
1456
+ return newStatus;
1457
+ }
1458
+ return "idle";
1459
+ }
1460
+ function getDetectionStatusFromSideChange(context, side) {
1461
+ const detectedSide = side?.toLowerCase() || "";
1462
+ const currentMode = context.currentMode;
1463
+ if (detectedSide === "wrong") return "wrongSide";
1464
+ const isBackDetected = detectedSide.includes("back") && !detectedSide.includes("front");
1465
+ const isFrontDetected = detectedSide.includes("front") && !detectedSide.includes("back");
1466
+ if (currentMode === "front" && isBackDetected || currentMode === "back" && isFrontDetected) return "wrongSide";
1467
+ if (currentMode === "front" && isFrontDetected || currentMode === "back" && isBackDetected || currentMode === "passport") return "detecting";
1468
+ return "detecting";
1469
+ }
1470
+ function getCurrentModeFromEvent(context, event) {
1471
+ if ("documentType" in event) {
1472
+ if (event.documentType === "passport") return "passport";
1473
+ return "front";
1474
+ }
1475
+ if (event.type === "FRONT_COMPLETE") return "back";
1476
+ return context.currentMode;
1477
+ }
1478
+
1479
+ //#endregion
1480
+ //#region src/modules/id/idCaptureGuards.ts
1481
+ const hasShowTutorialGuard = ({ context }) => context.config.showTutorial;
1482
+ const hasShowDocumentChooserGuard = ({ context }) => context.config.showDocumentChooserScreen ?? false;
1483
+ const isPermissionGrantedGuard = ({ event }) => {
1484
+ if ("output" in event) return event.output === "granted";
1485
+ return false;
1486
+ };
1487
+ const isPermissionDeniedErrorGuard = ({ event }) => {
1488
+ if ("error" in event) {
1489
+ const error = event.error;
1490
+ return error?.name === "NotAllowedError" || error?.name === "PermissionDeniedError";
1491
+ }
1492
+ return false;
1493
+ };
1494
+ const hasStreamGuard = ({ context }) => context.stream !== void 0;
1495
+ const hasAttemptsRemainingGuard = ({ context }) => context.attemptsRemaining > 0;
1496
+ const hasCapturedImageGuard = ({ context }) => {
1497
+ return context.provider?.getCapturedCanvas() !== null;
1498
+ };
1499
+ const hasUploadValidationErrorGuard = ({ context }) => {
1500
+ if (!context.uploadResponse) return false;
1501
+ return validateUploadResponse(context.uploadResponse, {
1502
+ skipGlareFront: context.uploadResponse.skipGlareFront,
1503
+ skipGlareBack: context.uploadResponse.skipGlareBack
1504
+ }) !== void 0;
1505
+ };
1506
+ const isFrontModeGuard = ({ context }) => context.currentMode === "front" || context.currentMode === "passport";
1507
+ const isOnlyFrontGuard = ({ context }) => context.config.onlyFront;
1508
+ const shouldContinueToBackGuard = ({ context }) => {
1509
+ if (context.currentMode === "passport") return false;
1510
+ if (context.currentMode !== "front") return false;
1511
+ if (context.config.onlyFront) return false;
1512
+ if (context.config.onlyBack) return false;
1513
+ if (!context.config.enableId && context.config.enablePassport) return false;
1514
+ if (context.uploadResponse?.skipBackIdCapture) return false;
1515
+ return true;
1516
+ };
1517
+ const isDeepsightEnabledGuard = ({ context }) => context.dependencies !== void 0;
1518
+ const isDeepsightReadyGuard = ({ context }) => context.deepsightService !== void 0;
1519
+ const needsDeepsightInitGuard = ({ context }) => context.dependencies !== void 0 && context.deepsightService === void 0 && !context.deepsightInitAttempted;
1520
+
1521
+ //#endregion
1522
+ //#region src/modules/id/idCaptureStateMachine.ts
1523
+ function getIdErrorCodeFromUnknown(error) {
1524
+ if (error instanceof Error) {
1525
+ const message = error.message;
1526
+ return Object.values(ID_ERROR_CODES).find((value) => message.includes(value));
1527
+ }
1528
+ }
1529
+ const _idCaptureMachine = setup({
1530
+ types: {
1531
+ context: {},
1532
+ events: {},
1533
+ input: {}
1534
+ },
1535
+ actors: {
1536
+ checkPermission: checkPermissionActor,
1537
+ requestPermission: requestPermissionActor,
1538
+ initializeCamera: initializeCameraActor,
1539
+ runDetection: runDetectionActor,
1540
+ uploadIdImage: uploadIdImageActor,
1541
+ processId: processIdActor,
1542
+ startRecording: startRecordingActor,
1543
+ checkMotionSensor: checkMotionSensorActor,
1544
+ initializeDeepsightSession: initializeDeepsightSessionActor,
1545
+ checkVirtualCamera: checkVirtualCameraActor
1546
+ },
1547
+ actions: {
1548
+ stopMediaStream: assign(({ context }) => {
1549
+ performStopMediaStream(context);
1550
+ return {
1551
+ stream: void 0,
1552
+ frameCapturer: void 0
1553
+ };
1554
+ }),
1555
+ disposeProvider: ({ context }) => {
1556
+ performDisposeProvider(context);
1557
+ },
1558
+ cleanupDeepsight: ({ context }) => {
1559
+ performCleanupDeepsight(context);
1560
+ },
1561
+ resetForBackCapture: assign(({ context }) => {
1562
+ performResetForBackCapture(context);
1563
+ return {
1564
+ stream: void 0,
1565
+ frameCapturer: void 0,
1566
+ detectionStatus: "idle",
1567
+ counterValue: 0,
1568
+ orientation: void 0,
1569
+ resetDetection: void 0,
1570
+ idType: void 0,
1571
+ qualityElements: void 0,
1572
+ debugFrame: void 0,
1573
+ frameRect: void 0,
1574
+ previewImageUrl: void 0,
1575
+ uploadProgress: void 0,
1576
+ manualCaptureTriggered: false
1577
+ };
1578
+ }),
1579
+ prepareForBackCapture: assign(({ context }) => {
1580
+ context.provider?.reset();
1581
+ return {
1582
+ detectionStatus: "idle",
1583
+ counterValue: 0,
1584
+ orientation: void 0,
1585
+ resetDetection: void 0,
1586
+ idType: void 0,
1587
+ qualityElements: void 0,
1588
+ debugFrame: void 0,
1589
+ frameRect: void 0,
1590
+ previewImageUrl: void 0,
1591
+ uploadProgress: void 0,
1592
+ manualCaptureTriggered: false
1593
+ };
1594
+ }),
1595
+ setStreamAndCapturer: assign({
1596
+ stream: ({ event }) => getStreamFromEvent(event),
1597
+ provider: ({ event }) => getProviderFromEvent(event),
1598
+ frameCapturer: ({ event }) => getFrameCapturerFromEvent(event)
1599
+ }),
1600
+ trackTutorialId: () => {
1601
+ performTrackTutorialId();
1602
+ },
1603
+ trackContinue: () => {
1604
+ /* @__PURE__ */ performTrackContinue();
1605
+ },
1606
+ resetContext: assign(({ context }) => getResetContextValues(context)),
1607
+ resetDetection: ({ context }) => {
1608
+ performResetDetection(context);
1609
+ },
1610
+ captureImage: assign({ qualityElements: ({ event }) => {
1611
+ if ("qualityElements" in event) return event.qualityElements;
1612
+ } }),
1613
+ storeCapturedCanvasInProvider: ({ context, event }) => {
1614
+ storeCapturedCanvasInProviderLogic(context, event);
1615
+ },
1616
+ captureLatestFrame: ({ context }) => {
1617
+ context.frameCapturer?.getLatestCanvas();
1618
+ },
1619
+ clearUploadFailure: assign(({ context }) => getClearUploadFailureValues(context)),
1620
+ decrementAttemptsRemaining: assign(({ context }) => ({ attemptsRemaining: context.attemptsRemaining - 1 })),
1621
+ setUploadErrorFromUploadValidation: assign({ uploadError: ({ context }) => getUploadValidationError(context) }),
1622
+ stopMediaRecording: ({ context }) => {
1623
+ performStopMediaRecording(context);
1624
+ },
1625
+ clearRecordingSession: assign({ recordingSession: () => void 0 }),
1626
+ setSelectedDocument: assign({ selectedDocumentType: ({ event }) => {
1627
+ if ("documentType" in event) return event.documentType;
1628
+ } }),
1629
+ setCurrentMode: assign({ currentMode: ({ event, context }) => getCurrentModeFromEvent(context, event) }),
1630
+ storeCapturedImage: assign(({ context, event }) => getStoreCapturedImageValues(context, event)),
1631
+ setDetectionStatus: assign({ detectionStatus: ({ event, context }) => getDetectionStatusFromUpdate(context, event) }),
1632
+ setCounterValue: assign({ counterValue: ({ event }) => {
1633
+ if ("value" in event) return event.value;
1634
+ return 0;
1635
+ } }),
1636
+ setIdType: assign({ idType: ({ event }) => {
1637
+ if ("idType" in event) return event.idType;
1638
+ } }),
1639
+ setOrientation: assign({ orientation: ({ event }) => {
1640
+ if ("orientation" in event) return event.orientation;
1641
+ } }),
1642
+ setFrameRect: assign({ frameRect: ({ event }) => {
1643
+ if ("frameRect" in event) return event.frameRect;
1644
+ } }),
1645
+ setDetectionArea: assign({ detectionArea: ({ event }) => {
1646
+ if ("detectionArea" in event) return event.detectionArea;
1647
+ } }),
1648
+ setMotionStatus: assign({ motionStatus: ({ event }) => {
1649
+ if ("status" in event && event.type === "MOTION_STATUS") return event.status;
1650
+ } })
1651
+ },
1652
+ guards: {
1653
+ hasShowTutorial: hasShowTutorialGuard,
1654
+ hasShowDocumentChooser: hasShowDocumentChooserGuard,
1655
+ isPermissionGranted: isPermissionGrantedGuard,
1656
+ isPermissionDeniedError: isPermissionDeniedErrorGuard,
1657
+ hasStream: hasStreamGuard,
1658
+ hasAttemptsRemaining: hasAttemptsRemainingGuard,
1659
+ hasCapturedImage: hasCapturedImageGuard,
1660
+ hasUploadValidationError: hasUploadValidationErrorGuard,
1661
+ isFrontMode: isFrontModeGuard,
1662
+ isOnlyFront: isOnlyFrontGuard,
1663
+ shouldContinueToBack: shouldContinueToBackGuard,
1664
+ isDeepsightEnabled: isDeepsightEnabledGuard,
1665
+ isDeepsightReady: isDeepsightReadyGuard,
1666
+ needsDeepsightInit: needsDeepsightInitGuard
1667
+ }
1668
+ }).createMachine({
1669
+ id: "idCapture",
1670
+ initial: "idle",
1671
+ context: ({ input }) => {
1672
+ const currentMode = input.config.onlyBack ? "back" : !input.config.enableId && input.config.enablePassport ? "passport" : "front";
1673
+ return {
1674
+ config: input.config,
1675
+ currentMode,
1676
+ selectedDocumentType: void 0,
1677
+ stream: void 0,
1678
+ provider: input.provider,
1679
+ frameCapturer: void 0,
1680
+ error: void 0,
1681
+ detectionStatus: "idle",
1682
+ counterValue: 0,
1683
+ orientation: void 0,
1684
+ capturedImages: {},
1685
+ uploadResponse: void 0,
1686
+ recordingSession: void 0,
1687
+ attemptsRemaining: input.config.captureAttempts,
1688
+ uploadError: void 0,
1689
+ permissionResult: void 0,
1690
+ resetDetection: void 0,
1691
+ idType: void 0,
1692
+ qualityElements: void 0,
1693
+ debugFrame: void 0,
1694
+ frameRect: void 0,
1695
+ detectionArea: void 0,
1696
+ previewImageUrl: void 0,
1697
+ uploadProgress: void 0,
1698
+ motionStatus: void 0,
1699
+ manualCaptureTriggered: false,
1700
+ deepsightService: void 0,
1701
+ analyticsProvider: input.analyticsProvider,
1702
+ dependencies: input.dependencies,
1703
+ disableIpify: getDisableIpify(),
1704
+ deepsightInitAttempted: false
1705
+ };
1706
+ },
1707
+ on: {
1708
+ QUIT: { target: "#idCapture.closed" },
1709
+ UPDATE_DETECTION_AREA: { actions: "setDetectionArea" }
1710
+ },
1711
+ states: {
1712
+ idle: { on: { LOAD: [
1713
+ {
1714
+ target: "chooser",
1715
+ guard: "hasShowDocumentChooser"
1716
+ },
1717
+ {
1718
+ target: "tutorial",
1719
+ guard: "hasShowTutorial"
1720
+ },
1721
+ { target: "loading" }
1722
+ ] } },
1723
+ chooser: { on: { SELECT_DOCUMENT: [{
1724
+ target: "tutorial",
1725
+ guard: "hasShowTutorial",
1726
+ actions: ["setSelectedDocument", "setCurrentMode"]
1727
+ }, {
1728
+ target: "loading",
1729
+ actions: ["setSelectedDocument", "setCurrentMode"]
1730
+ }] } },
1731
+ loading: {
1732
+ type: "parallel",
1733
+ states: {
1734
+ permissionCheck: {
1735
+ initial: "checking",
1736
+ states: {
1737
+ checking: { invoke: {
1738
+ id: "checkPermissionLoading",
1739
+ src: "checkPermission",
1740
+ onDone: {
1741
+ target: "done",
1742
+ actions: assign({ permissionResult: ({ event }) => event.output })
1743
+ },
1744
+ onError: {
1745
+ target: "done",
1746
+ actions: assign({ permissionResult: () => "prompt" })
1747
+ }
1748
+ } },
1749
+ done: { type: "final" }
1750
+ }
1751
+ },
1752
+ deepsightInit: {
1753
+ initial: "initializing",
1754
+ states: {
1755
+ initializing: { invoke: {
1756
+ id: "initDeepsightLoading",
1757
+ src: "initializeDeepsightSession",
1758
+ input: ({ context }) => ({
1759
+ ds: context.config.ds,
1760
+ dependencies: context.dependencies,
1761
+ disableIpify: context.disableIpify
1762
+ }),
1763
+ onDone: {
1764
+ target: "done",
1765
+ actions: assign({
1766
+ deepsightService: ({ event }) => event.output,
1767
+ deepsightInitAttempted: () => true
1768
+ })
1769
+ },
1770
+ onError: {
1771
+ target: "done",
1772
+ actions: [assign({ deepsightInitAttempted: () => true }), () => console.warn("Deepsight initialization failed")]
1773
+ }
1774
+ } },
1775
+ done: { type: "final" }
1776
+ }
1777
+ }
1778
+ },
1779
+ onDone: [{
1780
+ target: "capture",
1781
+ guard: "isPermissionGranted"
1782
+ }, { target: "permissions" }]
1783
+ },
1784
+ tutorial: {
1785
+ initial: "checkingPermission",
1786
+ entry: "trackTutorialId",
1787
+ states: {
1788
+ checkingPermission: {
1789
+ invoke: {
1790
+ id: "checkPermissionTutorial",
1791
+ src: "checkPermission",
1792
+ onDone: [{
1793
+ target: "initializingCamera",
1794
+ guard: "isPermissionGranted",
1795
+ actions: assign({ permissionResult: ({ event }) => event.output })
1796
+ }, {
1797
+ target: "ready",
1798
+ actions: assign({ permissionResult: ({ event }) => event.output })
1799
+ }]
1800
+ },
1801
+ on: { NEXT_STEP: {
1802
+ target: "initializingCamera",
1803
+ actions: "trackContinue"
1804
+ } }
1805
+ },
1806
+ initializingCamera: {
1807
+ type: "parallel",
1808
+ states: {
1809
+ cameraInit: {
1810
+ initial: "initializingDeepsight",
1811
+ states: {
1812
+ initializingDeepsight: { invoke: {
1813
+ id: "tutorialInitDeepsight",
1814
+ src: "initializeDeepsightSession",
1815
+ input: ({ context }) => ({
1816
+ ds: context.config.ds,
1817
+ dependencies: context.dependencies,
1818
+ disableIpify: context.disableIpify
1819
+ }),
1820
+ onDone: {
1821
+ target: "initializingStream",
1822
+ actions: assign({
1823
+ deepsightService: ({ event }) => event.output,
1824
+ deepsightInitAttempted: () => true
1825
+ })
1826
+ },
1827
+ onError: {
1828
+ target: "initializingStream",
1829
+ actions: [assign({ deepsightInitAttempted: () => true }), () => console.warn("Deepsight initialization failed in tutorial")]
1830
+ }
1831
+ } },
1832
+ initializingStream: { invoke: {
1833
+ id: "tutorialInitCamera",
1834
+ src: "initializeCamera",
1835
+ input: ({ context }) => {
1836
+ if (!context.provider) throw new Error("Provider is required");
1837
+ return {
1838
+ provider: context.provider,
1839
+ config: context.config,
1840
+ deepsightService: context.deepsightService
1841
+ };
1842
+ },
1843
+ onDone: {
1844
+ target: "ready",
1845
+ actions: "setStreamAndCapturer"
1846
+ },
1847
+ onError: [{
1848
+ target: "#idCapture.tutorial.ready",
1849
+ guard: "isPermissionDeniedError",
1850
+ actions: assign({ permissionResult: () => "denied" })
1851
+ }, {
1852
+ target: "#idCapture.tutorial.ready",
1853
+ actions: assign({ error: ({ event }) => String(event.error) })
1854
+ }]
1855
+ } },
1856
+ ready: { type: "final" }
1857
+ }
1858
+ },
1859
+ userIntent: {
1860
+ initial: "booting",
1861
+ states: {
1862
+ booting: {
1863
+ always: [{
1864
+ target: "clicked",
1865
+ guard: "hasStream"
1866
+ }],
1867
+ on: { NEXT_STEP: {
1868
+ target: "clicked",
1869
+ actions: "trackContinue"
1870
+ } }
1871
+ },
1872
+ clicked: { type: "final" }
1873
+ }
1874
+ }
1875
+ },
1876
+ onDone: [{
1877
+ target: "#tutorialCameraReady",
1878
+ guard: "hasStream"
1879
+ }, { target: "#idCapture.capture" }]
1880
+ },
1881
+ cameraReady: {
1882
+ id: "tutorialCameraReady",
1883
+ on: { NEXT_STEP: {
1884
+ target: "#idCapture.capture",
1885
+ actions: "trackContinue"
1886
+ } }
1887
+ },
1888
+ ready: { on: { NEXT_STEP: {
1889
+ target: "waitingForPermission",
1890
+ actions: "trackContinue"
1891
+ } } },
1892
+ waitingForPermission: { invoke: {
1893
+ id: "checkPermissionWaiting",
1894
+ src: "checkPermission",
1895
+ onDone: [{
1896
+ target: "#idCapture.capture",
1897
+ guard: "isPermissionGranted",
1898
+ actions: assign({ permissionResult: ({ event }) => event.output })
1899
+ }, {
1900
+ target: "#idCapture.permissions",
1901
+ actions: assign({ permissionResult: ({ event }) => event.output })
1902
+ }]
1903
+ } }
1904
+ }
1905
+ },
1906
+ permissions: {
1907
+ initial: "idle",
1908
+ states: {
1909
+ idle: {
1910
+ invoke: {
1911
+ id: "checkPermissionIdle",
1912
+ src: "checkPermission",
1913
+ onDone: [
1914
+ {
1915
+ target: "#idCapture.capture",
1916
+ guard: "isPermissionGranted",
1917
+ actions: assign({ permissionResult: ({ event }) => event.output })
1918
+ },
1919
+ {
1920
+ target: "denied",
1921
+ guard: ({ event }) => event.output === "denied",
1922
+ actions: assign({ permissionResult: ({ event }) => event.output })
1923
+ },
1924
+ {
1925
+ target: "waitingForUser",
1926
+ actions: assign({ permissionResult: ({ event }) => event.output })
1927
+ }
1928
+ ],
1929
+ onError: {
1930
+ target: "waitingForUser",
1931
+ actions: assign({ permissionResult: () => "prompt" })
1932
+ }
1933
+ },
1934
+ on: {
1935
+ REQUEST_PERMISSION: "requesting",
1936
+ GO_TO_LEARN_MORE: "learnMore"
1937
+ }
1938
+ },
1939
+ waitingForUser: { on: {
1940
+ REQUEST_PERMISSION: "requesting",
1941
+ GO_TO_LEARN_MORE: "learnMore"
1942
+ } },
1943
+ learnMore: { on: {
1944
+ BACK: "idle",
1945
+ REQUEST_PERMISSION: "requesting"
1946
+ } },
1947
+ requesting: { invoke: {
1948
+ id: "requestPermission",
1949
+ src: "requestPermission",
1950
+ onDone: [
1951
+ {
1952
+ target: "#idCapture.capture",
1953
+ guard: "isPermissionGranted",
1954
+ actions: assign({ permissionResult: ({ event }) => event.output })
1955
+ },
1956
+ {
1957
+ target: "denied",
1958
+ guard: ({ event }) => event.output === "denied",
1959
+ actions: assign({ permissionResult: ({ event }) => event.output })
1960
+ },
1961
+ {
1962
+ target: "idle",
1963
+ actions: assign({ permissionResult: ({ event }) => event.output })
1964
+ }
1965
+ ],
1966
+ onError: { target: "denied" }
1967
+ } },
1968
+ denied: { entry: assign({ permissionResult: () => "refresh" }) }
1969
+ }
1970
+ },
1971
+ capture: {
1972
+ initial: "checkingStream",
1973
+ exit: ["stopMediaRecording", "clearRecordingSession"],
1974
+ on: { SET_FRAME_RECT: { actions: "setFrameRect" } },
1975
+ states: {
1976
+ checkingStream: { always: [
1977
+ {
1978
+ target: "initializingDeepsight",
1979
+ guard: "needsDeepsightInit"
1980
+ },
1981
+ {
1982
+ target: "detecting",
1983
+ guard: "hasStream"
1984
+ },
1985
+ { target: "initializing" }
1986
+ ] },
1987
+ initializingDeepsight: { invoke: {
1988
+ id: "initDeepsightCapture",
1989
+ src: "initializeDeepsightSession",
1990
+ input: ({ context }) => ({
1991
+ ds: context.config.ds,
1992
+ dependencies: context.dependencies,
1993
+ disableIpify: context.disableIpify
1994
+ }),
1995
+ onDone: {
1996
+ target: "checkingStream",
1997
+ actions: assign({
1998
+ deepsightService: ({ event }) => event.output,
1999
+ deepsightInitAttempted: () => true
2000
+ })
2001
+ },
2002
+ onError: {
2003
+ target: "checkingStream",
2004
+ actions: [assign({ deepsightInitAttempted: () => true }), () => console.warn("Deepsight initialization failed in capture")]
2005
+ }
2006
+ } },
2007
+ initializing: { invoke: {
2008
+ id: "initializeCamera",
2009
+ src: "initializeCamera",
2010
+ input: ({ context }) => {
2011
+ if (!context.provider) throw new Error("Provider is required");
2012
+ return {
2013
+ provider: context.provider,
2014
+ config: context.config,
2015
+ deepsightService: context.deepsightService
2016
+ };
2017
+ },
2018
+ onDone: {
2019
+ target: "detecting",
2020
+ actions: "setStreamAndCapturer"
2021
+ },
2022
+ onError: [{
2023
+ target: "#idCapture.permissions",
2024
+ guard: "isPermissionDeniedError",
2025
+ actions: assign({ permissionResult: () => "denied" })
2026
+ }, {
2027
+ target: "#idCapture.error",
2028
+ actions: assign({ error: ({ event }) => String(event.error) })
2029
+ }]
2030
+ } },
2031
+ detecting: {
2032
+ always: [{
2033
+ target: "manualCaptureWaiting",
2034
+ guard: ({ context }) => context.manualCaptureTriggered,
2035
+ actions: assign({ detectionStatus: () => "manualCapture" })
2036
+ }],
2037
+ entry: [assign({ detectionStatus: () => "detecting" })],
2038
+ invoke: [{
2039
+ id: "startRecording",
2040
+ src: "startRecording",
2041
+ input: ({ context }) => ({
2042
+ config: context.config,
2043
+ stream: context.stream,
2044
+ existing: context.recordingSession,
2045
+ currentMode: context.currentMode
2046
+ }),
2047
+ onDone: { actions: assign({ recordingSession: ({ context, event }) => {
2048
+ return event.output ?? context.recordingSession;
2049
+ } }) },
2050
+ onError: { actions: () => void 0 }
2051
+ }, {
2052
+ id: "runDetection",
2053
+ src: "runDetection",
2054
+ input: ({ context }) => ({
2055
+ frameCapturer: context.frameCapturer,
2056
+ provider: context.provider,
2057
+ config: context.config,
2058
+ currentMode: context.currentMode,
2059
+ detectionArea: context.detectionArea ?? context.config.detectionArea
2060
+ })
2061
+ }],
2062
+ on: {
2063
+ DETECTION_UPDATE: { actions: "setDetectionStatus" },
2064
+ DETECTION_FRAME: { actions: assign({ debugFrame: ({ event }) => event.frame }) },
2065
+ DETECTION_RESET_READY: { actions: assign({ resetDetection: ({ event }) => event.reset }) },
2066
+ DETECTION_SUCCESS: {
2067
+ target: "capturing",
2068
+ actions: assign({ qualityElements: ({ event }) => event.qualityElements })
2069
+ },
2070
+ MANUAL_CAPTURE: { target: "capturingManual" },
2071
+ SWITCH_TO_MANUAL_CAPTURE: {
2072
+ target: "manualCaptureWaiting",
2073
+ actions: assign({
2074
+ detectionStatus: () => "manualCapture",
2075
+ manualCaptureTriggered: () => true
2076
+ })
2077
+ },
2078
+ COUNTER_VALUE_CHANGE: { actions: "setCounterValue" },
2079
+ ID_TYPE_CHANGE: { actions: "setIdType" },
2080
+ ID_SIDE_CHANGE: { actions: assign({ detectionStatus: ({ event, context }) => getDetectionStatusFromSideChange(context, event.side) }) },
2081
+ ORIENTATION_CHANGE: { actions: "setOrientation" }
2082
+ }
2083
+ },
2084
+ manualCaptureWaiting: { on: { MANUAL_CAPTURE: { target: "capturingManual" } } },
2085
+ capturing: {
2086
+ entry: [
2087
+ "captureImage",
2088
+ "storeCapturedCanvasInProvider",
2089
+ "storeCapturedImage"
2090
+ ],
2091
+ always: [{
2092
+ target: "uploading",
2093
+ guard: "hasCapturedImage"
2094
+ }, {
2095
+ target: "uploadError",
2096
+ actions: assign(({ context }) => ({
2097
+ uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
2098
+ attemptsRemaining: context.attemptsRemaining - 1
2099
+ }))
2100
+ }]
2101
+ },
2102
+ capturingManual: {
2103
+ entry: [
2104
+ "captureLatestFrame",
2105
+ "storeCapturedCanvasInProvider",
2106
+ "storeCapturedImage"
2107
+ ],
2108
+ always: [{
2109
+ target: "uploading",
2110
+ guard: "hasCapturedImage"
2111
+ }, {
2112
+ target: "uploadError",
2113
+ actions: assign(({ context }) => ({
2114
+ uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
2115
+ attemptsRemaining: context.attemptsRemaining - 1
2116
+ }))
2117
+ }]
2118
+ },
2119
+ uploading: {
2120
+ entry: assign({ uploadProgress: () => 0 }),
2121
+ invoke: {
2122
+ id: "uploadIdImage",
2123
+ src: "uploadIdImage",
2124
+ input: ({ context, self }) => {
2125
+ const canvas = context.provider?.getOriginalCapturedCanvas();
2126
+ if (!canvas) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
2127
+ return {
2128
+ canvas,
2129
+ type: context.currentMode === "back" ? "back" : "front",
2130
+ qualityElements: context.qualityElements,
2131
+ onProgress: (progress) => {
2132
+ self.send({
2133
+ type: "UPLOAD_PROGRESS",
2134
+ progress
2135
+ });
2136
+ },
2137
+ deepsightService: context.deepsightService,
2138
+ stream: context.stream
2139
+ };
2140
+ },
2141
+ onDone: {
2142
+ target: "validatingUpload",
2143
+ actions: [assign({
2144
+ uploadResponse: ({ event }) => event.output,
2145
+ uploadProgress: () => 100
2146
+ }), "storeCapturedImage"]
2147
+ },
2148
+ onError: {
2149
+ target: "uploadError",
2150
+ actions: assign(({ context, event }) => ({
2151
+ uploadError: getIdErrorCodeFromUnknown(event.error) ?? ID_ERROR_CODES.UPLOAD_ERROR,
2152
+ attemptsRemaining: context.attemptsRemaining - 1
2153
+ }))
2154
+ }
2155
+ },
2156
+ on: { UPLOAD_PROGRESS: { actions: assign({ uploadProgress: ({ event }) => event.progress }) } }
2157
+ },
2158
+ validatingUpload: { always: [{
2159
+ target: "uploadError",
2160
+ guard: "hasUploadValidationError",
2161
+ actions: ["setUploadErrorFromUploadValidation", "decrementAttemptsRemaining"]
2162
+ }, { target: "success" }] },
2163
+ uploadError: { on: { CONTINUE_FROM_ERROR: [
2164
+ {
2165
+ target: "detecting",
2166
+ guard: "hasAttemptsRemaining",
2167
+ actions: [
2168
+ "resetDetection",
2169
+ "clearUploadFailure",
2170
+ assign({ manualCaptureTriggered: () => false })
2171
+ ]
2172
+ },
2173
+ {
2174
+ target: "#idCapture.frontFinished",
2175
+ guard: "shouldContinueToBack"
2176
+ },
2177
+ { target: "#idCapture.finished" }
2178
+ ] } },
2179
+ success: { on: { NEXT_STEP: [{
2180
+ target: "#idCapture.frontFinished",
2181
+ guard: "shouldContinueToBack"
2182
+ }, { target: "#idCapture.processing" }] } }
2183
+ }
2184
+ },
2185
+ frontFinished: {
2186
+ entry: ["stopMediaRecording", "resetForBackCapture"],
2187
+ type: "parallel",
2188
+ states: {
2189
+ cameraInit: {
2190
+ initial: "checking",
2191
+ states: {
2192
+ checking: { always: [{
2193
+ target: "ready",
2194
+ guard: "hasStream"
2195
+ }, { target: "initializingStream" }] },
2196
+ initializingStream: { invoke: {
2197
+ id: "frontFinishedInitCamera",
2198
+ src: "initializeCamera",
2199
+ input: ({ context }) => {
2200
+ if (!context.provider) throw new Error("Provider is required");
2201
+ return {
2202
+ provider: context.provider,
2203
+ config: context.config,
2204
+ deepsightService: context.deepsightService
2205
+ };
2206
+ },
2207
+ onDone: {
2208
+ target: "ready",
2209
+ actions: "setStreamAndCapturer"
2210
+ },
2211
+ onError: {
2212
+ target: "ready",
2213
+ actions: () => {
2214
+ console.warn("Camera initialization failed during flip transition");
2215
+ }
2216
+ }
2217
+ } },
2218
+ ready: { type: "final" }
2219
+ }
2220
+ },
2221
+ userIntent: {
2222
+ initial: "waiting",
2223
+ states: {
2224
+ waiting: { on: { CONTINUE_TO_BACK: {
2225
+ target: "clicked",
2226
+ actions: assign({ currentMode: () => "back" })
2227
+ } } },
2228
+ clicked: { type: "final" }
2229
+ }
2230
+ }
2231
+ },
2232
+ onDone: [{
2233
+ target: "#idCapture.capture.detecting",
2234
+ guard: "hasStream"
2235
+ }, { target: "#idCapture.capture" }]
2236
+ },
2237
+ processing: {
2238
+ entry: "stopMediaStream",
2239
+ invoke: {
2240
+ id: "processId",
2241
+ src: "processId",
2242
+ input: ({ context }) => ({ isSecondId: context.config.isSecondId ?? false }),
2243
+ onDone: [{
2244
+ target: "expired",
2245
+ guard: ({ event }) => event.output.isDocumentExpired
2246
+ }, { target: "finished" }],
2247
+ onError: { target: "finished" }
2248
+ }
2249
+ },
2250
+ expired: { on: { RETRY_CAPTURE: [
2251
+ {
2252
+ target: "chooser",
2253
+ guard: "hasShowDocumentChooser",
2254
+ actions: ["resetContext"]
2255
+ },
2256
+ {
2257
+ target: "tutorial",
2258
+ guard: "hasShowTutorial",
2259
+ actions: "resetContext"
2260
+ },
2261
+ {
2262
+ target: "loading",
2263
+ actions: "resetContext"
2264
+ }
2265
+ ] } },
2266
+ finished: {
2267
+ entry: [
2268
+ "stopMediaRecording",
2269
+ "stopMediaStream",
2270
+ "disposeProvider",
2271
+ "cleanupDeepsight"
2272
+ ],
2273
+ type: "final"
2274
+ },
2275
+ closed: {
2276
+ entry: [
2277
+ "stopMediaStream",
2278
+ "disposeProvider",
2279
+ "cleanupDeepsight"
2280
+ ],
2281
+ type: "final"
2282
+ },
2283
+ error: {
2284
+ entry: [
2285
+ "stopMediaStream",
2286
+ "disposeProvider",
2287
+ "cleanupDeepsight"
2288
+ ],
2289
+ on: { RESET: {
2290
+ target: "idle",
2291
+ actions: "resetContext"
2292
+ } }
2293
+ },
2294
+ manualIdUpload: { on: { QUIT: { target: "closed" } } },
2295
+ digitalIdUpload: { on: { QUIT: { target: "closed" } } }
2296
+ }
2297
+ });
2298
+ const idCaptureMachine = _idCaptureMachine;
2299
+
2300
+ //#endregion
2301
+ //#region src/modules/id/idCaptureActor.ts
2302
+ function createIdCaptureActor(options) {
2303
+ const dependencies = options.dependencies ?? { storage: new BrowserStorageProvider() };
2304
+ return createActor(idCaptureMachine, { input: {
2305
+ config: options.config,
2306
+ provider: options.provider,
2307
+ dependencies
2308
+ } }).start();
2309
+ }
2310
+
2311
+ //#endregion
2312
+ //#region src/modules/id/idCaptureManager.ts
2313
+ function getPermissionStatus(snapshot) {
2314
+ if (!snapshot.matches("permissions")) return;
2315
+ if (snapshot.matches({ permissions: "idle" })) return "idle";
2316
+ if (snapshot.matches({ permissions: "waitingForUser" })) return "idle";
2317
+ if (snapshot.matches({ permissions: "learnMore" })) return "learnMore";
2318
+ if (snapshot.matches({ permissions: "requesting" })) return "requesting";
2319
+ if (snapshot.matches({ permissions: "denied" })) return "denied";
2320
+ }
2321
+ function getCaptureStatus(snapshot) {
2322
+ const matches = {
2323
+ initializing: snapshot.matches({ capture: "initializing" }),
2324
+ detecting: snapshot.matches({ capture: "detecting" }),
2325
+ manualCaptureWaiting: snapshot.matches({ capture: "manualCaptureWaiting" }),
2326
+ capturing: snapshot.matches({ capture: "capturing" }),
2327
+ capturingManual: snapshot.matches({ capture: "capturingManual" }),
2328
+ uploading: snapshot.matches({ capture: "uploading" }),
2329
+ uploadError: snapshot.matches({ capture: "uploadError" }),
2330
+ success: snapshot.matches({ capture: "success" })
2331
+ };
2332
+ if (matches.initializing) return "initializing";
2333
+ if (matches.detecting || matches.manualCaptureWaiting) return "detecting";
2334
+ if (matches.capturing || matches.capturingManual) return "capturing";
2335
+ if (matches.uploading) return "uploading";
2336
+ if (matches.uploadError) return "uploadError";
2337
+ if (matches.success) return "success";
2338
+ }
2339
+ function getErrorMessage(errorCode) {
2340
+ if (!errorCode) return void 0;
2341
+ return {
2342
+ UPLOAD_ERROR: "Upload failed",
2343
+ CLASSIFICATION_FAILED: "ID classification failed",
2344
+ LOW_SHARPNESS: "Image is not sharp enough",
2345
+ GLARE_DETECTED: "Glare detected on ID",
2346
+ WRONG_DOCUMENT_SIDE: "Wrong side of document",
2347
+ ID_TYPE_UNACCEPTABLE: "ID type is not acceptable",
2348
+ READABILITY_ISSUE: "ID readability issue",
2349
+ RETRY_EXHAUSTED_CONTINUE_TO_BACK: "Retry exhausted",
2350
+ RETRY_EXHAUSTED_SKIP_BACK: "Retry exhausted",
2351
+ NO_MORE_TRIES: "No more tries remaining",
2352
+ UNEXPECTED_ERROR: "An unexpected error occurred",
2353
+ NO_TOKEN: "No token available",
2354
+ PERMISSION_DENIED: "Permission denied",
2355
+ USER_CANCELLED: "User cancelled",
2356
+ SERVER_ERROR: "Server error"
2357
+ }[errorCode];
2358
+ }
2359
+ function getErrorDescription(errorCode, uploadResponse) {
2360
+ if (!errorCode) return void 0;
2361
+ return {
2362
+ UPLOAD_ERROR: "Please try again",
2363
+ CLASSIFICATION_FAILED: "Please ensure your ID is clearly visible",
2364
+ LOW_SHARPNESS: "Please ensure the image is clear and well-focused",
2365
+ GLARE_DETECTED: "Please avoid bright reflections on your ID",
2366
+ WRONG_DOCUMENT_SIDE: uploadResponse?.side === "back" ? "Please show the back side of your ID" : "Please show the front side of your ID",
2367
+ ID_TYPE_UNACCEPTABLE: "Please use a valid ID type",
2368
+ READABILITY_ISSUE: "Please ensure all text is clearly visible",
2369
+ RETRY_EXHAUSTED_CONTINUE_TO_BACK: "Continuing to back side capture",
2370
+ RETRY_EXHAUSTED_SKIP_BACK: "Skipping back side capture",
2371
+ NO_MORE_TRIES: "Maximum attempts reached",
2372
+ UNEXPECTED_ERROR: "Please try again later",
2373
+ NO_TOKEN: "Session expired",
2374
+ PERMISSION_DENIED: "Camera permission is required",
2375
+ USER_CANCELLED: "Capture was cancelled",
2376
+ SERVER_ERROR: "Please try again later"
2377
+ }[errorCode];
2378
+ }
2379
+ function mapState(snapshot) {
2380
+ const { context } = snapshot;
2381
+ if (snapshot.matches("idle")) return { status: "idle" };
2382
+ if (snapshot.matches("chooser")) return { status: "chooser" };
2383
+ if (snapshot.matches("loading")) return { status: "loading" };
2384
+ if (snapshot.matches("tutorial")) return {
2385
+ status: "tutorial",
2386
+ selectedDocumentType: context.selectedDocumentType
2387
+ };
2388
+ if (snapshot.matches("closed")) return { status: "closed" };
2389
+ if (snapshot.matches("permissions")) {
2390
+ const permissionStatus = getPermissionStatus(snapshot);
2391
+ if (permissionStatus === void 0) return {
2392
+ status: "permissions",
2393
+ permissionStatus: "idle"
2394
+ };
2395
+ return {
2396
+ status: "permissions",
2397
+ permissionStatus
2398
+ };
2399
+ }
2400
+ if (snapshot.matches("capture")) {
2401
+ const captureStatus = getCaptureStatus(snapshot);
2402
+ const needsBackCapture = context.currentMode === "front" && !context.config.onlyFront && !context.config.onlyBack;
2403
+ return {
2404
+ status: "capture",
2405
+ captureStatus: captureStatus ?? "initializing",
2406
+ stream: context.stream,
2407
+ detectionStatus: context.detectionStatus,
2408
+ debugFrame: void 0,
2409
+ attemptsRemaining: context.attemptsRemaining,
2410
+ uploadError: context.uploadError,
2411
+ currentMode: context.currentMode,
2412
+ counterValue: context.counterValue,
2413
+ orientation: context.orientation,
2414
+ idType: context.idType,
2415
+ previewImageUrl: context.previewImageUrl,
2416
+ uploadProgress: context.uploadProgress ?? 0,
2417
+ uploadErrorMessage: context.uploadError ? getErrorMessage(context.uploadError) : void 0,
2418
+ uploadErrorDescription: context.uploadError ? getErrorDescription(context.uploadError, context.uploadResponse) : void 0,
2419
+ needsBackCapture,
2420
+ showCaptureButtonInAuto: context.config.showCaptureButtonInAuto ?? false,
2421
+ canRetry: context.attemptsRemaining > 0
2422
+ };
2423
+ }
2424
+ if (snapshot.matches("frontFinished")) return { status: "frontFinished" };
2425
+ if (snapshot.matches("processing")) return { status: "processing" };
2426
+ if (snapshot.matches("expired")) return { status: "expired" };
2427
+ if (snapshot.matches("finished")) return { status: "finished" };
2428
+ if (snapshot.matches("error")) return {
2429
+ status: "error",
2430
+ error: context.error ?? "Unknown error"
2431
+ };
2432
+ return { status: "idle" };
2433
+ }
2434
+ function createApi({ actor }) {
2435
+ return {
2436
+ load() {
2437
+ actor.send({ type: "LOAD" });
2438
+ },
2439
+ selectDocument(documentType) {
2440
+ actor.send({
2441
+ type: "SELECT_DOCUMENT",
2442
+ documentType
2443
+ });
2444
+ },
2445
+ nextStep() {
2446
+ actor.send({ type: "NEXT_STEP" });
2447
+ },
2448
+ requestPermission() {
2449
+ actor.send({ type: "REQUEST_PERMISSION" });
2450
+ },
2451
+ goToLearnMore() {
2452
+ actor.send({ type: "GO_TO_LEARN_MORE" });
2453
+ },
2454
+ back() {
2455
+ actor.send({ type: "BACK" });
2456
+ },
2457
+ close() {
2458
+ actor.send({ type: "QUIT" });
2459
+ },
2460
+ reset() {
2461
+ actor.send({ type: "RESET" });
2462
+ },
2463
+ retryCapture() {
2464
+ actor.send({ type: "RETRY_CAPTURE" });
2465
+ },
2466
+ continueFromError() {
2467
+ actor.send({ type: "CONTINUE_FROM_ERROR" });
2468
+ },
2469
+ capture() {
2470
+ actor.send({ type: "MANUAL_CAPTURE" });
2471
+ },
2472
+ switchToManualCapture() {
2473
+ actor.send({ type: "SWITCH_TO_MANUAL_CAPTURE" });
2474
+ },
2475
+ continueToBack() {
2476
+ actor.send({ type: "CONTINUE_TO_BACK" });
2477
+ },
2478
+ skipBack() {
2479
+ actor.send({ type: "SKIP_BACK" });
2480
+ },
2481
+ updateDetectionArea(detectionArea) {
2482
+ actor.send({
2483
+ type: "UPDATE_DETECTION_AREA",
2484
+ detectionArea
2485
+ });
2486
+ }
2487
+ };
2488
+ }
2489
+ function createIdCaptureManager(options) {
2490
+ return createManager({
2491
+ actor: createIdCaptureActor(options),
2492
+ mapState,
2493
+ createApi
2494
+ });
2495
+ }
2496
+
2497
+ //#endregion
2498
+ export { createSession as _, processId as a, stopStream as c, ID_ERROR_CODES as d, getDisableIpify as f, resetSessionInit as g, isSessionInitialized as h, initializeIdCapture as i, uploadIdImage as l, initializeSession as m, createIdCaptureActor as n, startRecordingSession as o, getSessionFeatures as p, idCaptureMachine as r, stopRecording as s, createIdCaptureManager as t, validateUploadResponse as u };