@incodetech/core 0.0.0-dev-20260911-aec162f0 → 0.0.0-dev-20260914-8aef7789

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/dist/{FaceDetectionProvider-Cn1wS-sA.d.ts → BaseWasmProvider-CO0GfaHM.d.ts} +7 -37
  2. package/dist/{BrowserDiagnosticsCryptoProvider-8sT0QLgq.esm.js → BrowserDiagnosticsCryptoProvider-BoYzmYY5.esm.js} +1 -1
  3. package/dist/{onDeviceFaceResults-hle2Ci9l.esm.js → FaceDetectionProvider-0bWjPgW5.esm.js} +219 -340
  4. package/dist/FaceDetectionProvider-CyLxkzRP.d.ts +36 -0
  5. package/dist/{IIdCaptureCapability-DDWlQzhi.d.ts → IIdCaptureCapability-nfpic2z2.d.ts} +7 -3
  6. package/dist/{IWasmUtilCapability-SNPhmIwB.d.ts → IWasmUtilCapability-DaP6pfHq.d.ts} +10 -4
  7. package/dist/{MotionSensorProvider-Vlyhbcty.esm.js → MotionSensorProvider-BnZ8_1Sk.esm.js} +1 -1
  8. package/dist/{StreamCanvasCapture-D2XcXCZ4.esm.js → StreamCanvasCapture-C9jtgBdT.esm.js} +2 -2
  9. package/dist/{deepsightService-MZOpj7Vp.d.ts → StreamCanvasCapture-D9Dz8kqB.d.ts} +54 -41
  10. package/dist/ae-signature.esm.js +1 -1
  11. package/dist/{apiError-XdhqSAmg.esm.js → apiError-CixFeCyt.esm.js} +1 -1
  12. package/dist/authentication.d.ts +3 -3
  13. package/dist/authentication.esm.js +2 -2
  14. package/dist/{authenticationManager-DqBOkMzk.esm.js → authenticationManager-CN26fkJa.esm.js} +4 -4
  15. package/dist/{authenticationManager-BCkV6CSG.d.ts → authenticationManager-D7UUqgnN.d.ts} +1 -1
  16. package/dist/{authenticationStateMachine-Cto_G5bN.esm.js → authenticationStateMachine-CUXpIxAD.esm.js} +6 -62
  17. package/dist/avatar.d.ts +1 -1
  18. package/dist/avatar.esm.js +1 -1
  19. package/dist/{backCameraStream-DE_5qY04.esm.js → backCameraStream-BOtyQowf.esm.js} +75 -18
  20. package/dist/{camera-_2sXMXiF.esm.js → camera-sIa9ceW2.esm.js} +19 -1
  21. package/dist/camera.esm.js +1 -1
  22. package/dist/{canvas-E1zGkel4.esm.js → canvas-BxFzWTt-.esm.js} +19 -0
  23. package/dist/createCameraCycleOwnership-DQ5-F2oW.esm.js +826 -0
  24. package/dist/{createFaceAvatar-DQKaRise.esm.js → createFaceAvatar-BNHdeFj8.esm.js} +2 -2
  25. package/dist/{createFaceAvatar-pUEkdk4X.d.ts → createFaceAvatar-CTM9aj9Q.d.ts} +12 -6
  26. package/dist/curp-validation.d.ts +1 -1
  27. package/dist/{curpValidationManager-cSd2MrOQ.d.ts → curpValidationManager-CTJB2RsD.d.ts} +1 -1
  28. package/dist/{deepsightLoader-Dwr1zKFa.esm.js → deepsightLoader-Dhz9T_KJ.esm.js} +19 -53
  29. package/dist/document-capture.esm.js +2 -2
  30. package/dist/{documentCaptureManager-BTpHZMgW.esm.js → documentCaptureManager-BwcbMwo5.esm.js} +1 -1
  31. package/dist/{documentCaptureStateMachine-CPizkpcg.esm.js → documentCaptureStateMachine-F8SCOtL_.esm.js} +4 -4
  32. package/dist/dynamic-forms.esm.js +2 -2
  33. package/dist/{dynamicFormsManager-Cmh84Nz4.esm.js → dynamicFormsManager-CNBrBOR3.esm.js} +1 -1
  34. package/dist/{dynamicFormsStateMachine-DZr7Ugvy.esm.js → dynamicFormsStateMachine-Bo2qrjHB.esm.js} +1 -1
  35. package/dist/ekyb.d.ts +1 -1
  36. package/dist/ekyb.esm.js +3 -3
  37. package/dist/{ekybManager-Dc2tT9fu.esm.js → ekybManager-D8DdfRAD.esm.js} +1 -1
  38. package/dist/{ekybStateMachine-GSEMOiHn.esm.js → ekybStateMachine-oPCbEFob.esm.js} +1 -1
  39. package/dist/electronic-signature.esm.js +1 -1
  40. package/dist/{electronicSignatureManager-CpLuqw7T.esm.js → electronicSignatureManager-DkKQ07vx.esm.js} +1 -1
  41. package/dist/extensibility.d.ts +11 -58
  42. package/dist/extensibility.esm.js +13 -13
  43. package/dist/{faceCaptureManagerFactory-kDmbL0I7.esm.js → faceCaptureManagerFactory-D_EjQnnE.esm.js} +19 -4
  44. package/dist/{faceCaptureManagerFactory-CzUr_ZPq.d.ts → faceCaptureManagerFactory-DqrYpjA5.d.ts} +24 -12
  45. package/dist/{faceCaptureSetup-CAGBnLVx.esm.js → faceCaptureSetup-WeO8-TfR.esm.js} +999 -382
  46. package/dist/{feature-management-B6CtmH1O.esm.js → feature-management-BEqf3Sr3.esm.js} +1 -1
  47. package/dist/feature-management.esm.js +1 -1
  48. package/dist/fiscal-qr.esm.js +1 -1
  49. package/dist/{fiscalQrManager-DI6uOffY.esm.js → fiscalQrManager-Bp_-3LJr.esm.js} +3 -3
  50. package/dist/flow-events.d.ts +1 -1
  51. package/dist/flow.d.ts +6 -6
  52. package/dist/flow.esm.js +3 -3
  53. package/dist/{flowCompletionService-BpoZDpAn.esm.js → flowCompletionService-DELbVeL5.esm.js} +1 -1
  54. package/dist/{flowServices-BVnSStkh.esm.js → flowServices-Brr2fh_c.esm.js} +5 -5
  55. package/dist/geolocation.esm.js +2 -2
  56. package/dist/{geolocationManager-CLm86P8G.esm.js → geolocationManager-CtVZDjng.esm.js} +1 -1
  57. package/dist/{geolocationStateMachine-Bi3vy7Wl.esm.js → geolocationStateMachine-tjsYcMhr.esm.js} +1 -1
  58. package/dist/home.d.ts +1 -1
  59. package/dist/id-selfie-flow.d.ts +3 -3
  60. package/dist/id-selfie-flow.esm.js +10 -10
  61. package/dist/id.d.ts +4 -4
  62. package/dist/id.esm.js +35 -44
  63. package/dist/{idCaptureDefaults-BLsd7shz.esm.js → idCaptureDefaults-C66YvkgN.esm.js} +19 -14
  64. package/dist/{idCaptureManager-DW1GeEUk.d.ts → idCaptureManager-DE_jyr5X.d.ts} +98 -37
  65. package/dist/{idCaptureManager-BUTdcmaD.esm.js → idCaptureManager-zjul9YdZ.esm.js} +27 -17
  66. package/dist/{idCaptureServices-fzIhBqAK.esm.js → idCaptureServices-UXYCBZPK.esm.js} +80 -20
  67. package/dist/{idCaptureStateMachine-Cp2_revF.esm.js → idCaptureStateMachine-D3pqfK2Z.esm.js} +1954 -1082
  68. package/dist/{index-1PoJuUJ-.d.ts → index-BGxFwnld.d.ts} +1 -1
  69. package/dist/index.d.ts +1 -1
  70. package/dist/index.esm.js +1 -1
  71. package/dist/mandatory-consent.d.ts +2 -2
  72. package/dist/{mandatoryConsentManager-BJnylLZO.d.ts → mandatoryConsentManager-CZwhsUlO.d.ts} +1 -1
  73. package/dist/{permissionGuards-DxbvOoZJ.esm.js → permissionGuards-o1JLtewE.esm.js} +1 -1
  74. package/dist/{permissionServices-f7nBnW5P.esm.js → permissionServices-7lzEiOsO.esm.js} +3 -3
  75. package/dist/personhood.d.ts +5 -5
  76. package/dist/personhood.esm.js +8 -8
  77. package/dist/qe-signature.esm.js +1 -1
  78. package/dist/{runChildModule-BPlX2IDO.esm.js → runChildModule-BDlvNMrA.esm.js} +1 -1
  79. package/dist/{safeStorage-Dp9m6aOh.esm.js → safeStorage-1O9k0ulP.esm.js} +8 -8
  80. package/dist/{sdkVersion-Dr9_E3d9.esm.js → sdkVersion-CS_0s_Lc.esm.js} +1 -1
  81. package/dist/selfie.d.ts +4 -4
  82. package/dist/selfie.esm.js +12 -74
  83. package/dist/{selfieManager-BdP-RgH4.esm.js → selfieManager-BouB9yUQ.esm.js} +4 -4
  84. package/dist/{selfieManager-Bg_Xwe4p.d.ts → selfieManager-DZ3u_WN9.d.ts} +1 -1
  85. package/dist/{selfieStateMachine-B8vFeT39.esm.js → selfieStateMachine-TGfI9hNp.esm.js} +22 -31
  86. package/dist/{session-C2THUsQZ.esm.js → session-m9j27bHm.esm.js} +3 -3
  87. package/dist/session.d.ts +1 -1
  88. package/dist/session.esm.js +4 -4
  89. package/dist/{sessionInitializer-CEycxhm4.esm.js → sessionInitializer-CUpgbHzs.esm.js} +4 -4
  90. package/dist/{setup-D6yz9se2.d.ts → setup-CEX95gJ1.d.ts} +11 -6
  91. package/dist/{setup-BHJ3-fhX.esm.js → setup-CfNAVG-d.esm.js} +12 -11
  92. package/dist/{trueSightService-DLzYznkh.esm.js → trueSightService-TqmmYye3.esm.js} +4 -4
  93. package/dist/types-BhHF-CuL.d.ts +6 -0
  94. package/dist/{types-DJ2OM9rw.d.ts → types-BzuUebkB.d.ts} +1 -1
  95. package/dist/{types-DzmqFoCH.d.ts → types-CBctcPeF.d.ts} +1 -1
  96. package/dist/{types-BBI4-s9u2.d.ts → types-CoOlCUp82.d.ts} +5 -5
  97. package/dist/{types-hSqUyXyw.d.ts → types-DL0CXuWv.d.ts} +3 -3
  98. package/dist/types-Ds928RiU.d.ts +1 -0
  99. package/dist/{types-_pWNFY1F.d.ts → types-eLtVBhon.d.ts} +1 -1
  100. package/dist/video-selfie.d.ts +3 -3
  101. package/dist/video-selfie.esm.js +5 -5
  102. package/dist/{videoSelfieCameraWarmer-D5E12WjL.esm.js → videoSelfieCameraWarmer-C-cESczy.esm.js} +9 -9
  103. package/dist/{videoSelfieStepDetector-B8TvFDNK.d.ts → videoSelfieStepDetector-CdXIFh9s.d.ts} +4 -4
  104. package/dist/virtualCameraCheck-WOloJ-do.esm.js +48 -0
  105. package/dist/{warmup-BOM-sD-6.esm.js → warmup-CUUnwYxe.esm.js} +282 -90
  106. package/dist/{warmup-CXHBgvFC.d.ts → warmup-Cis2p6O0.d.ts} +13 -2
  107. package/dist/wasm.d.ts +9 -3
  108. package/dist/wasm.esm.js +10 -4
  109. package/dist/{wasmDsFlagSync-C3k_VK9o.esm.js → wasmDsFlagSync-BJtLh2XK.esm.js} +1 -1
  110. package/dist/{wasmWebClient-BJCaFeBL.esm.js → wasmWebClient-B7baE-Wf.esm.js} +223 -2
  111. package/dist/workflow.d.ts +1 -1
  112. package/dist/workflow.esm.js +15 -15
  113. package/package.json +1 -1
  114. package/dist/imageFile-CLaua6xO.esm.js +0 -69
  115. package/dist/types-m4t_cT5J.d.ts +0 -6
  116. package/dist/{Face2DProvider-UUQt_vRC.esm.js → Face2DProvider-BF48sz5c.esm.js} +1 -1
  117. package/dist/{PrivacyLensProvider-D8PS7AJa.esm.js → PrivacyLensProvider-EwnC_C5n.esm.js} +1 -1
  118. package/dist/{selfieErrorUtils-C6gC06J7.esm.js → selfieErrorUtils-C77GXJH6.esm.js} +1 -1
@@ -1,28 +1,32 @@
1
- import { l as BrowserStorageProvider } from "./setup-BHJ3-fhX.esm.js";
1
+ import { l as BrowserStorageProvider } from "./setup-CfNAVG-d.esm.js";
2
+ import { r as WasmUtilProvider } from "./safeStorage-1O9k0ulP.esm.js";
2
3
  import { t as BrowserTimerProvider } from "./BrowserTimerProvider-BXhgNxLt.esm.js";
3
- import { s as exposeExperimentOnce } from "./feature-management-B6CtmH1O.esm.js";
4
- import { n as revokeObjectURL } from "./IBrowserEnvironmentCapability-DEaRRk-R.esm.js";
4
+ import { s as exposeExperimentOnce } from "./feature-management-BEqf3Sr3.esm.js";
5
+ import { n as revokeObjectURL, t as getTimestamp } from "./IBrowserEnvironmentCapability-DEaRRk-R.esm.js";
5
6
  import { r as getToken, t as api } from "./api-Bx3gKW_y.esm.js";
6
- import { c as trueSightHintTransition, l as trueSightLog, r as noteTrueSightCaptureActivity } from "./trueSightService-DLzYznkh.esm.js";
7
+ import { c as trueSightHintTransition, l as trueSightLog, m as isTrueSightTesting, r as noteTrueSightCaptureActivity } from "./trueSightService-TqmmYye3.esm.js";
7
8
  import { n as eventModuleNames, s as recordExperimentAssignmentOnce } from "./events-ijlbX_Wx.esm.js";
8
- import { t as getDisableIpify } from "./sessionInitializer-CEycxhm4.esm.js";
9
+ import { t as getDisableIpify } from "./sessionInitializer-CUpgbHzs.esm.js";
9
10
  import { t as endpoints } from "./endpoints-D3B9mUHh.esm.js";
10
11
  import { t as addDeviceStats } from "./stats-DkK-f8JT.esm.js";
11
12
  import { a as isIOS, s as isIPad, t as isAndroid, u as isMacOsSafari } from "./platform-DkcRc9JV.esm.js";
12
- import "./session-C2THUsQZ.esm.js";
13
+ import "./session-m9j27bHm.esm.js";
13
14
  import { c as not, i as assign, l as raise, n as enqueueActions, o as and, r as sendTo, t as setup } from "./xstate.esm-BuFLFuzz.esm.js";
14
15
  import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-BXeQ-Ucz.esm.js";
15
16
  import { n as getUrlSearchParam, t as buildUrlWithSearchParams } from "./urlUtils-D6phjq1-.esm.js";
16
- import { n as invokeRequestPermissionActor, t as checkPermission } from "./permissionServices-f7nBnW5P.esm.js";
17
- import { t as sleep } from "./ITimerCapability-DgdDzp8G.esm.js";
18
- import { t as IncodeCanvas } from "./canvas-E1zGkel4.esm.js";
17
+ import { r as getCameraReacquisitionConstraints } from "./camera-sIa9ceW2.esm.js";
18
+ import { n as invokeRequestPermissionActor, t as checkPermission } from "./permissionServices-7lzEiOsO.esm.js";
19
+ import { i as imageFileToDataUrl, n as runProtectedAttempt, o as flagIdManualReview, r as cameraCycleProvider, s as PROTECTED_CAPTURE_TARGET, t as createCameraCycleOwnership } from "./createCameraCycleOwnership-DQ5-F2oW.esm.js";
20
+ import { t as IncodeCanvas } from "./canvas-BxFzWTt-.esm.js";
21
+ import { n as needsMotionPrimeOnlyFromPermission, r as resolveStoredPermissionResult, t as isReadyForCaptureFromPermission } from "./permissionGuards-o1JLtewE.esm.js";
19
22
  import { a as StreamCanvasProcessingSession } from "./recordingsRepository-CCWy13M-.esm.js";
23
+ import { t as sleep } from "./ITimerCapability-DgdDzp8G.esm.js";
20
24
  import { t as getBrowser } from "./getBrowser-Cbe1C4Tn.esm.js";
21
25
  import { n as getWindowDimensions } from "./getDeviceClass-CeBiEayS.esm.js";
22
- import { t as StreamCanvasCapture } from "./StreamCanvasCapture-D2XcXCZ4.esm.js";
23
- import { n as needsMotionPrimeOnlyFromPermission, r as resolveStoredPermissionResult, t as isReadyForCaptureFromPermission } from "./permissionGuards-DxbvOoZJ.esm.js";
24
- import { r as flagIdManualReview, t as imageFileToDataUrl } from "./imageFile-CLaua6xO.esm.js";
25
- import { _ as CAPTURE_OPTIMIZING, c as startRecordingSession, d as uploadIdImage, f as uploadManualIdFile, g as resolveIdCaptureThresholds, i as pauseRecordingForAttempt, l as stopRecording, m as ID_ERROR_CODES, o as prepareRecordingForCameraRetry, p as validateUploadResponse, r as initializeIdCapture, s as processId, t as addBarcode, u as stopStream, v as OPTIMIZING_WATCHDOG_MARGIN_MS, y as getIdCameraStream } from "./idCaptureServices-fzIhBqAK.esm.js";
26
+ import { n as runVirtualCameraCheck, t as frameSources } from "./virtualCameraCheck-WOloJ-do.esm.js";
27
+ import { n as DEFAULT_ID_CAPTURE_THRESHOLDS, r as IdCaptureProvider } from "./idCaptureDefaults-C66YvkgN.esm.js";
28
+ import { S as getIdCameraStream, _ as ID_ERROR_CODES, a as getRealQualityValue, b as CAPTURE_OPTIMIZING, d as startRecordingSession, f as stopRecording, g as validateUploadResponse, h as uploadManualIdFile, i as finalizeRecordingSession, l as prepareRecordingForCameraRetry, m as uploadIdImage, o as initializeIdCapture, p as stopStream, r as disposeRecordingSession, t as addBarcode, u as processId, x as OPTIMIZING_WATCHDOG_MARGIN_MS, y as resolveIdCaptureThresholds } from "./idCaptureServices-UXYCBZPK.esm.js";
29
+ import { t as StreamCanvasCapture } from "./StreamCanvasCapture-C9jtgBdT.esm.js";
26
30
  //#region src/modules/id/digitalUpload/digitalUploadErrors.ts
27
31
  const TERMINAL_FAIL_REASONS = /* @__PURE__ */ new Set(["DIGITAL_ID_REQUESTED_BUT_OTHER_PROVIDED", "ID_TYPE_UNACCEPTABLE"]);
28
32
  function isKnownFailReason(reason) {
@@ -955,8 +959,6 @@ const isPermissionDeniedErrorGuard = ({ event }) => {
955
959
  return false;
956
960
  };
957
961
  const isPermissionBlockedGuard = ({ context }) => context.permissionResult === "denied" || context.permissionResult === "refresh";
958
- const hasStreamGuard = ({ context }) => context.stream !== void 0;
959
- const hasInitializedCameraGuard = ({ context }) => context.stream !== void 0 && context.provider !== void 0;
960
962
  const hasAttemptsRemainingGuard = ({ context }) => context.attemptsRemaining > 0;
961
963
  const hasCapturedImageGuard = ({ context }) => {
962
964
  return context.provider?.getCapturedCanvas() !== null;
@@ -964,17 +966,6 @@ const hasCapturedImageGuard = ({ context }) => {
964
966
  const hasCandidateFrameGuard = ({ event }) => {
965
967
  return event.type === "DETECTION_OPTIMIZING" && Boolean(event.candidateCanvas);
966
968
  };
967
- /**
968
- * IDC-05: whether WASM's commit arrived while the optimizing phase was still
969
- * spending its dwell, and is therefore parked in `optimizing.holding` waiting
970
- * for the release.
971
- *
972
- * Gates the phase's own `SWITCH_TO_MANUAL_CAPTURE` / `MANUAL_CAPTURE` handlers:
973
- * while a commit is parked those escapes end the dwell early instead of leaving
974
- * the phase, so WASM's committed frame is honored rather than dropped on the way
975
- * out. False leaves both events to the arm parent, the documented escape hatch.
976
- */
977
- const hasStashedCommitGuard = ({ context }) => context.stashedCommit !== void 0;
978
969
  const hasUploadValidationErrorGuard = ({ context }) => {
979
970
  if (!context.uploadResponse) return false;
980
971
  return validateUploadResponse(context.uploadResponse, {
@@ -1353,412 +1344,133 @@ function computeIdCaptureOutput(context) {
1353
1344
  };
1354
1345
  }
1355
1346
  //#endregion
1356
- //#region src/modules/digital-id-redirect/digitalIdRedirectServices.ts
1357
- function withQuery(path, params) {
1358
- if (!params) return path;
1359
- const search = new URLSearchParams();
1360
- if (params.country) search.set("country", params.country);
1361
- if (params.region) search.set("region", params.region);
1362
- const qs = search.toString();
1363
- return qs ? `${path}?${qs}` : path;
1364
- }
1365
- /** GET the provider catalog. Ordering is applied FE-side by the module. */
1366
- async function listProviders(params, signal) {
1367
- const url = withQuery(endpoints.digitalIdProviders, params);
1368
- const res = await api.get(url, { signal });
1369
- if (!res.ok) throw new Error(`GET ${url} failed: ${res.status} ${res.statusText}`);
1370
- return res.data ?? [];
1371
- }
1372
- /**
1373
- * Append the onboarding `uuid` to `returnUrl` when absent, so the return leg can
1374
- * re-attach the (onboarding-scoped) verification. Idempotent (skips when a `uuid`
1375
- * is already present) and non-fatal (returns the URL unchanged on any failure).
1376
- * Exported for unit testing.
1377
- */
1378
- async function ensureReturnUrlUuid(returnUrl, signal) {
1379
- try {
1380
- if (getUrlSearchParam(returnUrl, "uuid")) return returnUrl;
1381
- const res = await api.get(endpoints.onboardingUrl, { signal });
1382
- const onboardingUrl = res.ok ? res.data?.url : void 0;
1383
- const uuid = onboardingUrl ? getUrlSearchParam(onboardingUrl, "uuid") : null;
1384
- if (!uuid) return returnUrl;
1385
- return buildUrlWithSearchParams(returnUrl, { uuid });
1386
- } catch (error) {
1387
- console.warn("[DigitalIdRedirect] ensureReturnUrlUuid failed, proceeding without uuid:", error);
1388
- return returnUrl;
1389
- }
1390
- }
1391
- /**
1392
- * Create a verification. On success the caller must full-page-redirect to
1393
- * `flowData.launchUrl`. NOTE: this is the billable moment for iDIN/FTN — each
1394
- * call (incl. every retry) is a new session and a new billing event.
1395
- *
1396
- * The `redirectUrl` is first passed through `ensureReturnUrlUuid` so a bare
1397
- * launch gets a resumable `uuid` before the bank round-trip. This reuses the
1398
- * current session token (no extra `/omni/start`) and only fetches when the URL
1399
- * lacks a `uuid`.
1400
- */
1401
- async function createVerification(params, signal) {
1402
- const redirectUrl = await ensureReturnUrlUuid(params.redirectUrl, signal);
1403
- const res = await api.post(endpoints.digitalIdVerifications, {
1404
- ...params,
1405
- redirectUrl
1406
- }, { signal });
1407
- if (!res.ok) throw new Error(`POST ${endpoints.digitalIdVerifications} failed: ${res.status} ${res.statusText}`);
1408
- return res.data;
1409
- }
1410
- /**
1411
- * Submit the provider's return params. The contract wants them wrapped as a
1412
- * JSON *string* under `walletResponse` (success serialized as "true"/"false").
1413
- */
1414
- async function submitWalletResponse(verificationId, params, signal) {
1415
- const url = `${endpoints.digitalIdVerifications}/${verificationId}/response`;
1416
- const body = { walletResponse: JSON.stringify({
1417
- sessionId: params.sessionId,
1418
- resultsAccessKey: params.resultsAccessKey,
1419
- success: String(params.success)
1420
- }) };
1421
- const res = await api.post(url, body, { signal });
1422
- if (!res.ok) throw new Error(`POST ${url} failed: ${res.status} ${res.statusText}`);
1423
- return res.data;
1424
- }
1425
- /** Poll fallback — used when the user does not return or results are delayed. */
1426
- async function getVerificationStatus(verificationId, signal) {
1427
- const url = `${endpoints.digitalIdVerifications}/${verificationId}/status`;
1428
- const res = await api.get(url, { signal });
1429
- if (!res.ok) throw new Error(`GET ${url} failed: ${res.status} ${res.statusText}`);
1430
- return res.data;
1347
+ //#region src/modules/id/idDetectionNotificationController.ts
1348
+ const NOTIFICATION_DURATION_MS = 500;
1349
+ function isReportedIdSideWrong(currentMode, reportedSide) {
1350
+ if (!reportedSide) return false;
1351
+ const detectedSide = reportedSide.toLowerCase();
1352
+ if (detectedSide === "wrong") return true;
1353
+ const isBackDetected = detectedSide.includes("back") && !detectedSide.includes("front");
1354
+ const isFrontDetected = detectedSide.includes("front") && !detectedSide.includes("back");
1355
+ return currentMode === "front" && isBackDetected || currentMode === "back" && isFrontDetected;
1431
1356
  }
1432
1357
  /**
1433
- * Refresh an expiring `launchUrl` (maps to `flowData.expiresAt`). Do NOT create
1434
- * a new verification for this — that would be a new billing event.
1358
+ * Coalesces noisy per-frame quality callbacks into stable presentation state.
1359
+ * Repeating the current status extends its display window without re-emitting
1360
+ * it, while a different status waits until that window closes.
1435
1361
  */
1436
- async function refreshVerification(verificationId, signal) {
1437
- const url = `${endpoints.digitalIdVerifications}/${verificationId}/refresh`;
1438
- const res = await api.post(url, {}, { signal });
1439
- if (!res.ok) throw new Error(`POST ${url} failed: ${res.status} ${res.statusText}`);
1440
- return res.data;
1362
+ function createIdDetectionNotificationController(input) {
1363
+ const timer = BrowserTimerProvider.getInstance();
1364
+ let timeout = null;
1365
+ let currentStatus = null;
1366
+ const scheduleReset = () => {
1367
+ timeout = timer.setTimeout(() => {
1368
+ timeout = null;
1369
+ currentStatus = null;
1370
+ input.send("detecting");
1371
+ }, NOTIFICATION_DURATION_MS);
1372
+ };
1373
+ const clear = () => {
1374
+ if (timeout) timer.clearTimeout(timeout);
1375
+ timeout = null;
1376
+ currentStatus = null;
1377
+ };
1378
+ const show = (status) => {
1379
+ if (input.isSuppressed()) return;
1380
+ if (timeout) {
1381
+ if (currentStatus !== status) return;
1382
+ timer.clearTimeout(timeout);
1383
+ scheduleReset();
1384
+ return;
1385
+ }
1386
+ currentStatus = status;
1387
+ input.onStatusShown?.(status);
1388
+ input.send(status);
1389
+ scheduleReset();
1390
+ };
1391
+ return {
1392
+ show,
1393
+ clear
1394
+ };
1441
1395
  }
1442
1396
  //#endregion
1443
- //#region src/modules/digital-id-redirect/digitalIdRedirectStorage.ts
1397
+ //#region src/modules/id/idCaptureActors.ts
1398
+ const checkPermissionActor = fromPromise(async () => {
1399
+ return checkPermission();
1400
+ });
1401
+ async function requestIdCapturePermission(input) {
1402
+ if (!isAndroid() || getBrowser() !== "firefox") return invokeRequestPermissionActor(input);
1403
+ if (input.requestMotionPermission) {
1404
+ const motionResult = await invokeRequestPermissionActor({
1405
+ ...input,
1406
+ permissionResult: "granted"
1407
+ });
1408
+ if (motionResult !== "granted") return motionResult;
1409
+ }
1410
+ const { stream, constraints, fallbackLevel } = await getIdCameraStream();
1411
+ if (!input.acceptStream?.({
1412
+ stream,
1413
+ constraints,
1414
+ fallbackLevel
1415
+ })) stopStream(stream);
1416
+ return {
1417
+ permissionResult: "granted",
1418
+ cameraFallbackLevel: fallbackLevel
1419
+ };
1420
+ }
1421
+ const requestPermissionActor = fromPromise(async ({ input }) => {
1422
+ return requestIdCapturePermission(input);
1423
+ });
1424
+ fromPromise(async ({ input }) => {
1425
+ return initializeIdCapture(input);
1426
+ });
1427
+ const DEFAULT_ID_CAPTURE_GEOMETRY = {
1428
+ areaDown: 25e3,
1429
+ areaUp: 55e3,
1430
+ areaIOSPassportUp: 3e4,
1431
+ areaIOSPassportDown: 2e4,
1432
+ widthIOSUp: 160,
1433
+ widthIOSDown: 85,
1434
+ widthDown: 110,
1435
+ widthUp: 205
1436
+ };
1444
1437
  /**
1445
- * Cross-redirect handoff. The full-page redirect out to the provider tears down
1446
- * the SPA, so the pending verification must be persisted BEFORE navigating and
1447
- * read back on the return leg. Only the non-sensitive `verificationId` +
1448
- * `providerId` live here — the session credential is the host's concern
1449
- * (re-bootstrapped from `url_uuid`) and is NEVER persisted by this module.
1438
+ * Applies the canonical ID workflow configuration to either the legacy
1439
+ * host-fed pipeline or the protected camera-cycle workflow.
1450
1440
  *
1451
- * Backed by whatever `IStorageCapability` is injected (default localStorage).
1452
- * sessionStorage would be preferable (auto-clears on tab close) — swap the
1453
- * capability when a sessionStorage-backed provider exists. A single fixed key
1454
- * is used, so a stale record from an abandoned redirect is overwritten by the
1455
- * next one and is only ever read on the host's return route.
1456
- *
1457
- * TODO(follow-up): add a staleness TTL once an injected time capability lands
1458
- * (Core forbids `Date.now()` directly — `no-browser-api-in-core`).
1441
+ * Keeping geometry, thresholds, settings, model selection, callbacks, and
1442
+ * barcode setup here prevents the two frame-transport architectures from
1443
+ * drifting while the protected path is rolled out.
1459
1444
  */
1460
- const PENDING_KEY = "incode.digitalIdRedirect.pending";
1461
- async function persistPendingRedirect(storage, entry) {
1462
- await storage.set(PENDING_KEY, {
1463
- verificationId: entry.verificationId,
1464
- providerId: entry.providerId
1445
+ function configureIdCaptureProvider(input) {
1446
+ const windowDimensions = getWindowDimensions(input.frameSize.width, input.frameSize.height);
1447
+ input.provider.setGeometry({
1448
+ ...input.config.geometry ?? DEFAULT_ID_CAPTURE_GEOMETRY,
1449
+ windowOuterWidth: windowDimensions.outerWidth,
1450
+ windowOuterHeight: windowDimensions.outerHeight,
1451
+ windowInnerWidth: windowDimensions.innerWidth,
1452
+ windowInnerHeight: windowDimensions.innerHeight
1465
1453
  });
1454
+ const idType = input.currentMode === "back" ? "BackId" : input.currentMode === "passport" ? "Passport" : "FrontId";
1455
+ input.provider.setSettings({
1456
+ isFixedMask: input.config.settings?.isFixedMask ?? false,
1457
+ isIPhone14OrHigher: input.config.settings?.isIPhone14OrHigher ?? false,
1458
+ idType,
1459
+ blurCheckEnabled: input.config.settings?.blurCheckEnabled ?? false,
1460
+ glareCheckEnabled: input.config.settings?.glareCheckEnabled ?? false,
1461
+ faceQualityCheckEnabled: input.config.settings?.faceQualityCheckEnabled ?? true,
1462
+ iouCheckEnabled: input.config.settings?.iouCheckEnabled ?? true,
1463
+ fpsLimitEnabled: input.config.settings?.fpsLimitEnabled ?? false
1464
+ });
1465
+ input.provider.setThresholds(resolveIdCaptureThresholds(input.config));
1466
+ input.provider.setModelType(input.config.modelVersion ?? "v2");
1467
+ input.provider.setCallbacks(input.callbacks);
1468
+ const barcodeCaptureEnabled = input.config.usSmartCapture === true && input.currentMode === "back";
1469
+ input.provider.setBarcodeCaptureEnabled(barcodeCaptureEnabled);
1470
+ input.provider.setBarcodeAutoCaptureOnDecode(false);
1471
+ if (barcodeCaptureEnabled) input.provider.setBarcodeEnhancementModel().catch(() => {});
1466
1472
  }
1467
- async function readPendingRedirect(storage) {
1468
- const pending = await storage.get(PENDING_KEY);
1469
- return pending?.verificationId && pending?.providerId ? pending : null;
1470
- }
1471
- async function clearPendingRedirect(storage) {
1472
- await storage.remove(PENDING_KEY);
1473
- }
1474
- //#endregion
1475
- //#region src/modules/digital-id-redirect/digitalIdRedirectStateMachine.ts
1476
- const POLL_INTERVAL_MS = 1500;
1477
- const MAX_POLL_ATTEMPTS = 20;
1478
- const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
1479
- "COMPLETED",
1480
- "FAILED",
1481
- "EXPIRED",
1482
- "CANCELLED"
1483
- ]);
1484
- function isTerminal(status) {
1485
- return TERMINAL_STATUSES.has(status);
1486
- }
1487
- const digitalIdRedirectMachine = setup({
1488
- types: {
1489
- context: {},
1490
- events: {},
1491
- input: {},
1492
- output: {}
1493
- },
1494
- actors: {
1495
- listProviders: fromPromise(async ({ input, signal }) => listProviders(input ? { country: input.country } : void 0, signal)),
1496
- createVerification: fromPromise(async ({ input, signal }) => createVerification(input, signal)),
1497
- submitWalletResponse: fromPromise(async ({ input, signal }) => submitWalletResponse(input.verificationId, input.params, signal)),
1498
- refreshVerification: fromPromise(async ({ input, signal }) => refreshVerification(input.verificationId, signal)),
1499
- pollStatus: fromCallback(({ sendBack, input }) => {
1500
- const timer = BrowserTimerProvider.getInstance();
1501
- const interval = timer.setInterval(async () => {
1502
- try {
1503
- const v = await getVerificationStatus(input.verificationId);
1504
- if (isTerminal(v.status)) sendBack({
1505
- type: "POLL_RESULT",
1506
- verification: v
1507
- });
1508
- } catch (error) {
1509
- console.warn("[DigitalIdRedirect] status poll failed:", error);
1510
- }
1511
- }, POLL_INTERVAL_MS);
1512
- return () => timer.clearInterval(interval);
1513
- }),
1514
- persistPending: fromPromise(async ({ input }) => {
1515
- await persistPendingRedirect(input.storage, {
1516
- verificationId: input.verificationId,
1517
- providerId: input.providerId
1518
- });
1519
- })
1520
- },
1521
- actions: {
1522
- setProviders: assign(({ event }) => ({ providers: event.output })),
1523
- setSelectedProvider: assign(({ event }) => ({ selectedProvider: event.providerId })),
1524
- setVerification: assign(({ event }) => {
1525
- const v = event.output;
1526
- return {
1527
- verificationId: v.verificationId,
1528
- launchUrl: v.flowData?.launchUrl ?? void 0,
1529
- expiresAt: v.flowData?.expiresAt ?? void 0,
1530
- status: v.status,
1531
- failureReason: v.failureReason ?? void 0,
1532
- portraitPresent: v.portraitPresent ?? false
1533
- };
1534
- }),
1535
- setVerificationFromEvent: assign(({ event }) => {
1536
- const v = event.verification;
1537
- return {
1538
- status: v.status,
1539
- failureReason: v.failureReason ?? void 0,
1540
- portraitPresent: v.portraitPresent ?? false
1541
- };
1542
- }),
1543
- setError: assign(({ event }) => ({ error: String(event.error ?? "Unknown error") })),
1544
- clearResume: assign(() => ({
1545
- returnParams: void 0,
1546
- verificationId: void 0,
1547
- failureReason: void 0,
1548
- status: void 0,
1549
- error: void 0
1550
- }))
1551
- },
1552
- guards: {
1553
- hasPresetProvider: ({ context }) => Boolean(context.config.provider),
1554
- autoStartPreset: ({ context }) => context.autoStart && Boolean(context.config.provider),
1555
- autoStartCatalog: ({ context }) => context.autoStart && !context.config.provider,
1556
- isResuming: ({ context }) => Boolean(context.returnParams),
1557
- returnSucceeded: ({ context }) => context.returnParams?.success === true,
1558
- submittedTerminalOk: ({ event }) => event.output.status === "COMPLETED",
1559
- submittedFailed: ({ event }) => {
1560
- const s = event.output.status;
1561
- return s === "FAILED" || s === "EXPIRED" || s === "CANCELLED";
1562
- },
1563
- polledOk: ({ event }) => event.verification.status === "COMPLETED"
1564
- }
1565
- }).createMachine({
1566
- id: "digitalIdRedirect",
1567
- initial: "idle",
1568
- context: ({ input }) => ({
1569
- config: input.config,
1570
- storage: input.storage,
1571
- returnUrl: input.config.returnUrl ?? "",
1572
- providers: [],
1573
- selectedProvider: input.config.provider,
1574
- verificationId: input.resume?.verificationId,
1575
- launchUrl: void 0,
1576
- expiresAt: void 0,
1577
- status: void 0,
1578
- failureReason: void 0,
1579
- portraitPresent: false,
1580
- returnParams: input.resume?.returnParams,
1581
- error: void 0,
1582
- autoStart: input.autoStart ?? false
1583
- }),
1584
- output: ({ context }) => context.status === "COMPLETED" ? {
1585
- status: "COMPLETED",
1586
- providerId: context.selectedProvider ?? context.config.provider ?? "",
1587
- portraitPresent: context.portraitPresent
1588
- } : { status: "DISMISSED" },
1589
- states: {
1590
- idle: {
1591
- always: [
1592
- {
1593
- guard: "isResuming",
1594
- target: "submitting"
1595
- },
1596
- {
1597
- guard: "autoStartPreset",
1598
- target: "creating"
1599
- },
1600
- {
1601
- guard: "autoStartCatalog",
1602
- target: "loadingProviders"
1603
- }
1604
- ],
1605
- on: { LOAD: [{
1606
- guard: "hasPresetProvider",
1607
- target: "creating"
1608
- }, { target: "loadingProviders" }] }
1609
- },
1610
- loadingProviders: { invoke: {
1611
- src: "listProviders",
1612
- input: ({ context }) => ({ country: context.config.country }),
1613
- onDone: {
1614
- target: "choosingProvider",
1615
- actions: "setProviders"
1616
- },
1617
- onError: {
1618
- target: "error",
1619
- actions: "setError"
1620
- }
1621
- } },
1622
- choosingProvider: { on: { SELECT_PROVIDER: {
1623
- target: "creating",
1624
- actions: "setSelectedProvider"
1625
- } } },
1626
- creating: { invoke: {
1627
- src: "createVerification",
1628
- input: ({ context }) => ({
1629
- providerId: context.selectedProvider ?? "",
1630
- redirectUrl: context.returnUrl
1631
- }),
1632
- onDone: {
1633
- target: "persisting",
1634
- actions: "setVerification"
1635
- },
1636
- onError: {
1637
- target: "error",
1638
- actions: "setError"
1639
- }
1640
- } },
1641
- persisting: { invoke: {
1642
- src: "persistPending",
1643
- input: ({ context }) => ({
1644
- storage: context.storage,
1645
- verificationId: context.verificationId ?? "",
1646
- providerId: context.selectedProvider ?? ""
1647
- }),
1648
- onDone: { target: "readyToRedirect" },
1649
- onError: { target: "readyToRedirect" }
1650
- } },
1651
- readyToRedirect: { on: { REFRESH: { target: "refreshing" } } },
1652
- refreshing: { invoke: {
1653
- src: "refreshVerification",
1654
- input: ({ context }) => ({ verificationId: context.verificationId ?? "" }),
1655
- onDone: {
1656
- target: "readyToRedirect",
1657
- actions: "setVerification"
1658
- },
1659
- onError: {
1660
- target: "error",
1661
- actions: "setError"
1662
- }
1663
- } },
1664
- submitting: { invoke: {
1665
- src: "submitWalletResponse",
1666
- input: ({ context }) => ({
1667
- verificationId: context.verificationId ?? "",
1668
- params: context.returnParams
1669
- }),
1670
- onDone: [
1671
- {
1672
- guard: "submittedTerminalOk",
1673
- target: "succeeded",
1674
- actions: "setVerification"
1675
- },
1676
- {
1677
- guard: "submittedFailed",
1678
- target: "failed",
1679
- actions: "setVerification"
1680
- },
1681
- {
1682
- target: "polling",
1683
- actions: "setVerification"
1684
- }
1685
- ],
1686
- onError: {
1687
- target: "error",
1688
- actions: "setError"
1689
- }
1690
- } },
1691
- polling: {
1692
- invoke: {
1693
- src: "pollStatus",
1694
- input: ({ context }) => ({ verificationId: context.verificationId ?? "" })
1695
- },
1696
- on: { POLL_RESULT: [{
1697
- guard: "polledOk",
1698
- target: "succeeded",
1699
- actions: "setVerificationFromEvent"
1700
- }, {
1701
- target: "failed",
1702
- actions: "setVerificationFromEvent"
1703
- }] },
1704
- after: { [POLL_INTERVAL_MS * MAX_POLL_ATTEMPTS]: {
1705
- target: "failed",
1706
- actions: assign({ failureReason: () => "SESSION_EXPIRED" })
1707
- } }
1708
- },
1709
- succeeded: { on: { CONTINUE: { target: "finished" } } },
1710
- finished: { type: "final" },
1711
- failed: { on: {
1712
- RETRY: [{
1713
- guard: "hasPresetProvider",
1714
- target: "creating"
1715
- }, { target: "choosingProvider" }],
1716
- USE_ANOTHER_METHOD: { target: "closed" },
1717
- CLOSE: { target: "closed" }
1718
- } },
1719
- error: { on: {
1720
- RETRY: [{
1721
- guard: "hasPresetProvider",
1722
- target: "creating",
1723
- actions: "clearResume"
1724
- }, {
1725
- target: "choosingProvider",
1726
- actions: "clearResume"
1727
- }],
1728
- USE_ANOTHER_METHOD: { target: "closed" },
1729
- CLOSE: { target: "closed" }
1730
- } },
1731
- closed: { type: "final" }
1732
- }
1733
- });
1734
- //#endregion
1735
- //#region src/modules/id/idCaptureActors.ts
1736
- const checkPermissionActor = fromPromise(async () => {
1737
- return checkPermission();
1738
- });
1739
- async function requestIdCapturePermission(input) {
1740
- if (!isAndroid() || getBrowser() !== "firefox") return invokeRequestPermissionActor(input);
1741
- if (input.requestMotionPermission) {
1742
- const motionResult = await invokeRequestPermissionActor({
1743
- ...input,
1744
- permissionResult: "granted"
1745
- });
1746
- if (motionResult !== "granted") return motionResult;
1747
- }
1748
- const { stream, fallbackLevel } = await getIdCameraStream();
1749
- return {
1750
- permissionResult: "granted",
1751
- stream,
1752
- cameraFallbackLevel: fallbackLevel
1753
- };
1754
- }
1755
- const requestPermissionActor = fromPromise(async ({ input }) => {
1756
- return requestIdCapturePermission(input);
1757
- });
1758
- const initializeCameraActor = fromPromise(async ({ input }) => {
1759
- return initializeIdCapture(input);
1760
- });
1761
- const runDetectionActor = fromCallback(({ input, sendBack }) => {
1473
+ fromCallback(({ input, sendBack }) => {
1762
1474
  if (!input.frameCapturer || !input.provider) {
1763
1475
  sendBack({
1764
1476
  type: "DETECTION_UPDATE",
@@ -1772,7 +1484,6 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
1772
1484
  let storedQualityElements = {};
1773
1485
  let isCapturing = false;
1774
1486
  const timer = BrowserTimerProvider.getInstance();
1775
- let notificationTimeout = null;
1776
1487
  let optimizingBoundaryTimeout = null;
1777
1488
  /**
1778
1489
  * IDC-05 latch for the boundary clock: whether a boundary is currently
@@ -1780,24 +1491,8 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
1780
1491
  * the clock's only T0; nothing in WASM enforces that it fires once per window.
1781
1492
  */
1782
1493
  let isOptimizingBoundaryArmed = false;
1783
- let currentNotificationStatus = null;
1784
1494
  let lastReportedSide = null;
1785
- const NOTIFICATION_DURATION_MS = 500;
1786
- const isReportedSideWrong = () => {
1787
- if (!lastReportedSide) return false;
1788
- const detectedSide = lastReportedSide.toLowerCase();
1789
- if (detectedSide === "wrong") return true;
1790
- const isBackDetected = detectedSide.includes("back") && !detectedSide.includes("front");
1791
- const isFrontDetected = detectedSide.includes("front") && !detectedSide.includes("back");
1792
- return input.currentMode === "front" && isBackDetected || input.currentMode === "back" && isFrontDetected;
1793
- };
1794
- const clearNotificationTimeout = () => {
1795
- if (notificationTimeout) {
1796
- timer.clearTimeout(notificationTimeout);
1797
- notificationTimeout = null;
1798
- currentNotificationStatus = null;
1799
- }
1800
- };
1495
+ const isReportedSideWrong = () => isReportedIdSideWrong(input.currentMode, lastReportedSide);
1801
1496
  const clearOptimizingBoundary = () => {
1802
1497
  if (optimizingBoundaryTimeout) {
1803
1498
  timer.clearTimeout(optimizingBoundaryTimeout);
@@ -1842,173 +1537,130 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
1842
1537
  candidateCanvas
1843
1538
  });
1844
1539
  };
1845
- const sendNotification = (status) => {
1846
- if (isReportedSideWrong()) return;
1847
- if (notificationTimeout && currentNotificationStatus !== status) return;
1848
- if (notificationTimeout && currentNotificationStatus === status) timer.clearTimeout(notificationTimeout);
1849
- currentNotificationStatus = status;
1850
- trueSightHintTransition("module.id-capture", "idScan", status);
1851
- sendBack({
1540
+ const notifications = createIdDetectionNotificationController({
1541
+ send: (status) => sendBack({
1852
1542
  type: "DETECTION_UPDATE",
1853
1543
  status
1854
- });
1855
- notificationTimeout = timer.setTimeout(() => {
1856
- notificationTimeout = null;
1857
- currentNotificationStatus = null;
1858
- sendBack({
1859
- type: "DETECTION_UPDATE",
1860
- status: "detecting"
1861
- });
1862
- }, NOTIFICATION_DURATION_MS);
1863
- };
1864
- const canvas = input.frameCapturer.getLatestCanvas();
1865
- const windowDimensions = getWindowDimensions(canvas?.width() ?? 1280, canvas?.height() ?? 720);
1866
- const DEFAULT_GEOMETRY = {
1867
- areaDown: 25e3,
1868
- areaUp: 55e3,
1869
- areaIOSPassportUp: 3e4,
1870
- areaIOSPassportDown: 2e4,
1871
- widthIOSUp: 160,
1872
- widthIOSDown: 85,
1873
- widthDown: 110,
1874
- widthUp: 205
1875
- };
1876
- if (input.config.geometry) provider.setGeometry({
1877
- ...input.config.geometry,
1878
- windowOuterWidth: windowDimensions.outerWidth,
1879
- windowOuterHeight: windowDimensions.outerHeight,
1880
- windowInnerWidth: windowDimensions.innerWidth,
1881
- windowInnerHeight: windowDimensions.innerHeight
1882
- });
1883
- else provider.setGeometry({
1884
- ...DEFAULT_GEOMETRY,
1885
- windowOuterWidth: windowDimensions.outerWidth,
1886
- windowOuterHeight: windowDimensions.outerHeight,
1887
- windowInnerWidth: windowDimensions.innerWidth,
1888
- windowInnerHeight: windowDimensions.innerHeight
1889
- });
1890
- const idType = input.currentMode === "back" ? "BackId" : input.currentMode === "passport" ? "Passport" : "FrontId";
1891
- provider.setSettings({
1892
- isFixedMask: input.config.settings?.isFixedMask ?? false,
1893
- isIPhone14OrHigher: input.config.settings?.isIPhone14OrHigher ?? false,
1894
- idType,
1895
- blurCheckEnabled: input.config.settings?.blurCheckEnabled ?? false,
1896
- glareCheckEnabled: input.config.settings?.glareCheckEnabled ?? false,
1897
- faceQualityCheckEnabled: input.config.settings?.faceQualityCheckEnabled ?? true,
1898
- iouCheckEnabled: input.config.settings?.iouCheckEnabled ?? true,
1899
- fpsLimitEnabled: input.config.settings?.fpsLimitEnabled ?? false
1544
+ }),
1545
+ isSuppressed: isReportedSideWrong,
1546
+ onStatusShown: (status) => trueSightHintTransition("module.id-capture", "idScan", status)
1900
1547
  });
1901
- provider.setThresholds(resolveIdCaptureThresholds(input.config));
1902
- const modelType = input.config.modelVersion ?? "v2";
1903
- provider.setModelType(modelType);
1904
- provider.setCallbacks({
1905
- onFarAway: () => {
1906
- if (!isCapturing) sendNotification("farAway");
1907
- },
1908
- onDetectionStarted: () => {},
1909
- onMaskChange: (_show, _mask, _top, orientation) => {
1910
- sendBack({
1911
- type: "ORIENTATION_CHANGE",
1912
- orientation
1913
- });
1914
- },
1915
- onBlur: () => {
1916
- if (!isCapturing) sendNotification("blur");
1917
- },
1918
- onGlare: () => {
1919
- if (!isCapturing) sendNotification("glare");
1548
+ const canvas = input.frameCapturer.getLatestCanvas();
1549
+ const canvasWidth = canvas?.width() ?? 1280;
1550
+ const canvasHeight = canvas?.height() ?? 720;
1551
+ configureIdCaptureProvider({
1552
+ provider,
1553
+ config: input.config,
1554
+ currentMode: input.currentMode,
1555
+ frameSize: {
1556
+ width: canvasWidth,
1557
+ height: canvasHeight
1920
1558
  },
1921
- onIdNotDetected: () => {
1922
- if (!isCapturing) {
1923
- trueSightHintTransition("module.id-capture", "idScan", "idNotDetected");
1559
+ callbacks: {
1560
+ onFarAway: () => {
1561
+ if (!isCapturing) notifications.show("farAway");
1562
+ },
1563
+ onDetectionStarted: () => {},
1564
+ onMaskChange: (_show, _mask, _top, orientation) => {
1924
1565
  sendBack({
1925
- type: "DETECTION_UPDATE",
1926
- status: "idNotDetected"
1566
+ type: "ORIENTATION_CHANGE",
1567
+ orientation
1927
1568
  });
1928
- }
1929
- },
1930
- onSwitchToManualCapture: () => {
1931
- isCapturing = false;
1932
- clearOptimizingBoundary();
1933
- sendBack({ type: "SWITCH_TO_MANUAL_CAPTURE" });
1934
- },
1935
- onCapturing: () => {
1936
- clearNotificationTimeout();
1937
- lastReportedSide = null;
1938
- isCapturing = true;
1939
- trueSightHintTransition("module.id-capture", "idScan", "capturing");
1940
- sendBack({
1941
- type: "DETECTION_UPDATE",
1942
- status: "capturing"
1943
- });
1944
- scheduleOptimizingBoundary();
1945
- },
1946
- onBestFrame: (blur, glare, orientation) => {
1947
- if (currentCanvas) {
1948
- bestCanvas = currentCanvas.clone();
1949
- storedQualityElements = {
1950
- glare,
1951
- sharpness: blur
1952
- };
1953
- }
1954
- if (orientation === "horizontal" || orientation === "vertical") sendBack({
1955
- type: "ORIENTATION_CHANGE",
1956
- orientation
1957
- });
1958
- },
1959
- onCapture: () => {
1960
- isCapturing = false;
1961
- if (isOptimizingBoundaryArmed) {
1569
+ },
1570
+ onBlur: () => {
1571
+ if (!isCapturing) notifications.show("blur");
1572
+ },
1573
+ onGlare: () => {
1574
+ if (!isCapturing) notifications.show("glare");
1575
+ },
1576
+ onIdNotDetected: () => {
1577
+ if (!isCapturing) {
1578
+ trueSightHintTransition("module.id-capture", "idScan", "idNotDetected");
1579
+ sendBack({
1580
+ type: "DETECTION_UPDATE",
1581
+ status: "idNotDetected"
1582
+ });
1583
+ }
1584
+ },
1585
+ onSwitchToManualCapture: () => {
1586
+ isCapturing = false;
1962
1587
  clearOptimizingBoundary();
1963
- enterOptimizing();
1964
- }
1965
- if (!bestCanvas) {
1966
- const clonedCanvas = currentCanvas?.clone();
1967
- if (clonedCanvas) sendBack({
1968
- type: "DETECTION_SUCCESS",
1969
- canvas: clonedCanvas,
1970
- qualityElements: {}
1588
+ sendBack({ type: "SWITCH_TO_MANUAL_CAPTURE" });
1589
+ },
1590
+ onCapturing: () => {
1591
+ notifications.clear();
1592
+ lastReportedSide = null;
1593
+ isCapturing = true;
1594
+ trueSightHintTransition("module.id-capture", "idScan", "capturing");
1595
+ sendBack({
1596
+ type: "DETECTION_UPDATE",
1597
+ status: "capturing"
1598
+ });
1599
+ scheduleOptimizingBoundary();
1600
+ },
1601
+ onBestFrame: (blur, glare, orientation) => {
1602
+ if (currentCanvas) {
1603
+ bestCanvas = currentCanvas.clone();
1604
+ storedQualityElements = {
1605
+ glare,
1606
+ sharpness: blur
1607
+ };
1608
+ }
1609
+ if (orientation === "horizontal" || orientation === "vertical") sendBack({
1610
+ type: "ORIENTATION_CHANGE",
1611
+ orientation
1612
+ });
1613
+ },
1614
+ onCapture: () => {
1615
+ isCapturing = false;
1616
+ if (isOptimizingBoundaryArmed) {
1617
+ clearOptimizingBoundary();
1618
+ enterOptimizing();
1619
+ }
1620
+ if (!bestCanvas) {
1621
+ const clonedCanvas = currentCanvas?.clone();
1622
+ if (clonedCanvas) sendBack({
1623
+ type: "DETECTION_SUCCESS",
1624
+ canvas: clonedCanvas,
1625
+ qualityElements: {}
1626
+ });
1627
+ return;
1628
+ }
1629
+ sendBack({
1630
+ type: "DETECTION_SUCCESS",
1631
+ canvas: bestCanvas,
1632
+ qualityElements: storedQualityElements
1633
+ });
1634
+ },
1635
+ onIdTypeChange: (idType) => {
1636
+ sendBack({
1637
+ type: "ID_TYPE_CHANGE",
1638
+ idType
1639
+ });
1640
+ },
1641
+ onIdSideChange: (side) => {
1642
+ if (isCapturing) return;
1643
+ lastReportedSide = side;
1644
+ if (isReportedSideWrong()) notifications.clear();
1645
+ sendBack({
1646
+ type: "ID_SIDE_CHANGE",
1647
+ side
1648
+ });
1649
+ },
1650
+ onCapturingCounterValueChange: (value) => {
1651
+ sendBack({
1652
+ type: "COUNTER_VALUE_CHANGE",
1653
+ value
1654
+ });
1655
+ },
1656
+ onBarcodeDecoded: (decoded) => {
1657
+ sendBack({
1658
+ type: "BARCODE_DECODED",
1659
+ decoded
1971
1660
  });
1972
- return;
1973
1661
  }
1974
- sendBack({
1975
- type: "DETECTION_SUCCESS",
1976
- canvas: bestCanvas,
1977
- qualityElements: storedQualityElements
1978
- });
1979
- },
1980
- onIdTypeChange: (idType) => {
1981
- sendBack({
1982
- type: "ID_TYPE_CHANGE",
1983
- idType
1984
- });
1985
- },
1986
- onIdSideChange: (side) => {
1987
- if (isCapturing) return;
1988
- lastReportedSide = side;
1989
- if (isReportedSideWrong()) clearNotificationTimeout();
1990
- sendBack({
1991
- type: "ID_SIDE_CHANGE",
1992
- side
1993
- });
1994
- },
1995
- onCapturingCounterValueChange: (value) => {
1996
- sendBack({
1997
- type: "COUNTER_VALUE_CHANGE",
1998
- value
1999
- });
2000
- },
2001
- onBarcodeDecoded: (decoded) => {
2002
- sendBack({
2003
- type: "BARCODE_DECODED",
2004
- decoded
2005
- });
2006
1662
  }
2007
1663
  });
2008
- const barcodeCaptureEnabled = input.config.usSmartCapture === true && input.currentMode === "back";
2009
- provider.setBarcodeCaptureEnabled(barcodeCaptureEnabled);
2010
- provider.setBarcodeAutoCaptureOnDecode(false);
2011
- if (barcodeCaptureEnabled) provider.setBarcodeEnhancementModel().catch(() => {});
2012
1664
  sendBack({
2013
1665
  type: "DETECTION_UPDATE",
2014
1666
  status: "detecting"
@@ -2021,11 +1673,7 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
2021
1673
  await provider.processFrame(frame);
2022
1674
  },
2023
1675
  reset: () => {
2024
- if (notificationTimeout) {
2025
- timer.clearTimeout(notificationTimeout);
2026
- notificationTimeout = null;
2027
- currentNotificationStatus = null;
2028
- }
1676
+ notifications.clear();
2029
1677
  clearOptimizingBoundary();
2030
1678
  currentCanvas = null;
2031
1679
  bestCanvas = null;
@@ -2044,7 +1692,7 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
2044
1692
  }
2045
1693
  });
2046
1694
  return () => {
2047
- clearNotificationTimeout();
1695
+ notifications.clear();
2048
1696
  clearOptimizingBoundary();
2049
1697
  session?.dispose();
2050
1698
  noteTrueSightCaptureActivity(false);
@@ -2057,7 +1705,7 @@ const uploadProgressTickerActor = fromCallback(({ sendBack }) => {
2057
1705
  }, 400);
2058
1706
  return () => timer.clearInterval(interval);
2059
1707
  });
2060
- const uploadIdImageActor = fromPromise(async ({ input, signal }) => {
1708
+ fromPromise(async ({ input, signal }) => {
2061
1709
  const image = input.canvas.getBase64Image();
2062
1710
  if (!image) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
2063
1711
  let metadata;
@@ -2121,7 +1769,7 @@ const submitBarcodeActor = fromPromise(async ({ input, signal }) => {
2121
1769
  }), input.minDurationMs ? sleep(input.minDurationMs) : Promise.resolve()]);
2122
1770
  return response;
2123
1771
  });
2124
- const startRecordingActor = fromPromise(async ({ input }) => {
1772
+ fromPromise(async ({ input }) => {
2125
1773
  if (!input.stream) return;
2126
1774
  const type = input.currentMode === "back" ? "backId" : "frontId";
2127
1775
  return startRecordingSession({
@@ -2131,16 +1779,16 @@ const startRecordingActor = fromPromise(async ({ input }) => {
2131
1779
  type
2132
1780
  });
2133
1781
  });
2134
- const initializeDeepsightSessionActor = fromPromise(async ({ input }) => {
1782
+ fromPromise(async ({ input }) => {
2135
1783
  if (!input.dependencies) return;
2136
- const { loadDeepsightSession } = await import("./deepsightLoader-Dwr1zKFa.esm.js");
1784
+ const { loadDeepsightSession } = await import("./deepsightLoader-Dhz9T_KJ.esm.js");
2137
1785
  return loadDeepsightSession({
2138
1786
  ds: input.ds,
2139
1787
  storage: input.dependencies.storage,
2140
1788
  disableIpify: input.disableIpify
2141
1789
  });
2142
1790
  });
2143
- const checkVirtualCameraActor = fromPromise(async ({ input }) => {
1791
+ fromPromise(async ({ input }) => {
2144
1792
  if (!input.deepsightService || !input.stream) return false;
2145
1793
  const videoTrack = input.stream.getVideoTracks()[0];
2146
1794
  if (!videoTrack) return false;
@@ -2155,9 +1803,6 @@ function performStopMediaStream(context) {
2155
1803
  function performDisposeProvider(context) {
2156
1804
  context.provider?.dispose?.();
2157
1805
  }
2158
- function performCleanupDeepsight(context) {
2159
- context.deepsightService?.cleanup();
2160
- }
2161
1806
  function performResetForBackCapture(context) {
2162
1807
  context.frameCapturer?.dispose();
2163
1808
  if (context.stream) stopStream(context.stream);
@@ -2168,9 +1813,6 @@ function performResetForFrontCapture(context) {
2168
1813
  if (context.stream) stopStream(context.stream);
2169
1814
  context.provider?.reset();
2170
1815
  }
2171
- function performResetDetection(context) {
2172
- context.resetDetection?.();
2173
- }
2174
1816
  function performReportCaptureOptimizingAssignment(context) {
2175
1817
  if (context.config.supportsOptimizingStatus !== true) return;
2176
1818
  const assignment = exposeExperimentOnce(CAPTURE_OPTIMIZING);
@@ -2180,15 +1822,9 @@ function performReportCaptureOptimizingAssignment(context) {
2180
1822
  function performStopMediaRecording(context) {
2181
1823
  if (context.recordingSession) stopRecording(context.recordingSession);
2182
1824
  }
2183
- function performPauseRecordingForAttempt(context) {
2184
- if (context.recordingSession) pauseRecordingForAttempt(context.recordingSession);
2185
- }
2186
1825
  function performPrepareRecordingForCameraRetry(context) {
2187
1826
  if (context.recordingSession) prepareRecordingForCameraRetry(context.recordingSession);
2188
1827
  }
2189
- function getStreamFromEvent(event) {
2190
- if ("output" in event) return event.output.stream;
2191
- }
2192
1828
  function getPermissionResultFromEvent(event) {
2193
1829
  if (!("output" in event)) return;
2194
1830
  const { output } = event;
@@ -2198,24 +1834,9 @@ function getPermissionResultFromEvent(event) {
2198
1834
  if (result === "granted" || result === "denied" || result === "prompt") return result;
2199
1835
  }
2200
1836
  }
2201
- function getProviderFromEvent(event) {
2202
- if ("output" in event) return event.output.provider;
2203
- }
2204
- function getCaptureResolutionFromEvent(event) {
2205
- if ("output" in event) return event.output.captureResolution;
2206
- }
2207
1837
  function getCameraFallbackLevelFromEvent(event) {
2208
1838
  if ("output" in event) return event.output.cameraFallbackLevel;
2209
1839
  }
2210
- function getUsingBackCameraFromEvent(event) {
2211
- if ("output" in event) return event.output.usingBackCamera;
2212
- }
2213
- function getFrameCapturerFromEvent(event) {
2214
- if ("output" in event) {
2215
- const output = event.output;
2216
- if (output.stream) return new StreamCanvasCapture(output.stream);
2217
- }
2218
- }
2219
1840
  function getResetContextValues(context) {
2220
1841
  context.provider?.reset();
2221
1842
  const currentMode = context.config.onlyBack ? "back" : !context.config.enableId && context.config.enablePassport ? "passport" : context.config.usSmartCapture ? "back" : "front";
@@ -2274,19 +1895,6 @@ function getUploadValidationError(context) {
2274
1895
  skipGlareBack: context.uploadResponse.skipGlareBack
2275
1896
  })?.errorKey ?? ID_ERROR_CODES.SERVER;
2276
1897
  }
2277
- function storeCapturedCanvasInProviderLogic(context, event) {
2278
- let canvas = null;
2279
- if ("canvas" in event && event.canvas) canvas = event.canvas;
2280
- else canvas = context.frameCapturer?.getLatestCanvas() ?? null;
2281
- if (!canvas || !context.provider) return;
2282
- const canvasWidth = canvas.width();
2283
- const canvasHeight = canvas.height();
2284
- if (!canvasWidth || !canvasHeight) return;
2285
- const originalCanvas = canvas;
2286
- const frameRect = resolveCaptureFrameRect(context, canvasWidth, canvasHeight);
2287
- const transformedCanvas = context.provider.transformPerspective(originalCanvas, frameRect);
2288
- context.provider.setCapturedCanvases(originalCanvas, transformedCanvas);
2289
- }
2290
1898
  /**
2291
1899
  * JPEG quality for the IDC-05 optimizing phase's display-only still.
2292
1900
  *
@@ -2426,10 +2034,10 @@ function getStoreCapturedImageValues(context, event) {
2426
2034
  *
2427
2035
  * Revokes any previous URL rather than leaking one per retry.
2428
2036
  */
2429
- function getCandidateFrameValues(context, event) {
2037
+ function getCandidateFrameValues(context, event, provider = context.provider) {
2430
2038
  if (event.type !== "DETECTION_OPTIMIZING" || !event.candidateCanvas) return { candidateImageUrl: context.candidateImageUrl };
2431
2039
  const candidateCanvas = event.candidateCanvas;
2432
- const canvas = cropCanvasForPreview(context, candidateCanvas);
2040
+ const canvas = cropCanvasForPreview(context, candidateCanvas, provider);
2433
2041
  canvas.updateBlob(CANDIDATE_FRAME_JPEG_QUALITY);
2434
2042
  const url = canvas.getBlobData()?.url;
2435
2043
  if (!url) {
@@ -2464,12 +2072,12 @@ function releasePreviewCanvases(source, preview, { shouldReleasePreview }) {
2464
2072
  if (shouldReleasePreview) preview.release();
2465
2073
  source.release();
2466
2074
  }
2467
- function cropCanvasForPreview(context, canvas) {
2075
+ function cropCanvasForPreview(context, canvas, provider = context.provider) {
2468
2076
  const canvasWidth = canvas.width();
2469
2077
  const canvasHeight = canvas.height();
2470
- if (!context.provider || !canvasWidth || !canvasHeight) return canvas;
2078
+ if (!provider || !canvasWidth || !canvasHeight) return canvas;
2471
2079
  const frameRect = resolveCaptureFrameRect(context, canvasWidth, canvasHeight);
2472
- return context.provider.transformPerspective(canvas, frameRect);
2080
+ return provider.transformPerspective(canvas, frameRect);
2473
2081
  }
2474
2082
  /**
2475
2083
  * Drops the IDC-05 best-so-far still at every attempt/side boundary. Unlike
@@ -2491,6 +2099,7 @@ function getDetectionStatusFromUpdate(context, event) {
2491
2099
  if (event.type === "DETECTION_UPDATE") {
2492
2100
  const newStatus = event.status;
2493
2101
  const currentStatus = context.detectionStatus;
2102
+ if (currentStatus === "capturing" && newStatus !== "offline") return currentStatus;
2494
2103
  if (currentStatus === "wrongSide") {
2495
2104
  if (!(newStatus === "capturing" || newStatus === "manualCapture" || newStatus === "offline")) return currentStatus;
2496
2105
  return newStatus;
@@ -2550,8 +2159,1353 @@ function getBarcodeResponseValues(context, event) {
2550
2159
  isDocumentExpired: output?.isDocumentExpired,
2551
2160
  success: output?.success
2552
2161
  },
2553
- skipProcessId: output?.idValidationCompleted === true || context.skipProcessId
2554
- };
2162
+ skipProcessId: output?.idValidationCompleted === true || context.skipProcessId
2163
+ };
2164
+ }
2165
+ //#endregion
2166
+ //#region src/modules/id/idCaptureDiagnostics.ts
2167
+ const ID_CAMERA_DIAGNOSTIC_PREFIX = "[Incode ID camera]";
2168
+ function finiteNumber(value) {
2169
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2170
+ }
2171
+ function stringValue(value) {
2172
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2173
+ }
2174
+ function readFocusRequest(constraints) {
2175
+ const sets = [constraints, ...constraints.advanced ?? []];
2176
+ const requestedFocusMode = sets.map((set) => stringValue(set.focusMode)).find((value) => value !== void 0);
2177
+ const requestedFocusDistance = sets.map((set) => finiteNumber(set.focusDistance)).find((value) => value !== void 0);
2178
+ return {
2179
+ focusRequested: requestedFocusMode !== void 0 || requestedFocusDistance !== void 0,
2180
+ requestedFocusMode,
2181
+ requestedFocusDistance
2182
+ };
2183
+ }
2184
+ /**
2185
+ * Returns an intentionally narrow, non-identifying camera diagnostic view.
2186
+ * Device IDs and labels must never be added here.
2187
+ */
2188
+ function getIdCameraTrackDiagnosticFields(track) {
2189
+ if (!track) return {};
2190
+ let settings = {};
2191
+ let constraints = {};
2192
+ try {
2193
+ settings = track.getSettings();
2194
+ } catch {}
2195
+ try {
2196
+ constraints = track.getConstraints();
2197
+ } catch {}
2198
+ return {
2199
+ width: finiteNumber(settings.width),
2200
+ height: finiteNumber(settings.height),
2201
+ frameRate: finiteNumber(settings.frameRate),
2202
+ facingMode: stringValue(settings.facingMode),
2203
+ readyState: track.readyState,
2204
+ muted: track.muted,
2205
+ devicePinned: settings.deviceId !== void 0,
2206
+ ...readFocusRequest(constraints),
2207
+ appliedFocusMode: stringValue(settings.focusMode),
2208
+ appliedFocusDistance: finiteNumber(settings.focusDistance)
2209
+ };
2210
+ }
2211
+ /**
2212
+ * Emits a bounded diagnostic line only in SDK testing mode (devMode or a
2213
+ * recognized non-production API host). The payload is primitive-only so the
2214
+ * browser console cannot retain live media or native-backed objects.
2215
+ */
2216
+ function logIdCameraDiagnostic(event, fields) {
2217
+ if (!isTrueSightTesting()) return;
2218
+ console.debug(`${ID_CAMERA_DIAGNOSTIC_PREFIX} ${event} ${JSON.stringify(fields)}`);
2219
+ }
2220
+ //#endregion
2221
+ //#region src/modules/id/idMetadataPreparationCoordinator.ts
2222
+ function cleanup(service) {
2223
+ try {
2224
+ service.cleanup();
2225
+ } catch {}
2226
+ }
2227
+ function createIdMetadataPreparationCoordinator(load = async (input) => (await import("./deepsightLoader-Dhz9T_KJ.esm.js")).loadDeepsightSession(input)) {
2228
+ let disposed = false;
2229
+ let preparation;
2230
+ let service;
2231
+ const prepare = (input) => {
2232
+ if (disposed) return Promise.reject(/* @__PURE__ */ new Error("ID metadata coordinator is disposed"));
2233
+ preparation ??= (async () => {
2234
+ const prepared = await load(input);
2235
+ if (disposed) {
2236
+ cleanup(prepared);
2237
+ throw new Error("ID metadata coordinator is disposed");
2238
+ }
2239
+ service = prepared;
2240
+ return prepared;
2241
+ })();
2242
+ return preparation;
2243
+ };
2244
+ return {
2245
+ start(input) {
2246
+ prepare(input).catch(() => void 0);
2247
+ },
2248
+ prepare,
2249
+ dispose() {
2250
+ if (disposed) return;
2251
+ disposed = true;
2252
+ preparation = void 0;
2253
+ const prepared = service;
2254
+ service = void 0;
2255
+ if (prepared) cleanup(prepared);
2256
+ }
2257
+ };
2258
+ }
2259
+ //#endregion
2260
+ //#region src/modules/id/idProtectedCapture.ts
2261
+ const ID_PROTECTED_CAPTURE_CONTROLLER = Symbol("idProtectedCaptureController");
2262
+ function attachIdProtectedCaptureController(owner, controller) {
2263
+ const target = owner;
2264
+ const existing = target[ID_PROTECTED_CAPTURE_CONTROLLER];
2265
+ if (existing) {
2266
+ if (existing !== controller) {
2267
+ existing.dispose();
2268
+ target[ID_PROTECTED_CAPTURE_CONTROLLER] = controller;
2269
+ }
2270
+ return controller;
2271
+ }
2272
+ Object.defineProperty(target, ID_PROTECTED_CAPTURE_CONTROLLER, {
2273
+ configurable: false,
2274
+ enumerable: false,
2275
+ value: controller,
2276
+ writable: true
2277
+ });
2278
+ return controller;
2279
+ }
2280
+ function getIdProtectedCaptureController(owner) {
2281
+ const controller = findIdProtectedCaptureController(owner);
2282
+ if (!controller) throw new Error("ID protected capture controller is unavailable");
2283
+ return controller;
2284
+ }
2285
+ function findIdProtectedCaptureController(owner) {
2286
+ return owner[ID_PROTECTED_CAPTURE_CONTROLLER];
2287
+ }
2288
+ function streamResolution(stream) {
2289
+ const track = stream.getVideoTracks()[0];
2290
+ const settings = track && typeof track.getSettings === "function" ? track.getSettings() : void 0;
2291
+ return typeof settings?.width === "number" && typeof settings.height === "number" ? `${settings.width}x${settings.height}` : void 0;
2292
+ }
2293
+ function streamUsesBackCamera(stream) {
2294
+ const track = stream.getVideoTracks()[0];
2295
+ if (!track) return;
2296
+ const settings = typeof track.getSettings === "function" ? track.getSettings() : {};
2297
+ const constrainedFacingMode = typeof track.getConstraints === "function" ? track.getConstraints().facingMode : void 0;
2298
+ const facingMode = settings.facingMode ?? (typeof constrainedFacingMode === "string" ? constrainedFacingMode : void 0);
2299
+ if (facingMode === "environment") return true;
2300
+ if (facingMode === "user") return false;
2301
+ const label = typeof track.label === "string" ? track.label.toLowerCase() : "";
2302
+ if (label.includes("back") || label.includes("rear")) return true;
2303
+ if (label.includes("front") || label.includes("user")) return false;
2304
+ }
2305
+ function createIdProtectedCaptureController(dependencies = {}) {
2306
+ const metadataCoordinator = createIdMetadataPreparationCoordinator(dependencies.loadMetadata);
2307
+ let disposed = false;
2308
+ let visible = true;
2309
+ let preAcquired;
2310
+ let preAcquirePromise;
2311
+ let preAcquireGeneration = 0;
2312
+ let provider;
2313
+ let displayStream;
2314
+ let frameCapturer;
2315
+ let captureResolution;
2316
+ let cameraFallbackLevel;
2317
+ let usingBackCamera;
2318
+ let previewImageUrl;
2319
+ let pendingPreviewCanvas;
2320
+ let attemptGeneration = 0;
2321
+ let autoCaptureFrame;
2322
+ let committed = false;
2323
+ let uploadResponse;
2324
+ let cycleOwnership;
2325
+ let attemptContext;
2326
+ let attemptSend;
2327
+ let diagnosticAttempt;
2328
+ const logDiagnostic = (event, fields = {}) => {
2329
+ if (!diagnosticAttempt) return;
2330
+ logIdCameraDiagnostic(event, {
2331
+ attempt: diagnosticAttempt.attempt,
2332
+ side: diagnosticAttempt.side,
2333
+ elapsedMs: Math.round(getTimestamp() - diagnosticAttempt.startedAt),
2334
+ ...fields
2335
+ });
2336
+ };
2337
+ const releasePresentationFrame = (frame) => {
2338
+ frame.width = 1;
2339
+ frame.height = 1;
2340
+ };
2341
+ const releasePreAcquired = () => {
2342
+ if (preAcquired) {
2343
+ stopStream(preAcquired.stream);
2344
+ preAcquired = void 0;
2345
+ }
2346
+ };
2347
+ const acceptPreAcquired = (value) => {
2348
+ if (disposed) {
2349
+ stopStream(value.stream);
2350
+ return false;
2351
+ }
2352
+ releasePreAcquired();
2353
+ preAcquired = value;
2354
+ captureResolution = streamResolution(value.stream);
2355
+ cameraFallbackLevel = value.fallbackLevel;
2356
+ usingBackCamera = streamUsesBackCamera(value.stream);
2357
+ return true;
2358
+ };
2359
+ const preAcquire = () => {
2360
+ if (disposed) return Promise.reject(/* @__PURE__ */ new Error("ID protected capture controller is disposed"));
2361
+ if (preAcquired) return Promise.resolve();
2362
+ if (preAcquirePromise) return preAcquirePromise;
2363
+ const generation = preAcquireGeneration;
2364
+ const pending = (async () => {
2365
+ const value = await (dependencies.acquireCamera ?? getIdCameraStream)();
2366
+ if (disposed || generation !== preAcquireGeneration) {
2367
+ stopStream(value.stream);
2368
+ if (disposed) throw new Error("ID protected capture controller is disposed");
2369
+ return;
2370
+ }
2371
+ acceptPreAcquired(value);
2372
+ })();
2373
+ preAcquirePromise = pending;
2374
+ pending.then(() => {
2375
+ if (preAcquirePromise === pending) preAcquirePromise = void 0;
2376
+ }, () => {
2377
+ if (preAcquirePromise === pending) preAcquirePromise = void 0;
2378
+ });
2379
+ return pending;
2380
+ };
2381
+ const replaceDisplayStream = (stream) => {
2382
+ frameCapturer?.dispose();
2383
+ frameCapturer = void 0;
2384
+ if (displayStream && displayStream !== stream) stopStream(displayStream);
2385
+ displayStream = stream;
2386
+ frameCapturer = new StreamCanvasCapture(stream, { continuous: false });
2387
+ captureResolution = streamResolution(stream);
2388
+ usingBackCamera = streamUsesBackCamera(stream) ?? usingBackCamera;
2389
+ };
2390
+ const clearAttempt = () => {
2391
+ if (diagnosticAttempt) logDiagnostic("attempt-release", { committed });
2392
+ attemptGeneration += 1;
2393
+ frameCapturer?.dispose();
2394
+ frameCapturer = void 0;
2395
+ if (displayStream) stopStream(displayStream);
2396
+ displayStream = void 0;
2397
+ provider?.dispose?.();
2398
+ provider = void 0;
2399
+ captureResolution = void 0;
2400
+ usingBackCamera = void 0;
2401
+ previewImageUrl = void 0;
2402
+ pendingPreviewCanvas?.dispose();
2403
+ pendingPreviewCanvas = void 0;
2404
+ if (autoCaptureFrame) {
2405
+ releasePresentationFrame(autoCaptureFrame);
2406
+ autoCaptureFrame = void 0;
2407
+ }
2408
+ committed = false;
2409
+ uploadResponse = void 0;
2410
+ attemptContext = void 0;
2411
+ attemptSend = void 0;
2412
+ diagnosticAttempt = void 0;
2413
+ cycleOwnership?.releaseByController();
2414
+ cycleOwnership = void 0;
2415
+ };
2416
+ const storeCommit = (previewElement) => {
2417
+ previewImageUrl = void 0;
2418
+ pendingPreviewCanvas?.dispose();
2419
+ pendingPreviewCanvas = void 0;
2420
+ const generation = attemptGeneration;
2421
+ try {
2422
+ const original = new IncodeCanvas(previewElement);
2423
+ let presentation = original;
2424
+ if (attemptContext) try {
2425
+ presentation = cropCanvasForPreview(attemptContext, original, provider);
2426
+ } catch {}
2427
+ try {
2428
+ provider?.setCapturedCanvases(original, presentation);
2429
+ } catch {}
2430
+ pendingPreviewCanvas = presentation;
2431
+ presentation.updateBlobAsync(1).then((blobData) => {
2432
+ if (disposed || attemptGeneration !== generation || pendingPreviewCanvas !== presentation) {
2433
+ presentation.dispose();
2434
+ return;
2435
+ }
2436
+ if (!blobData) {
2437
+ pendingPreviewCanvas = void 0;
2438
+ return;
2439
+ }
2440
+ previewImageUrl = blobData.url;
2441
+ attemptSend?.({ type: "PROTECTED_PREVIEW_READY" });
2442
+ }).catch(() => {
2443
+ if (pendingPreviewCanvas === presentation) pendingPreviewCanvas = void 0;
2444
+ presentation.dispose();
2445
+ });
2446
+ } finally {
2447
+ releasePresentationFrame(previewElement);
2448
+ committed = true;
2449
+ }
2450
+ };
2451
+ const controller = {
2452
+ get disposed() {
2453
+ return disposed;
2454
+ },
2455
+ beginAttempt(context, send) {
2456
+ if (disposed) throw new Error("ID protected capture controller is disposed");
2457
+ clearAttempt();
2458
+ diagnosticAttempt = {
2459
+ attempt: ++protectedIdAttemptSequence,
2460
+ side: context.currentMode === "back" ? "back" : "front",
2461
+ startedAt: getTimestamp()
2462
+ };
2463
+ attemptContext = context;
2464
+ attemptSend = send;
2465
+ provider = (dependencies.createProvider ?? createProtectedIdProvider)(context.config);
2466
+ cycleOwnership = createCameraCycleOwnership(cameraCycleProvider);
2467
+ logDiagnostic("attempt-start", { aggregationMs: context.config?.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval });
2468
+ },
2469
+ startMetadataPreparation(context) {
2470
+ const storage = context.dependencies?.storage;
2471
+ if (!storage) return;
2472
+ metadataCoordinator.start({
2473
+ ds: context.config.ds,
2474
+ storage,
2475
+ disableIpify: context.disableIpify
2476
+ });
2477
+ },
2478
+ prepareMetadata(context) {
2479
+ const storage = context.dependencies?.storage;
2480
+ if (!storage) return Promise.reject(/* @__PURE__ */ new Error("ID metadata storage is unavailable"));
2481
+ return metadataCoordinator.prepare({
2482
+ ds: context.config.ds,
2483
+ storage,
2484
+ disableIpify: context.disableIpify
2485
+ });
2486
+ },
2487
+ clearAttempt,
2488
+ dispose() {
2489
+ if (disposed) return;
2490
+ disposed = true;
2491
+ preAcquireGeneration += 1;
2492
+ metadataCoordinator.dispose();
2493
+ releasePreAcquired();
2494
+ clearAttempt();
2495
+ },
2496
+ preAcquire,
2497
+ setVisible(nextVisible) {
2498
+ visible = nextVisible;
2499
+ },
2500
+ isVisible() {
2501
+ return visible;
2502
+ },
2503
+ storePreAcquiredStream(value) {
2504
+ preAcquireGeneration += 1;
2505
+ return acceptPreAcquired(value);
2506
+ },
2507
+ takePreAcquiredStream() {
2508
+ const value = preAcquired;
2509
+ preAcquired = void 0;
2510
+ if (value) cameraFallbackLevel = value.fallbackLevel;
2511
+ return value;
2512
+ },
2513
+ getProvider() {
2514
+ return provider;
2515
+ },
2516
+ getCycleOwnership() {
2517
+ if (!cycleOwnership) throw new Error("Protected ID cycle ownership is unavailable");
2518
+ return cycleOwnership;
2519
+ },
2520
+ getProjection() {
2521
+ return {
2522
+ stream: displayStream,
2523
+ captureResolution,
2524
+ cameraFallbackLevel,
2525
+ usingBackCamera
2526
+ };
2527
+ },
2528
+ getDiagnosticAttempt() {
2529
+ return diagnosticAttempt;
2530
+ },
2531
+ getPresentationCanvas() {
2532
+ return frameCapturer?.getLatestCanvas() ?? null;
2533
+ },
2534
+ storeAutoCaptureFrame(canvas) {
2535
+ if (disposed) {
2536
+ if (canvas) releasePresentationFrame(canvas);
2537
+ return;
2538
+ }
2539
+ if (autoCaptureFrame && autoCaptureFrame !== canvas) releasePresentationFrame(autoCaptureFrame);
2540
+ autoCaptureFrame = canvas ?? void 0;
2541
+ },
2542
+ hasAutoCaptureFrame() {
2543
+ return autoCaptureFrame !== void 0;
2544
+ },
2545
+ takeAutoCaptureFrame() {
2546
+ const frame = autoCaptureFrame;
2547
+ autoCaptureFrame = void 0;
2548
+ return frame ?? null;
2549
+ },
2550
+ takeCommitPreviewImageUrl() {
2551
+ const value = previewImageUrl;
2552
+ previewImageUrl = void 0;
2553
+ pendingPreviewCanvas = void 0;
2554
+ return value;
2555
+ },
2556
+ hasCommit() {
2557
+ return committed;
2558
+ },
2559
+ takeUploadResponse() {
2560
+ const value = uploadResponse;
2561
+ uploadResponse = void 0;
2562
+ return value;
2563
+ },
2564
+ buildCaptureOnlyUpload(context, onProgress) {
2565
+ const canvas = provider?.getOriginalCapturedCanvas();
2566
+ if (!canvas) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
2567
+ const type = context.currentMode === "back" ? "back" : "front";
2568
+ return {
2569
+ canvas,
2570
+ type,
2571
+ qualityElements: context.qualityElements,
2572
+ onProgress,
2573
+ deepsightService: void 0,
2574
+ stream: displayStream,
2575
+ isSecondId: context.config.isSecondId ?? false,
2576
+ onlyFront: type === "front" && (context.config.onlyFront ?? false),
2577
+ ageAssurance: context.config.ageAssurance,
2578
+ dependencies: context.dependencies,
2579
+ captureResolution,
2580
+ cameraFallbackLevel,
2581
+ extractIdFace: context.config.extractIdFace,
2582
+ croppedCanvas: provider?.getCapturedCanvas() ?? null
2583
+ };
2584
+ },
2585
+ runAttempt(sendBack, receive) {
2586
+ if (!attemptContext || !attemptSend) throw new Error("Protected ID attempt is unavailable");
2587
+ return (dependencies.runAttempt ?? runProtectedAttempt)({
2588
+ input: createProtectedIdAttemptInput({
2589
+ context: attemptContext,
2590
+ controller,
2591
+ send: attemptSend
2592
+ }),
2593
+ sendBack,
2594
+ receive
2595
+ });
2596
+ },
2597
+ async deliverCaptureOnly(signal) {
2598
+ if (!dependencies.finalizeCaptureOnly || !attemptContext || !attemptSend) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
2599
+ return dependencies.finalizeCaptureOnly(controller.buildCaptureOnlyUpload(attemptContext, (progress) => {
2600
+ attemptSend?.({
2601
+ type: "UPLOAD_PROGRESS",
2602
+ progress
2603
+ });
2604
+ }), signal);
2605
+ },
2606
+ resourceSink: {
2607
+ ready(stream) {
2608
+ if (disposed) {
2609
+ stopStream(stream);
2610
+ return;
2611
+ }
2612
+ replaceDisplayStream(stream);
2613
+ logDiagnostic("protected-camera-ready", {
2614
+ ...getIdCameraTrackDiagnosticFields(stream.getVideoTracks()[0]),
2615
+ fallbackLevel: cameraFallbackLevel
2616
+ });
2617
+ },
2618
+ autoCommitted: storeCommit,
2619
+ manualCommitted: storeCommit,
2620
+ finalized(_recording, stream) {
2621
+ if (!stream || disposed) {
2622
+ logDiagnostic("attempt-finalized");
2623
+ return;
2624
+ }
2625
+ replaceDisplayStream(stream);
2626
+ logDiagnostic("attempt-finalized");
2627
+ },
2628
+ uploadDone(result) {
2629
+ if (result.kind === "id") {
2630
+ uploadResponse = protectedIdResultToCaptureResponse(result);
2631
+ logDiagnostic("upload-complete", {
2632
+ classification: result.classification,
2633
+ readability: result.readability
2634
+ });
2635
+ }
2636
+ }
2637
+ }
2638
+ };
2639
+ return controller;
2640
+ }
2641
+ function createProtectedIdAttemptActor(ownedController) {
2642
+ return fromCallback(({ sendBack, receive }) => {
2643
+ let controller = ownedController;
2644
+ let attemptReceiver;
2645
+ let cleanup;
2646
+ receive((command) => {
2647
+ if (command.type === "BIND_PROTECTED_ID_CONTROLLER") {
2648
+ controller ??= command.controller;
2649
+ return;
2650
+ }
2651
+ if (command.type === "START_PROTECTED_ID_ATTEMPT") {
2652
+ if (!controller || cleanup) return;
2653
+ cleanup = controller.runAttempt(sendBack, (listener) => {
2654
+ attemptReceiver = listener;
2655
+ });
2656
+ return;
2657
+ }
2658
+ attemptReceiver?.(command);
2659
+ });
2660
+ return () => cleanup?.();
2661
+ });
2662
+ }
2663
+ function createProtectedIdControllerLifetimeActor(ownedController) {
2664
+ return fromCallback(({ receive, sendBack }) => {
2665
+ let active = true;
2666
+ let controller = ownedController;
2667
+ let preAcquireOwner;
2668
+ let preAcquirePending = false;
2669
+ receive((command) => {
2670
+ if (command.type === "BIND_PROTECTED_ID_CONTROLLER") {
2671
+ controller ??= command.controller;
2672
+ return;
2673
+ }
2674
+ const owner = controller;
2675
+ if (!owner || preAcquirePending && preAcquireOwner === owner) return;
2676
+ preAcquireOwner = owner;
2677
+ preAcquirePending = true;
2678
+ owner.preAcquire().then(() => {
2679
+ if (active && controller === owner) sendBack({ type: "PROTECTED_PRE_ACQUIRE_DONE" });
2680
+ if (preAcquireOwner === owner) preAcquirePending = false;
2681
+ }, (error) => {
2682
+ if (active && controller === owner) sendBack({
2683
+ type: "PROTECTED_PRE_ACQUIRE_FAILED",
2684
+ error
2685
+ });
2686
+ if (preAcquireOwner === owner) preAcquirePending = false;
2687
+ });
2688
+ });
2689
+ return () => {
2690
+ active = false;
2691
+ controller?.dispose();
2692
+ };
2693
+ });
2694
+ }
2695
+ function createIdCaptureOnlyDeliveryActor(ownedController) {
2696
+ return fromCallback(({ input, sendBack, receive }) => {
2697
+ if (input.mode !== "captureOnly") return;
2698
+ const abortController = new AbortController();
2699
+ let controller = ownedController;
2700
+ let started = false;
2701
+ const start = () => {
2702
+ if (!controller || started) return;
2703
+ started = true;
2704
+ controller.deliverCaptureOnly(abortController.signal).then((output) => {
2705
+ if (!abortController.signal.aborted) sendBack({
2706
+ type: "CAPTURE_ONLY_DELIVERY_DONE",
2707
+ output
2708
+ });
2709
+ }, (error) => {
2710
+ if (!abortController.signal.aborted) sendBack({
2711
+ type: "CAPTURE_ONLY_DELIVERY_FAILED",
2712
+ error
2713
+ });
2714
+ });
2715
+ };
2716
+ receive((command) => {
2717
+ controller ??= command.controller;
2718
+ start();
2719
+ });
2720
+ start();
2721
+ return () => abortController.abort();
2722
+ });
2723
+ }
2724
+ let protectedIdAttemptSequence = 0;
2725
+ function resolveProtectedIdWorkflow(config) {
2726
+ if (config.modelVersion === "v3" || config.usSmartCapture === true) return 7;
2727
+ if (config.settings?.faceQualityCheckEnabled === true) return 8;
2728
+ if (config.settings?.faceQualityCheckEnabled === false) return 6;
2729
+ if (config.modelVersion === "v1") return 6;
2730
+ return 0;
2731
+ }
2732
+ function resolveProtectedIdTarget(config, currentMode) {
2733
+ const back = currentMode === "back";
2734
+ if (config.isSecondId === true) return back ? PROTECTED_CAPTURE_TARGET.backSecondId : PROTECTED_CAPTURE_TARGET.frontSecondId;
2735
+ return back ? PROTECTED_CAPTURE_TARGET.backId : PROTECTED_CAPTURE_TARGET.frontId;
2736
+ }
2737
+ function createProtectedIdProvider(config) {
2738
+ const workflow = resolveProtectedIdWorkflow(config);
2739
+ return workflow === 0 ? new IdCaptureProvider() : new IdCaptureProvider(workflow);
2740
+ }
2741
+ function createProtectedIdRecorderHooks(input) {
2742
+ let session;
2743
+ const enabled = input.enabled !== false && input.config.enableIdRecording === true;
2744
+ return {
2745
+ mode: enabled ? "finalizeBeforePoc" : "none",
2746
+ async start(stream) {
2747
+ if (!enabled) return;
2748
+ session = await startRecordingSession({
2749
+ config: input.config,
2750
+ stream,
2751
+ type: input.currentMode === "back" ? "backId" : "frontId"
2752
+ });
2753
+ },
2754
+ async prepareForPoc() {
2755
+ if (!session) return {};
2756
+ const currentSession = session;
2757
+ session = void 0;
2758
+ return finalizeRecordingSession(currentSession);
2759
+ },
2760
+ async resumeAfterPoc() {},
2761
+ pauseForBackground() {},
2762
+ async resumeFromBackground() {},
2763
+ dispose() {
2764
+ input.disposeCaptureCallbacks();
2765
+ if (!session) return;
2766
+ disposeRecordingSession(session);
2767
+ session = void 0;
2768
+ }
2769
+ };
2770
+ }
2771
+ function createProtectedIdCallbackSession(input) {
2772
+ const timer = BrowserTimerProvider.getInstance();
2773
+ let optimizingBoundary = null;
2774
+ let countdownTimers = [];
2775
+ let qualityElements;
2776
+ let isCapturing = false;
2777
+ let lastReportedSide = null;
2778
+ let blurSignals = 0;
2779
+ let bestFrameSignals = 0;
2780
+ let bestBlur;
2781
+ const logDiagnostic = (event, fields = {}) => {
2782
+ const attempt = input.controller.getDiagnosticAttempt();
2783
+ if (!attempt) return;
2784
+ logIdCameraDiagnostic(event, {
2785
+ attempt: attempt.attempt,
2786
+ side: attempt.side,
2787
+ elapsedMs: Math.round(getTimestamp() - attempt.startedAt),
2788
+ ...fields
2789
+ });
2790
+ };
2791
+ const clearOptimizingBoundary = () => {
2792
+ if (optimizingBoundary) {
2793
+ timer.clearTimeout(optimizingBoundary);
2794
+ optimizingBoundary = null;
2795
+ }
2796
+ };
2797
+ const clearCountdown = () => {
2798
+ for (const countdownTimer of countdownTimers) timer.clearTimeout(countdownTimer);
2799
+ countdownTimers = [];
2800
+ };
2801
+ const startCountdown = () => {
2802
+ clearCountdown();
2803
+ const stepMs = (input.context.config.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval) / 3;
2804
+ input.send({
2805
+ type: "COUNTER_VALUE_CHANGE",
2806
+ value: 3
2807
+ });
2808
+ countdownTimers = [
2809
+ 2,
2810
+ 1,
2811
+ 0
2812
+ ].map((value, index) => timer.setTimeout(() => input.send({
2813
+ type: "COUNTER_VALUE_CHANGE",
2814
+ value
2815
+ }), stepMs * (index + 1)));
2816
+ };
2817
+ const setDetectionStatus = (status) => {
2818
+ input.send({
2819
+ type: "DETECTION_UPDATE",
2820
+ status
2821
+ });
2822
+ };
2823
+ const notifications = createIdDetectionNotificationController({
2824
+ send: setDetectionStatus,
2825
+ isSuppressed: () => isReportedIdSideWrong(input.context.currentMode, lastReportedSide)
2826
+ });
2827
+ return {
2828
+ callbacks: {
2829
+ onFarAway: () => {
2830
+ if (!isCapturing) notifications.show("farAway");
2831
+ },
2832
+ onDetectionStarted: () => {},
2833
+ onMaskChange: (_show, _mask, _top, orientation) => {
2834
+ input.send({
2835
+ type: "ORIENTATION_CHANGE",
2836
+ orientation
2837
+ });
2838
+ },
2839
+ onBlur: () => {
2840
+ blurSignals += 1;
2841
+ if (!isCapturing) notifications.show("blur");
2842
+ },
2843
+ onGlare: () => {
2844
+ if (!isCapturing) notifications.show("glare");
2845
+ },
2846
+ onIdNotDetected: () => {
2847
+ if (!isCapturing) setDetectionStatus("idNotDetected");
2848
+ },
2849
+ onSwitchToManualCapture: () => {
2850
+ isCapturing = false;
2851
+ notifications.clear();
2852
+ clearOptimizingBoundary();
2853
+ clearCountdown();
2854
+ input.send({ type: input.controller.hasCommit() ? "PROTECTED_RELEASE_STASHED_COMMIT" : "SWITCH_TO_MANUAL_CAPTURE" });
2855
+ },
2856
+ onCapturing: () => {
2857
+ const wasCapturing = isCapturing;
2858
+ notifications.clear();
2859
+ lastReportedSide = null;
2860
+ isCapturing = true;
2861
+ setDetectionStatus("capturing");
2862
+ if (!wasCapturing) logDiagnostic("capture-window", {
2863
+ aggregationMs: input.context.config.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval,
2864
+ blurSignals
2865
+ });
2866
+ if (!wasCapturing && input.context.captureFeedbackMode === "countdown") startCountdown();
2867
+ if (input.context.captureFeedbackMode === "optimizing" && input.context.config.supportsOptimizingStatus === true && input.context.captureWindowMs != null) {
2868
+ clearOptimizingBoundary();
2869
+ optimizingBoundary = timer.setTimeout(() => {
2870
+ optimizingBoundary = null;
2871
+ const presentation = input.controller.getPresentationCanvas();
2872
+ if (presentation) {
2873
+ presentation.release();
2874
+ input.send({ type: "PROTECTED_OPTIMIZING" });
2875
+ }
2876
+ }, input.context.captureWindowMs);
2877
+ }
2878
+ },
2879
+ onBestFrame: (blur, glare, orientation) => {
2880
+ bestFrameSignals += 1;
2881
+ bestBlur = bestBlur === void 0 ? blur : Math.min(bestBlur, blur);
2882
+ qualityElements = {
2883
+ glare,
2884
+ sharpness: blur
2885
+ };
2886
+ if (orientation === "horizontal" || orientation === "vertical") input.send({
2887
+ type: "ORIENTATION_CHANGE",
2888
+ orientation
2889
+ });
2890
+ },
2891
+ onCapture: (canvas) => {
2892
+ isCapturing = false;
2893
+ clearOptimizingBoundary();
2894
+ clearCountdown();
2895
+ logDiagnostic("capture-selected", {
2896
+ blurSignals,
2897
+ bestFrameSignals,
2898
+ bestBlur,
2899
+ selectedBlur: qualityElements?.sharpness,
2900
+ selectedGlare: qualityElements?.glare
2901
+ });
2902
+ input.controller.storeAutoCaptureFrame(canvas);
2903
+ input.send({
2904
+ type: "PROTECTED_AUTO_CAPTURE",
2905
+ qualityElements
2906
+ });
2907
+ },
2908
+ onIdTypeChange: (idType) => {
2909
+ input.send({
2910
+ type: "ID_TYPE_CHANGE",
2911
+ idType
2912
+ });
2913
+ },
2914
+ onIdSideChange: (side) => {
2915
+ if (isCapturing) return;
2916
+ lastReportedSide = side;
2917
+ if (isReportedIdSideWrong(input.context.currentMode, lastReportedSide)) notifications.clear();
2918
+ input.send({
2919
+ type: "ID_SIDE_CHANGE",
2920
+ side
2921
+ });
2922
+ },
2923
+ onCapturingCounterValueChange: () => {},
2924
+ onBarcodeDecoded: (decoded) => {
2925
+ input.send({
2926
+ type: "BARCODE_DECODED",
2927
+ decoded
2928
+ });
2929
+ }
2930
+ },
2931
+ dispose: () => {
2932
+ notifications.clear();
2933
+ clearOptimizingBoundary();
2934
+ clearCountdown();
2935
+ }
2936
+ };
2937
+ }
2938
+ function createProtectedIdAttemptInput(input) {
2939
+ const { context, controller } = input;
2940
+ const provider = controller.getProvider();
2941
+ if (!provider) throw new Error("Protected ID provider is unavailable");
2942
+ const workflow = resolveProtectedIdWorkflow(context.config);
2943
+ const target = resolveProtectedIdTarget(context.config, context.currentMode);
2944
+ const callbackSession = createProtectedIdCallbackSession(input);
2945
+ let wasmUtil;
2946
+ const preAcquired = controller.takePreAcquiredStream();
2947
+ let selectedVideoTrack = preAcquired?.stream.getVideoTracks()[0];
2948
+ const constraints = getCameraReacquisitionConstraints(selectedVideoTrack, preAcquired?.constraints);
2949
+ const captureOnly = "onCapture" in context.config && typeof context.config.onCapture === "function";
2950
+ const diagnosticAttempt = controller.getDiagnosticAttempt();
2951
+ const isAttemptActive = () => !controller.disposed && controller.getProvider() === provider && controller.getDiagnosticAttempt() === diagnosticAttempt;
2952
+ const logSelectedCamera = (stream, fallbackLevel, reacquisitionConstraints, preAcquiredStream) => {
2953
+ if (!diagnosticAttempt) return;
2954
+ logIdCameraDiagnostic("selected-camera", {
2955
+ attempt: diagnosticAttempt.attempt,
2956
+ side: diagnosticAttempt.side,
2957
+ elapsedMs: Math.round(getTimestamp() - diagnosticAttempt.startedAt),
2958
+ ...getIdCameraTrackDiagnosticFields(stream.getVideoTracks()[0]),
2959
+ preAcquired: preAcquiredStream,
2960
+ fallbackLevel,
2961
+ workflow: String(workflow),
2962
+ devicePinned: reacquisitionConstraints?.deviceId !== void 0
2963
+ });
2964
+ };
2965
+ if (preAcquired) logSelectedCamera(preAcquired.stream, preAcquired.fallbackLevel, constraints, true);
2966
+ return {
2967
+ attemptId: `protected-id-${diagnosticAttempt?.attempt ?? ++protectedIdAttemptSequence}`,
2968
+ target,
2969
+ workflow,
2970
+ streamSource: preAcquired ? {
2971
+ kind: "preAcquired",
2972
+ stream: preAcquired.stream,
2973
+ constraints
2974
+ } : {
2975
+ kind: "acquire",
2976
+ async acquire() {
2977
+ await controller.preAcquire();
2978
+ const adopted = controller.takePreAcquiredStream();
2979
+ if (!adopted) throw new Error("Protected ID stream acquisition was cancelled");
2980
+ selectedVideoTrack = adopted.stream.getVideoTracks()[0];
2981
+ const reacquisitionConstraints = getCameraReacquisitionConstraints(selectedVideoTrack, adopted.constraints);
2982
+ logSelectedCamera(adopted.stream, adopted.fallbackLevel, reacquisitionConstraints, false);
2983
+ return {
2984
+ stream: adopted.stream,
2985
+ constraints: reacquisitionConstraints
2986
+ };
2987
+ }
2988
+ },
2989
+ initiallyBackgrounded: !controller.isVisible(),
2990
+ resourceSink: controller.resourceSink,
2991
+ capturePresentationFrame: () => controller.takeAutoCaptureFrame(),
2992
+ cycle: cameraCycleProvider,
2993
+ cycleOwnership: controller.getCycleOwnership(),
2994
+ recorder: createProtectedIdRecorderHooks({
2995
+ config: context.config,
2996
+ currentMode: context.currentMode,
2997
+ disposeCaptureCallbacks: callbackSession.dispose,
2998
+ enabled: !captureOnly
2999
+ }),
3000
+ releaseUnadoptedStream: stopStream,
3001
+ beforeAdopt: captureOnly ? void 0 : async (stream) => {
3002
+ const track = stream.getVideoTracks()[0];
3003
+ if (!track) throw new Error("Protected ID camera has no selected video track");
3004
+ const metadata = await controller.prepareMetadata(context);
3005
+ if (!isAttemptActive()) return;
3006
+ await metadata.checkVirtualCamera(track);
3007
+ },
3008
+ onStartupTiming(timing) {
3009
+ if (!diagnosticAttempt) return;
3010
+ logIdCameraDiagnostic("camera-startup", {
3011
+ attempt: diagnosticAttempt.attempt,
3012
+ side: diagnosticAttempt.side,
3013
+ elapsedMs: Math.round(getTimestamp() - diagnosticAttempt.startedAt),
3014
+ ...timing
3015
+ });
3016
+ },
3017
+ async configureWorkflow() {
3018
+ await provider.initialize({});
3019
+ configureIdCaptureProvider({
3020
+ provider,
3021
+ config: context.config,
3022
+ currentMode: context.currentMode,
3023
+ frameSize: cameraCycleProvider.getCaptureSize(),
3024
+ callbacks: callbackSession.callbacks
3025
+ });
3026
+ },
3027
+ async runPrc() {
3028
+ if (!isAttemptActive()) return;
3029
+ wasmUtil = await (input.getWasmUtil ?? WasmUtilProvider.getInstance)();
3030
+ if (!isAttemptActive()) return;
3031
+ await wasmUtil.prc();
3032
+ },
3033
+ async runPoc() {
3034
+ if (captureOnly) return;
3035
+ if (!isAttemptActive()) return;
3036
+ if (!wasmUtil) wasmUtil = await (input.getWasmUtil ?? WasmUtilProvider.getInstance)();
3037
+ if (!isAttemptActive()) return;
3038
+ await (input.runPocCheck ?? runVirtualCameraCheck)({
3039
+ wasmUtil,
3040
+ source: context.currentMode === "back" ? frameSources.back : frameSources.front,
3041
+ token: (input.getSessionToken ?? getToken)()
3042
+ });
3043
+ if (!isAttemptActive()) return;
3044
+ const metadata = await controller.prepareMetadata(context);
3045
+ if (isAttemptActive()) {
3046
+ try {
3047
+ await metadata.finalizeProtectedCaptureMetadata();
3048
+ } catch {
3049
+ if (isAttemptActive()) trueSightLog("warning", "deepsight", "idScan", "protected capture metadata finalization failed");
3050
+ }
3051
+ if (!isAttemptActive()) return;
3052
+ try {
3053
+ const analysisStatus = metadata.getAnalysisStatus();
3054
+ const motionStatus = metadata.getMotionStatus();
3055
+ const isBack = context.currentMode === "back";
3056
+ addDeviceStats({
3057
+ frontIdStatsAnalysisStatus: isBack ? "" : analysisStatus,
3058
+ backIdStatsAnalysisStatus: isBack ? analysisStatus : "",
3059
+ selfieStatsAnalysisStatus: "",
3060
+ motionStatus
3061
+ });
3062
+ } catch {}
3063
+ try {
3064
+ const logs = metadata.getPipelineState();
3065
+ if (!isAttemptActive()) return;
3066
+ const projection = controller.getProjection();
3067
+ context.dependencies?.trackCaptureAttemptFinished?.({
3068
+ logs,
3069
+ resolution: projection.captureResolution,
3070
+ fallbackLevel: projection.cameraFallbackLevel
3071
+ });
3072
+ } catch {
3073
+ if (isAttemptActive()) trueSightLog("warning", "deepsight", "idScan", "protected capture diagnostics failed");
3074
+ }
3075
+ }
3076
+ }
3077
+ };
3078
+ }
3079
+ function protectedIdResultToCaptureResponse(result) {
3080
+ const regulationType = result.regulationType === "US" || result.regulationType === "Worldwide" || result.regulationType === "Other" || result.regulationType === "US_Illinois" || result.regulationType === "US_Texas" || result.regulationType === "US_California" || result.regulationType === "US_Washington" ? result.regulationType : void 0;
3081
+ return {
3082
+ sharpness: result.sharpness ?? 0,
3083
+ correctSharpness: result.sharpness != null && result.sharpness >= 10,
3084
+ glare: result.glare ?? 0,
3085
+ correctGlare: result.glare != null && result.glare >= 10,
3086
+ horizontalResolution: 0,
3087
+ classification: result.classification === true,
3088
+ readability: result.readability === true,
3089
+ typeOfId: result.typeOfId ?? "",
3090
+ issueYear: 0,
3091
+ issueName: "",
3092
+ sessionStatus: result.sessionStatus ?? "",
3093
+ countryCode: result.countryCode ?? "",
3094
+ skipBackIdCapture: result.skipBackIdCapture,
3095
+ skipGlareFront: result.skipGlareFront,
3096
+ skipGlareBack: result.skipGlareBack,
3097
+ acceptedDocuments: result.acceptedDocuments ? [...result.acceptedDocuments] : void 0,
3098
+ forceFrontIdCapture: result.forceFrontIdCapture,
3099
+ idValidationCompleted: result.idValidationCompleted,
3100
+ showMandatoryConsent: result.showMandatoryConsent,
3101
+ regulationType,
3102
+ failReason: result.failReason,
3103
+ side: result.side,
3104
+ idQualityAttemptApproved: result.idQualityAttemptApproved,
3105
+ isDocumentExpired: result.isDocumentExpired,
3106
+ success: result.success
3107
+ };
3108
+ }
3109
+ function getProtectedIdUploadRequest(context, sessionToken = getToken()) {
3110
+ const target = resolveProtectedIdTarget(context.config, context.currentMode);
3111
+ const front = context.currentMode !== "back";
3112
+ const params = {};
3113
+ if (front && context.config.onlyFront === true) params.onlyFront = true;
3114
+ if (front && context.config.extractIdFace === false) params.extractIdFace = false;
3115
+ const sharpness = context.qualityElements?.sharpness;
3116
+ if (sharpness !== void 0 && sharpness !== 0 && Number.isFinite(sharpness)) params.sharpness = getRealQualityValue(sharpness);
3117
+ const config = {
3118
+ ...sessionToken ? { headers: { "X-Incode-Hardware-Id": sessionToken } } : {},
3119
+ ...Object.keys(params).length > 0 ? { params } : {}
3120
+ };
3121
+ return Object.keys(config).length > 0 ? {
3122
+ target,
3123
+ config
3124
+ } : { target };
3125
+ }
3126
+ //#endregion
3127
+ //#region src/modules/digital-id-redirect/digitalIdRedirectServices.ts
3128
+ function withQuery(path, params) {
3129
+ if (!params) return path;
3130
+ const search = new URLSearchParams();
3131
+ if (params.country) search.set("country", params.country);
3132
+ if (params.region) search.set("region", params.region);
3133
+ const qs = search.toString();
3134
+ return qs ? `${path}?${qs}` : path;
3135
+ }
3136
+ /** GET the provider catalog. Ordering is applied FE-side by the module. */
3137
+ async function listProviders(params, signal) {
3138
+ const url = withQuery(endpoints.digitalIdProviders, params);
3139
+ const res = await api.get(url, { signal });
3140
+ if (!res.ok) throw new Error(`GET ${url} failed: ${res.status} ${res.statusText}`);
3141
+ return res.data ?? [];
3142
+ }
3143
+ /**
3144
+ * Append the onboarding `uuid` to `returnUrl` when absent, so the return leg can
3145
+ * re-attach the (onboarding-scoped) verification. Idempotent (skips when a `uuid`
3146
+ * is already present) and non-fatal (returns the URL unchanged on any failure).
3147
+ * Exported for unit testing.
3148
+ */
3149
+ async function ensureReturnUrlUuid(returnUrl, signal) {
3150
+ try {
3151
+ if (getUrlSearchParam(returnUrl, "uuid")) return returnUrl;
3152
+ const res = await api.get(endpoints.onboardingUrl, { signal });
3153
+ const onboardingUrl = res.ok ? res.data?.url : void 0;
3154
+ const uuid = onboardingUrl ? getUrlSearchParam(onboardingUrl, "uuid") : null;
3155
+ if (!uuid) return returnUrl;
3156
+ return buildUrlWithSearchParams(returnUrl, { uuid });
3157
+ } catch (error) {
3158
+ console.warn("[DigitalIdRedirect] ensureReturnUrlUuid failed, proceeding without uuid:", error);
3159
+ return returnUrl;
3160
+ }
3161
+ }
3162
+ /**
3163
+ * Create a verification. On success the caller must full-page-redirect to
3164
+ * `flowData.launchUrl`. NOTE: this is the billable moment for iDIN/FTN — each
3165
+ * call (incl. every retry) is a new session and a new billing event.
3166
+ *
3167
+ * The `redirectUrl` is first passed through `ensureReturnUrlUuid` so a bare
3168
+ * launch gets a resumable `uuid` before the bank round-trip. This reuses the
3169
+ * current session token (no extra `/omni/start`) and only fetches when the URL
3170
+ * lacks a `uuid`.
3171
+ */
3172
+ async function createVerification(params, signal) {
3173
+ const redirectUrl = await ensureReturnUrlUuid(params.redirectUrl, signal);
3174
+ const res = await api.post(endpoints.digitalIdVerifications, {
3175
+ ...params,
3176
+ redirectUrl
3177
+ }, { signal });
3178
+ if (!res.ok) throw new Error(`POST ${endpoints.digitalIdVerifications} failed: ${res.status} ${res.statusText}`);
3179
+ return res.data;
3180
+ }
3181
+ /**
3182
+ * Submit the provider's return params. The contract wants them wrapped as a
3183
+ * JSON *string* under `walletResponse` (success serialized as "true"/"false").
3184
+ */
3185
+ async function submitWalletResponse(verificationId, params, signal) {
3186
+ const url = `${endpoints.digitalIdVerifications}/${verificationId}/response`;
3187
+ const body = { walletResponse: JSON.stringify({
3188
+ sessionId: params.sessionId,
3189
+ resultsAccessKey: params.resultsAccessKey,
3190
+ success: String(params.success)
3191
+ }) };
3192
+ const res = await api.post(url, body, { signal });
3193
+ if (!res.ok) throw new Error(`POST ${url} failed: ${res.status} ${res.statusText}`);
3194
+ return res.data;
3195
+ }
3196
+ /** Poll fallback — used when the user does not return or results are delayed. */
3197
+ async function getVerificationStatus(verificationId, signal) {
3198
+ const url = `${endpoints.digitalIdVerifications}/${verificationId}/status`;
3199
+ const res = await api.get(url, { signal });
3200
+ if (!res.ok) throw new Error(`GET ${url} failed: ${res.status} ${res.statusText}`);
3201
+ return res.data;
3202
+ }
3203
+ /**
3204
+ * Refresh an expiring `launchUrl` (maps to `flowData.expiresAt`). Do NOT create
3205
+ * a new verification for this — that would be a new billing event.
3206
+ */
3207
+ async function refreshVerification(verificationId, signal) {
3208
+ const url = `${endpoints.digitalIdVerifications}/${verificationId}/refresh`;
3209
+ const res = await api.post(url, {}, { signal });
3210
+ if (!res.ok) throw new Error(`POST ${url} failed: ${res.status} ${res.statusText}`);
3211
+ return res.data;
3212
+ }
3213
+ //#endregion
3214
+ //#region src/modules/digital-id-redirect/digitalIdRedirectStorage.ts
3215
+ /**
3216
+ * Cross-redirect handoff. The full-page redirect out to the provider tears down
3217
+ * the SPA, so the pending verification must be persisted BEFORE navigating and
3218
+ * read back on the return leg. Only the non-sensitive `verificationId` +
3219
+ * `providerId` live here — the session credential is the host's concern
3220
+ * (re-bootstrapped from `url_uuid`) and is NEVER persisted by this module.
3221
+ *
3222
+ * Backed by whatever `IStorageCapability` is injected (default localStorage).
3223
+ * sessionStorage would be preferable (auto-clears on tab close) — swap the
3224
+ * capability when a sessionStorage-backed provider exists. A single fixed key
3225
+ * is used, so a stale record from an abandoned redirect is overwritten by the
3226
+ * next one and is only ever read on the host's return route.
3227
+ *
3228
+ * TODO(follow-up): add a staleness TTL once an injected time capability lands
3229
+ * (Core forbids `Date.now()` directly — `no-browser-api-in-core`).
3230
+ */
3231
+ const PENDING_KEY = "incode.digitalIdRedirect.pending";
3232
+ async function persistPendingRedirect(storage, entry) {
3233
+ await storage.set(PENDING_KEY, {
3234
+ verificationId: entry.verificationId,
3235
+ providerId: entry.providerId
3236
+ });
3237
+ }
3238
+ async function readPendingRedirect(storage) {
3239
+ const pending = await storage.get(PENDING_KEY);
3240
+ return pending?.verificationId && pending?.providerId ? pending : null;
3241
+ }
3242
+ async function clearPendingRedirect(storage) {
3243
+ await storage.remove(PENDING_KEY);
3244
+ }
3245
+ //#endregion
3246
+ //#region src/modules/digital-id-redirect/digitalIdRedirectStateMachine.ts
3247
+ const POLL_INTERVAL_MS = 1500;
3248
+ const MAX_POLL_ATTEMPTS = 20;
3249
+ const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
3250
+ "COMPLETED",
3251
+ "FAILED",
3252
+ "EXPIRED",
3253
+ "CANCELLED"
3254
+ ]);
3255
+ function isTerminal(status) {
3256
+ return TERMINAL_STATUSES.has(status);
3257
+ }
3258
+ const digitalIdRedirectMachine = setup({
3259
+ types: {
3260
+ context: {},
3261
+ events: {},
3262
+ input: {},
3263
+ output: {}
3264
+ },
3265
+ actors: {
3266
+ listProviders: fromPromise(async ({ input, signal }) => listProviders(input ? { country: input.country } : void 0, signal)),
3267
+ createVerification: fromPromise(async ({ input, signal }) => createVerification(input, signal)),
3268
+ submitWalletResponse: fromPromise(async ({ input, signal }) => submitWalletResponse(input.verificationId, input.params, signal)),
3269
+ refreshVerification: fromPromise(async ({ input, signal }) => refreshVerification(input.verificationId, signal)),
3270
+ pollStatus: fromCallback(({ sendBack, input }) => {
3271
+ const timer = BrowserTimerProvider.getInstance();
3272
+ const interval = timer.setInterval(async () => {
3273
+ try {
3274
+ const v = await getVerificationStatus(input.verificationId);
3275
+ if (isTerminal(v.status)) sendBack({
3276
+ type: "POLL_RESULT",
3277
+ verification: v
3278
+ });
3279
+ } catch (error) {
3280
+ console.warn("[DigitalIdRedirect] status poll failed:", error);
3281
+ }
3282
+ }, POLL_INTERVAL_MS);
3283
+ return () => timer.clearInterval(interval);
3284
+ }),
3285
+ persistPending: fromPromise(async ({ input }) => {
3286
+ await persistPendingRedirect(input.storage, {
3287
+ verificationId: input.verificationId,
3288
+ providerId: input.providerId
3289
+ });
3290
+ })
3291
+ },
3292
+ actions: {
3293
+ setProviders: assign(({ event }) => ({ providers: event.output })),
3294
+ setSelectedProvider: assign(({ event }) => ({ selectedProvider: event.providerId })),
3295
+ setVerification: assign(({ event }) => {
3296
+ const v = event.output;
3297
+ return {
3298
+ verificationId: v.verificationId,
3299
+ launchUrl: v.flowData?.launchUrl ?? void 0,
3300
+ expiresAt: v.flowData?.expiresAt ?? void 0,
3301
+ status: v.status,
3302
+ failureReason: v.failureReason ?? void 0,
3303
+ portraitPresent: v.portraitPresent ?? false
3304
+ };
3305
+ }),
3306
+ setVerificationFromEvent: assign(({ event }) => {
3307
+ const v = event.verification;
3308
+ return {
3309
+ status: v.status,
3310
+ failureReason: v.failureReason ?? void 0,
3311
+ portraitPresent: v.portraitPresent ?? false
3312
+ };
3313
+ }),
3314
+ setError: assign(({ event }) => ({ error: String(event.error ?? "Unknown error") })),
3315
+ clearResume: assign(() => ({
3316
+ returnParams: void 0,
3317
+ verificationId: void 0,
3318
+ failureReason: void 0,
3319
+ status: void 0,
3320
+ error: void 0
3321
+ }))
3322
+ },
3323
+ guards: {
3324
+ hasPresetProvider: ({ context }) => Boolean(context.config.provider),
3325
+ autoStartPreset: ({ context }) => context.autoStart && Boolean(context.config.provider),
3326
+ autoStartCatalog: ({ context }) => context.autoStart && !context.config.provider,
3327
+ isResuming: ({ context }) => Boolean(context.returnParams),
3328
+ returnSucceeded: ({ context }) => context.returnParams?.success === true,
3329
+ submittedTerminalOk: ({ event }) => event.output.status === "COMPLETED",
3330
+ submittedFailed: ({ event }) => {
3331
+ const s = event.output.status;
3332
+ return s === "FAILED" || s === "EXPIRED" || s === "CANCELLED";
3333
+ },
3334
+ polledOk: ({ event }) => event.verification.status === "COMPLETED"
3335
+ }
3336
+ }).createMachine({
3337
+ id: "digitalIdRedirect",
3338
+ initial: "idle",
3339
+ context: ({ input }) => ({
3340
+ config: input.config,
3341
+ storage: input.storage,
3342
+ returnUrl: input.config.returnUrl ?? "",
3343
+ providers: [],
3344
+ selectedProvider: input.config.provider,
3345
+ verificationId: input.resume?.verificationId,
3346
+ launchUrl: void 0,
3347
+ expiresAt: void 0,
3348
+ status: void 0,
3349
+ failureReason: void 0,
3350
+ portraitPresent: false,
3351
+ returnParams: input.resume?.returnParams,
3352
+ error: void 0,
3353
+ autoStart: input.autoStart ?? false
3354
+ }),
3355
+ output: ({ context }) => context.status === "COMPLETED" ? {
3356
+ status: "COMPLETED",
3357
+ providerId: context.selectedProvider ?? context.config.provider ?? "",
3358
+ portraitPresent: context.portraitPresent
3359
+ } : { status: "DISMISSED" },
3360
+ states: {
3361
+ idle: {
3362
+ always: [
3363
+ {
3364
+ guard: "isResuming",
3365
+ target: "submitting"
3366
+ },
3367
+ {
3368
+ guard: "autoStartPreset",
3369
+ target: "creating"
3370
+ },
3371
+ {
3372
+ guard: "autoStartCatalog",
3373
+ target: "loadingProviders"
3374
+ }
3375
+ ],
3376
+ on: { LOAD: [{
3377
+ guard: "hasPresetProvider",
3378
+ target: "creating"
3379
+ }, { target: "loadingProviders" }] }
3380
+ },
3381
+ loadingProviders: { invoke: {
3382
+ src: "listProviders",
3383
+ input: ({ context }) => ({ country: context.config.country }),
3384
+ onDone: {
3385
+ target: "choosingProvider",
3386
+ actions: "setProviders"
3387
+ },
3388
+ onError: {
3389
+ target: "error",
3390
+ actions: "setError"
3391
+ }
3392
+ } },
3393
+ choosingProvider: { on: { SELECT_PROVIDER: {
3394
+ target: "creating",
3395
+ actions: "setSelectedProvider"
3396
+ } } },
3397
+ creating: { invoke: {
3398
+ src: "createVerification",
3399
+ input: ({ context }) => ({
3400
+ providerId: context.selectedProvider ?? "",
3401
+ redirectUrl: context.returnUrl
3402
+ }),
3403
+ onDone: {
3404
+ target: "persisting",
3405
+ actions: "setVerification"
3406
+ },
3407
+ onError: {
3408
+ target: "error",
3409
+ actions: "setError"
3410
+ }
3411
+ } },
3412
+ persisting: { invoke: {
3413
+ src: "persistPending",
3414
+ input: ({ context }) => ({
3415
+ storage: context.storage,
3416
+ verificationId: context.verificationId ?? "",
3417
+ providerId: context.selectedProvider ?? ""
3418
+ }),
3419
+ onDone: { target: "readyToRedirect" },
3420
+ onError: { target: "readyToRedirect" }
3421
+ } },
3422
+ readyToRedirect: { on: { REFRESH: { target: "refreshing" } } },
3423
+ refreshing: { invoke: {
3424
+ src: "refreshVerification",
3425
+ input: ({ context }) => ({ verificationId: context.verificationId ?? "" }),
3426
+ onDone: {
3427
+ target: "readyToRedirect",
3428
+ actions: "setVerification"
3429
+ },
3430
+ onError: {
3431
+ target: "error",
3432
+ actions: "setError"
3433
+ }
3434
+ } },
3435
+ submitting: { invoke: {
3436
+ src: "submitWalletResponse",
3437
+ input: ({ context }) => ({
3438
+ verificationId: context.verificationId ?? "",
3439
+ params: context.returnParams
3440
+ }),
3441
+ onDone: [
3442
+ {
3443
+ guard: "submittedTerminalOk",
3444
+ target: "succeeded",
3445
+ actions: "setVerification"
3446
+ },
3447
+ {
3448
+ guard: "submittedFailed",
3449
+ target: "failed",
3450
+ actions: "setVerification"
3451
+ },
3452
+ {
3453
+ target: "polling",
3454
+ actions: "setVerification"
3455
+ }
3456
+ ],
3457
+ onError: {
3458
+ target: "error",
3459
+ actions: "setError"
3460
+ }
3461
+ } },
3462
+ polling: {
3463
+ invoke: {
3464
+ src: "pollStatus",
3465
+ input: ({ context }) => ({ verificationId: context.verificationId ?? "" })
3466
+ },
3467
+ on: { POLL_RESULT: [{
3468
+ guard: "polledOk",
3469
+ target: "succeeded",
3470
+ actions: "setVerificationFromEvent"
3471
+ }, {
3472
+ target: "failed",
3473
+ actions: "setVerificationFromEvent"
3474
+ }] },
3475
+ after: { [POLL_INTERVAL_MS * MAX_POLL_ATTEMPTS]: {
3476
+ target: "failed",
3477
+ actions: assign({ failureReason: () => "SESSION_EXPIRED" })
3478
+ } }
3479
+ },
3480
+ succeeded: { on: { CONTINUE: { target: "finished" } } },
3481
+ finished: { type: "final" },
3482
+ failed: { on: {
3483
+ RETRY: [{
3484
+ guard: "hasPresetProvider",
3485
+ target: "creating"
3486
+ }, { target: "choosingProvider" }],
3487
+ USE_ANOTHER_METHOD: { target: "closed" },
3488
+ CLOSE: { target: "closed" }
3489
+ } },
3490
+ error: { on: {
3491
+ RETRY: [{
3492
+ guard: "hasPresetProvider",
3493
+ target: "creating",
3494
+ actions: "clearResume"
3495
+ }, {
3496
+ target: "choosingProvider",
3497
+ actions: "clearResume"
3498
+ }],
3499
+ USE_ANOTHER_METHOD: { target: "closed" },
3500
+ CLOSE: { target: "closed" }
3501
+ } },
3502
+ closed: { type: "final" }
3503
+ }
3504
+ });
3505
+ //#endregion
3506
+ //#region src/modules/id/idCaptureDependencies.ts
3507
+ function resolveIdCaptureDependencies(dependencies) {
3508
+ return dependencies ?? { storage: new BrowserStorageProvider() };
2555
3509
  }
2556
3510
  //#endregion
2557
3511
  //#region src/modules/id/idCaptureStateMachine.ts
@@ -2580,6 +3534,15 @@ function getIdErrorCodeFromUnknown(error) {
2580
3534
  return Object.values(ID_ERROR_CODES).find((value) => message.includes(value));
2581
3535
  }
2582
3536
  }
3537
+ function asPublicIdEvent(event) {
3538
+ return event;
3539
+ }
3540
+ function isCaptureOnlyConfig(config) {
3541
+ return "onCapture" in config && typeof config.onCapture === "function";
3542
+ }
3543
+ function protectedFailure(event) {
3544
+ return event.type === "PROTECTED_ATTEMPT_FAILED" ? event : null;
3545
+ }
2583
3546
  const idCaptureMachine = setup({
2584
3547
  types: {
2585
3548
  context: {},
@@ -2590,15 +3553,12 @@ const idCaptureMachine = setup({
2590
3553
  actors: {
2591
3554
  checkPermission: checkPermissionActor,
2592
3555
  requestPermission: requestPermissionActor,
2593
- initializeCamera: initializeCameraActor,
2594
- runDetection: runDetectionActor,
2595
- uploadIdImage: uploadIdImageActor,
3556
+ protectedAttempt: createProtectedIdAttemptActor(),
3557
+ protectedControllerLifetime: createProtectedIdControllerLifetimeActor(),
3558
+ captureOnlyDelivery: createIdCaptureOnlyDeliveryActor(),
2596
3559
  uploadProgressTicker: uploadProgressTickerActor,
2597
3560
  processId: processIdActor,
2598
3561
  submitBarcode: submitBarcodeActor,
2599
- startRecording: startRecordingActor,
2600
- initializeDeepsightSession: initializeDeepsightSessionActor,
2601
- checkVirtualCamera: checkVirtualCameraActor,
2602
3562
  manualUploadMachine,
2603
3563
  digitalUploadMachine,
2604
3564
  digitalIdRedirectMachine,
@@ -2611,6 +3571,108 @@ const idCaptureMachine = setup({
2611
3571
  })
2612
3572
  },
2613
3573
  actions: {
3574
+ attachProtectedController: assign(({ self }) => {
3575
+ attachIdProtectedCaptureController(self, createIdProtectedCaptureController());
3576
+ return {};
3577
+ }),
3578
+ startProtectedMetadataPreparation: ({ context, self }) => {
3579
+ if (!isCaptureOnlyConfig(context.config)) getIdProtectedCaptureController(self).startMetadataPreparation(context);
3580
+ },
3581
+ bindProtectedControllerLifetime: sendTo("protectedControllerLifetime", ({ self }) => ({
3582
+ type: "BIND_PROTECTED_ID_CONTROLLER",
3583
+ controller: getIdProtectedCaptureController(self)
3584
+ })),
3585
+ preAcquireProtectedCamera: sendTo("protectedControllerLifetime", { type: "PRE_ACQUIRE" }),
3586
+ initializeProtectedProvider: assign(({ context, self }) => {
3587
+ getIdProtectedCaptureController(self).beginAttempt(context, (event) => self.send(event));
3588
+ return {
3589
+ stream: void 0,
3590
+ provider: void 0,
3591
+ frameCapturer: void 0
3592
+ };
3593
+ }),
3594
+ bindProtectedAttempt: sendTo("protectedAttempt", ({ self }) => ({
3595
+ type: "BIND_PROTECTED_ID_CONTROLLER",
3596
+ controller: getIdProtectedCaptureController(self)
3597
+ })),
3598
+ startProtectedAttempt: sendTo("protectedAttempt", { type: "START_PROTECTED_ID_ATTEMPT" }),
3599
+ setProtectedAttemptReady: assign(({ self, event }) => {
3600
+ if (event.type !== "PROTECTED_ATTEMPT_READY_SIGNAL") return {};
3601
+ const projection = getIdProtectedCaptureController(self).getProjection();
3602
+ return {
3603
+ stream: void 0,
3604
+ frameCapturer: void 0,
3605
+ captureResolution: projection.captureResolution,
3606
+ cameraFallbackLevel: projection.cameraFallbackLevel,
3607
+ usingBackCamera: projection.usingBackCamera
3608
+ };
3609
+ }),
3610
+ requestProtectedAutoCommit: enqueueActions(({ self, enqueue }) => {
3611
+ if (!getIdProtectedCaptureController(self).hasAutoCaptureFrame()) {
3612
+ enqueue(raise({ type: "PROTECTED_PRESENTATION_FAILED" }));
3613
+ return;
3614
+ }
3615
+ enqueue(sendTo("protectedAttempt", { type: "AUTO_CAPTURE" }));
3616
+ }),
3617
+ requestProtectedManualCommit: sendTo("protectedAttempt", { type: "MANUAL_CAPTURE" }),
3618
+ finalizeProtectedAttempt: sendTo("protectedAttempt", { type: "FINALIZE" }),
3619
+ requestProtectedUpload: enqueueActions(({ context, enqueue }) => {
3620
+ if (isCaptureOnlyConfig(context.config)) return;
3621
+ enqueue(sendTo("protectedAttempt", {
3622
+ type: "UPLOAD_PROTECTED",
3623
+ request: getProtectedIdUploadRequest(context)
3624
+ }));
3625
+ }),
3626
+ bindCaptureOnlyDelivery: sendTo("captureOnlyDelivery", ({ self }) => ({
3627
+ type: "BIND_ID_CAPTURE_ONLY_DELIVERY",
3628
+ controller: getIdProtectedCaptureController(self)
3629
+ })),
3630
+ releaseProtectedForClose: sendTo("protectedAttempt", {
3631
+ type: "RELEASE",
3632
+ reason: "close"
3633
+ }),
3634
+ releaseProtectedForRetry: sendTo("protectedAttempt", {
3635
+ type: "RELEASE",
3636
+ reason: "retry"
3637
+ }),
3638
+ forwardProtectedBackground: sendTo("protectedAttempt", { type: "BACKGROUND" }),
3639
+ forwardProtectedForeground: sendTo("protectedAttempt", { type: "FOREGROUND" }),
3640
+ storeProtectedAutoQuality: assign(({ event }) => event.type === "PROTECTED_AUTO_CAPTURE" ? { qualityElements: event.qualityElements } : {}),
3641
+ storeProtectedCommit: assign(({ context, event }) => {
3642
+ if (event.type !== "PROTECTED_AUTO_COMMITTED_SIGNAL" && event.type !== "PROTECTED_MANUAL_COMMITTED_SIGNAL") return {};
3643
+ const side = context.currentMode === "back" ? "back" : "front";
3644
+ return { capturedImages: {
3645
+ ...context.capturedImages,
3646
+ [side]: { imageBase64: "" }
3647
+ } };
3648
+ }),
3649
+ storeProtectedPreview: assign(({ context, self, event }) => {
3650
+ if (event.type !== "PROTECTED_PREVIEW_READY") return {};
3651
+ return { previewImageUrl: getIdProtectedCaptureController(self).takeCommitPreviewImageUrl() ?? context.previewImageUrl };
3652
+ }),
3653
+ storeProtectedCandidate: assign(({ context, self }) => {
3654
+ const controller = getIdProtectedCaptureController(self);
3655
+ const candidateCanvas = controller.getPresentationCanvas();
3656
+ return candidateCanvas ? getCandidateFrameValues(context, {
3657
+ type: "DETECTION_OPTIMIZING",
3658
+ candidateCanvas
3659
+ }, controller.getProvider()) : {};
3660
+ }),
3661
+ storeProtectedUploadResponse: assign(({ context, self, event }) => {
3662
+ if (event.type !== "PROTECTED_UPLOAD_DONE_SIGNAL") return {};
3663
+ const uploadResponse = getIdProtectedCaptureController(self).takeUploadResponse();
3664
+ if (!uploadResponse) return {};
3665
+ return {
3666
+ uploadResponse,
3667
+ uploadProgress: 100,
3668
+ skipProcessId: uploadResponse.idValidationCompleted === true || context.skipProcessId
3669
+ };
3670
+ }),
3671
+ setProtectedFailure: assign(({ context, event }) => ({ error: protectedFailure(event)?.failure.code ?? context.error })),
3672
+ setProtectedUploadFailure: assign(({ context }) => ({
3673
+ uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
3674
+ attemptsRemaining: context.attemptsRemaining - 1
3675
+ })),
2614
3676
  stopMediaStream: assign(({ context }) => {
2615
3677
  performStopMediaStream(context);
2616
3678
  return {
@@ -2618,12 +3680,12 @@ const idCaptureMachine = setup({
2618
3680
  frameCapturer: void 0
2619
3681
  };
2620
3682
  }),
3683
+ clearProtectedAttempt: ({ self }) => {
3684
+ getIdProtectedCaptureController(self).clearAttempt();
3685
+ },
2621
3686
  disposeProvider: ({ context }) => {
2622
3687
  performDisposeProvider(context);
2623
3688
  },
2624
- cleanupDeepsight: ({ context }) => {
2625
- performCleanupDeepsight(context);
2626
- },
2627
3689
  flagIdManualReview: () => {
2628
3690
  flagIdManualReview();
2629
3691
  },
@@ -2707,19 +3769,9 @@ const idCaptureMachine = setup({
2707
3769
  attemptsRemaining: context.config.captureAttempts
2708
3770
  };
2709
3771
  }),
2710
- setStreamAndCapturer: assign({
2711
- stream: ({ event }) => getStreamFromEvent(event),
2712
- provider: ({ event }) => getProviderFromEvent(event),
2713
- frameCapturer: ({ event }) => getFrameCapturerFromEvent(event),
2714
- captureResolution: ({ event }) => getCaptureResolutionFromEvent(event),
2715
- cameraFallbackLevel: ({ event }) => getCameraFallbackLevelFromEvent(event),
2716
- usingBackCamera: ({ event }) => getUsingBackCameraFromEvent(event)
2717
- }),
2718
3772
  setPermissionResultAndStream: assign({
2719
3773
  permissionResult: ({ context, event }) => getPermissionResultFromEvent(event) ?? context.permissionResult,
2720
- stream: ({ context, event }) => getStreamFromEvent(event) ?? context.stream,
2721
- cameraFallbackLevel: ({ context, event }) => getCameraFallbackLevelFromEvent(event) ?? context.cameraFallbackLevel,
2722
- usingBackCamera: ({ context, event }) => getUsingBackCameraFromEvent(event) ?? context.usingBackCamera
3774
+ cameraFallbackLevel: ({ context, event }) => getCameraFallbackLevelFromEvent(asPublicIdEvent(event)) ?? context.cameraFallbackLevel
2723
3775
  }),
2724
3776
  trackTutorialId: () => {},
2725
3777
  trackContinue: () => {},
@@ -2728,21 +3780,14 @@ const idCaptureMachine = setup({
2728
3780
  performReportCaptureOptimizingAssignment(context);
2729
3781
  },
2730
3782
  preloadRecordingProvider: ({ context }) => {
3783
+ if (isCaptureOnlyConfig(context.config)) return;
2731
3784
  context.config;
2732
3785
  },
2733
3786
  resetContext: assign(({ context }) => getResetContextValues(context)),
2734
- resetDetection: ({ context }) => {
2735
- performResetDetection(context);
2736
- },
3787
+ resetDetection: () => {},
2737
3788
  captureImage: assign({ qualityElements: ({ event }) => {
2738
3789
  if ("qualityElements" in event) return event.qualityElements;
2739
3790
  } }),
2740
- storeCapturedCanvasInProvider: ({ context, event }) => {
2741
- storeCapturedCanvasInProviderLogic(context, event);
2742
- },
2743
- captureLatestFrame: ({ context }) => {
2744
- context.frameCapturer?.getLatestCanvas();
2745
- },
2746
3791
  clearUploadFailure: assign(({ context }) => getClearUploadFailureValues(context)),
2747
3792
  clearStreamForRetry: assign(({ context }) => {
2748
3793
  performPrepareRecordingForCameraRetry(context);
@@ -2762,33 +3807,19 @@ const idCaptureMachine = setup({
2762
3807
  stopMediaRecording: ({ context }) => {
2763
3808
  performStopMediaRecording(context);
2764
3809
  },
2765
- pauseRecordingForAttempt: ({ context }) => {
2766
- performPauseRecordingForAttempt(context);
2767
- },
2768
- discardFailedRecordingSession: assign(({ context }) => {
2769
- performStopMediaRecording(context);
2770
- return { recordingSession: void 0 };
2771
- }),
2772
3810
  clearRecordingSession: assign({ recordingSession: () => void 0 }),
2773
3811
  setSelectedDocument: assign({ selectedDocumentType: ({ event }) => {
2774
3812
  if ("documentType" in event) return event.documentType;
2775
3813
  } }),
2776
- setCurrentMode: assign({ currentMode: ({ event, context }) => getCurrentModeFromEvent(context, event) }),
2777
- storeCapturedImage: assign(({ context, event }) => getStoreCapturedImageValues(context, event)),
2778
- setCandidateFrame: assign(({ context, event }) => getCandidateFrameValues(context, event)),
3814
+ setCurrentMode: assign({ currentMode: ({ event, context }) => getCurrentModeFromEvent(context, asPublicIdEvent(event)) }),
3815
+ storeCapturedImage: assign(({ context, event }) => getStoreCapturedImageValues(context, asPublicIdEvent(event))),
3816
+ setCandidateFrame: assign(({ context, event }) => getCandidateFrameValues(context, asPublicIdEvent(event))),
2779
3817
  /**
2780
3818
  * IDC-05: parks WASM's commit while the optimizing phase spends the rest of
2781
3819
  * its dwell. The frame travels in context only for that stretch —
2782
3820
  * `releaseStashedCommit` re-raises it and `getClearOptimizingValues` drops it
2783
3821
  * on every other exit from the phase.
2784
3822
  */
2785
- stashCommittedFrame: assign({ stashedCommit: ({ event }) => {
2786
- const commit = event;
2787
- return {
2788
- canvas: commit.canvas,
2789
- qualityElements: commit.qualityElements
2790
- };
2791
- } }),
2792
3823
  /**
2793
3824
  * IDC-05: hands a parked commit back to the machine as the very event that
2794
3825
  * would have carried it, so `capturing`'s entry actions stay event-driven.
@@ -2797,15 +3828,16 @@ const idCaptureMachine = setup({
2797
3828
  * entering event has no canvas — a timer transition into `capturing` would
2798
3829
  * stage a live frame for upload instead of the one WASM committed.
2799
3830
  */
2800
- releaseStashedCommit: enqueueActions(({ context, enqueue }) => {
2801
- const stashed = context.stashedCommit;
2802
- if (!stashed) return;
3831
+ releaseStashedCommit: enqueueActions(({ self, enqueue }) => {
3832
+ if (!getIdProtectedCaptureController(self).hasCommit()) return;
2803
3833
  enqueue(raise({
2804
- type: "DETECTION_SUCCESS",
2805
- canvas: stashed.canvas,
2806
- qualityElements: stashed.qualityElements
3834
+ type: "PROTECTED_AUTO_COMMITTED_SIGNAL",
3835
+ attemptId: "stashed-protected-id-commit"
2807
3836
  }));
2808
3837
  }),
3838
+ releaseStashedCommitIfPresent: enqueueActions(({ self, enqueue }) => {
3839
+ if (getIdProtectedCaptureController(self).hasCommit()) enqueue(raise({ type: "PROTECTED_RELEASE_STASHED_COMMIT" }));
3840
+ }),
2809
3841
  /**
2810
3842
  * IDC-05: releases everything the optimizing phase allocated — the parked
2811
3843
  * commit AND the frozen still's blob URL. Bound to `optimizing.exit`, so it
@@ -2822,7 +3854,7 @@ const idCaptureMachine = setup({
2822
3854
  * resolves to nothing (see `getStoreCapturedImageValues`).
2823
3855
  */
2824
3856
  clearOptimizingPhase: assign(({ context }) => getClearOptimizingValues(context)),
2825
- storeBarcodeResponse: assign(({ context, event }) => getBarcodeResponseValues(context, event)),
3857
+ storeBarcodeResponse: assign(({ context, event }) => getBarcodeResponseValues(context, asPublicIdEvent(event))),
2826
3858
  storeDecodedBarcode: assign({ decodedBarcode: ({ event }) => event.decoded }),
2827
3859
  setBarcodeFastPathAttempted: assign({ barcodeFastPathAttempted: true }),
2828
3860
  markBarcodeFastPathCaptured: assign({
@@ -2830,7 +3862,7 @@ const idCaptureMachine = setup({
2830
3862
  previewImageUrl: void 0
2831
3863
  }),
2832
3864
  clearBarcodeFastPathCaptured: assign({ barcodeFastPathCaptured: false }),
2833
- setDetectionStatus: assign({ detectionStatus: ({ event, context }) => getDetectionStatusFromUpdate(context, event) }),
3865
+ setDetectionStatus: assign({ detectionStatus: ({ event, context }) => getDetectionStatusFromUpdate(context, asPublicIdEvent(event)) }),
2834
3866
  setCounterValue: assign({ counterValue: ({ event }) => {
2835
3867
  if ("value" in event) return event.value;
2836
3868
  return 0;
@@ -2928,20 +3960,26 @@ const idCaptureMachine = setup({
2928
3960
  clearExpiredFromUpload: assign({ expiredFromUpload: () => false })
2929
3961
  },
2930
3962
  guards: {
3963
+ isCaptureOnlyDelivery: ({ context }) => isCaptureOnlyConfig(context.config),
3964
+ isProtectedUploadFailure: ({ event }) => protectedFailure(event)?.failure.stage === "upload",
3965
+ isProtectedManualRefusal: ({ event }) => protectedFailure(event)?.failure.stage === "manualCommit",
2931
3966
  hasShowTutorial: hasShowTutorialGuard,
2932
3967
  hasAgeAssurance: hasAgeAssuranceGuard,
2933
3968
  hasShowDocumentChooser: hasShowDocumentChooserGuard,
2934
- isPermissionGranted: isPermissionGrantedGuard,
2935
- isReadyForCapture: isReadyForCaptureGuard,
2936
- needsMotionPrimeOnly: needsMotionPrimeOnlyGuard,
2937
- isPermissionDeniedError: isPermissionDeniedErrorGuard,
3969
+ isPermissionGranted: ({ event }) => isPermissionGrantedGuard({ event: asPublicIdEvent(event) }),
3970
+ isReadyForCapture: ({ context, event }) => isReadyForCaptureGuard({
3971
+ context,
3972
+ event: asPublicIdEvent(event)
3973
+ }),
3974
+ needsMotionPrimeOnly: ({ context, event }) => needsMotionPrimeOnlyGuard({
3975
+ context,
3976
+ event: asPublicIdEvent(event)
3977
+ }),
3978
+ isPermissionDeniedError: ({ event }) => isPermissionDeniedErrorGuard({ event: asPublicIdEvent(event) }),
2938
3979
  isPermissionBlocked: isPermissionBlockedGuard,
2939
- hasStream: hasStreamGuard,
2940
- hasInitializedCamera: hasInitializedCameraGuard,
2941
3980
  hasAttemptsRemaining: hasAttemptsRemainingGuard,
2942
3981
  hasCapturedImage: hasCapturedImageGuard,
2943
- hasCandidateFrame: hasCandidateFrameGuard,
2944
- hasStashedCommit: hasStashedCommitGuard,
3982
+ hasCandidateFrame: ({ event }) => hasCandidateFrameGuard({ event: asPublicIdEvent(event) }),
2945
3983
  hasUploadValidationError: hasUploadValidationErrorGuard,
2946
3984
  isFrontMode: isFrontModeGuard,
2947
3985
  shouldContinueToBack: shouldContinueToBackGuard,
@@ -2954,10 +3992,10 @@ const idCaptureMachine = setup({
2954
3992
  needsDeepsightInit: needsDeepsightInitGuard,
2955
3993
  hasOnlyManualUploadAvailable: hasOnlyManualUploadAvailableGuard,
2956
3994
  hasNoDocumentTypesAvailable: hasNoDocumentTypesAvailableGuard,
2957
- isManualIdUploadSelected: isManualIdUploadSelectedGuard,
2958
- isDigitalIdUploadSelected: isDigitalIdUploadSelectedGuard,
2959
- isDigitalIdRedirectSelected: isDigitalIdRedirectSelectedGuard,
2960
- isDigitalIdSelected: isDigitalIdSelectedGuard,
3995
+ isManualIdUploadSelected: ({ event }) => isManualIdUploadSelectedGuard({ event: asPublicIdEvent(event) }),
3996
+ isDigitalIdUploadSelected: ({ event }) => isDigitalIdUploadSelectedGuard({ event: asPublicIdEvent(event) }),
3997
+ isDigitalIdRedirectSelected: ({ event }) => isDigitalIdRedirectSelectedGuard({ event: asPublicIdEvent(event) }),
3998
+ isDigitalIdSelected: ({ event }) => isDigitalIdSelectedGuard({ event: asPublicIdEvent(event) }),
2961
3999
  digitalIdRouteIsWallet: digitalIdRouteIsWalletGuard,
2962
4000
  digitalIdRouteIsTrinsic: digitalIdRouteIsTrinsicGuard,
2963
4001
  isUploadExpired: isUploadExpiredGuard,
@@ -2967,7 +4005,10 @@ const idCaptureMachine = setup({
2967
4005
  wasExpiredFromUpload: wasExpiredFromUploadGuard,
2968
4006
  shouldSkipProcessId: shouldSkipProcessIdGuard,
2969
4007
  shouldSubmitBarcode: shouldSubmitBarcodeGuard,
2970
- canRunBarcodeFastPath: canRunBarcodeFastPathGuard,
4008
+ canRunBarcodeFastPath: ({ context, event }) => canRunBarcodeFastPathGuard({
4009
+ context,
4010
+ event: asPublicIdEvent(event)
4011
+ }),
2971
4012
  hasManualCaptureTriggered: hasManualCaptureTriggeredGuard,
2972
4013
  isProcessingExpiredExhausted: isProcessingExpiredExhaustedGuard,
2973
4014
  shouldProcessAfterExhaustion: shouldProcessAfterExhaustionGuard,
@@ -3016,7 +4057,13 @@ const idCaptureMachine = setup({
3016
4057
  id: "idCapture",
3017
4058
  initial: "idle",
3018
4059
  output: ({ context }) => computeIdCaptureOutput(context),
3019
- context: ({ input }) => {
4060
+ entry: ["attachProtectedController", "bindProtectedControllerLifetime"],
4061
+ invoke: {
4062
+ id: "protectedControllerLifetime",
4063
+ src: "protectedControllerLifetime"
4064
+ },
4065
+ context: ({ input, self }) => {
4066
+ attachIdProtectedCaptureController(self, createIdProtectedCaptureController());
3020
4067
  const currentMode = input.config.onlyBack ? "back" : !input.config.enableId && input.config.enablePassport ? "passport" : input.config.usSmartCapture ? "back" : "front";
3021
4068
  return {
3022
4069
  config: input.config,
@@ -3069,7 +4116,7 @@ const idCaptureMachine = setup({
3069
4116
  manualCaptureTriggered: false,
3070
4117
  deepsightService: void 0,
3071
4118
  analyticsProvider: input.analyticsProvider,
3072
- dependencies: input.dependencies,
4119
+ dependencies: resolveIdCaptureDependencies(input.dependencies),
3073
4120
  disableIpify: getDisableIpify(),
3074
4121
  deepsightInitAttempted: false,
3075
4122
  availableDocumentTypes: getAvailableDocumentTypes(input.config, { credentialsAvailable: input.dependencies?.credentials?.isAvailable() ?? false }),
@@ -3147,16 +4194,19 @@ const idCaptureMachine = setup({
3147
4194
  actions: ["setSelectedDocument", "setCurrentMode"]
3148
4195
  }
3149
4196
  ] } },
3150
- ageVerification: { on: { NEXT_STEP: [{
3151
- target: "tutorial",
3152
- guard: "hasShowTutorial",
3153
- actions: "trackContinue"
3154
- }, {
3155
- target: "loading",
3156
- actions: "trackContinue"
3157
- }] } },
4197
+ ageVerification: {
4198
+ entry: "startProtectedMetadataPreparation",
4199
+ on: { NEXT_STEP: [{
4200
+ target: "tutorial",
4201
+ guard: "hasShowTutorial",
4202
+ actions: "trackContinue"
4203
+ }, {
4204
+ target: "loading",
4205
+ actions: "trackContinue"
4206
+ }] }
4207
+ },
3158
4208
  loading: {
3159
- entry: "preloadRecordingProvider",
4209
+ entry: ["startProtectedMetadataPreparation", "preloadRecordingProvider"],
3160
4210
  type: "parallel",
3161
4211
  states: {
3162
4212
  permissionCheck: {
@@ -3178,30 +4228,8 @@ const idCaptureMachine = setup({
3178
4228
  }
3179
4229
  },
3180
4230
  deepsightInit: {
3181
- initial: "initializing",
3182
- states: {
3183
- initializing: { invoke: {
3184
- id: "initDeepsightLoading",
3185
- src: "initializeDeepsightSession",
3186
- input: ({ context }) => ({
3187
- ds: context.config.ds,
3188
- dependencies: context.dependencies,
3189
- disableIpify: context.disableIpify
3190
- }),
3191
- onDone: {
3192
- target: "done",
3193
- actions: assign({
3194
- deepsightService: ({ event }) => event.output,
3195
- deepsightInitAttempted: () => true
3196
- })
3197
- },
3198
- onError: {
3199
- target: "done",
3200
- actions: [assign({ deepsightInitAttempted: () => true }), () => console.warn("Deepsight initialization failed")]
3201
- }
3202
- } },
3203
- done: { type: "final" }
3204
- }
4231
+ initial: "done",
4232
+ states: { done: { type: "final" } }
3205
4233
  }
3206
4234
  },
3207
4235
  onDone: [{
@@ -3211,7 +4239,11 @@ const idCaptureMachine = setup({
3211
4239
  },
3212
4240
  tutorial: {
3213
4241
  initial: "checkingPermission",
3214
- entry: ["trackTutorialId", "preloadRecordingProvider"],
4242
+ entry: [
4243
+ "startProtectedMetadataPreparation",
4244
+ "trackTutorialId",
4245
+ "preloadRecordingProvider"
4246
+ ],
3215
4247
  states: {
3216
4248
  checkingPermission: {
3217
4249
  invoke: {
@@ -3241,50 +4273,22 @@ const idCaptureMachine = setup({
3241
4273
  type: "parallel",
3242
4274
  states: {
3243
4275
  cameraInit: {
3244
- initial: "initializingDeepsight",
4276
+ initial: "initializingStream",
3245
4277
  states: {
3246
- initializingDeepsight: { invoke: {
3247
- id: "tutorialInitDeepsight",
3248
- src: "initializeDeepsightSession",
3249
- input: ({ context }) => ({
3250
- ds: context.config.ds,
3251
- dependencies: context.dependencies,
3252
- disableIpify: context.disableIpify
3253
- }),
3254
- onDone: {
3255
- target: "initializingStream",
3256
- actions: assign({
3257
- deepsightService: ({ event }) => event.output,
3258
- deepsightInitAttempted: () => true
3259
- })
3260
- },
3261
- onError: {
3262
- target: "initializingStream",
3263
- actions: [assign({ deepsightInitAttempted: () => true }), () => console.warn("Deepsight initialization failed in tutorial")]
4278
+ initializingStream: {
4279
+ entry: "preAcquireProtectedCamera",
4280
+ on: {
4281
+ PROTECTED_PRE_ACQUIRE_DONE: { target: "ready" },
4282
+ PROTECTED_PRE_ACQUIRE_FAILED: [{
4283
+ target: "#idCapture.tutorial.ready",
4284
+ guard: "isPermissionDeniedError",
4285
+ actions: assign({ permissionResult: () => "denied" })
4286
+ }, {
4287
+ target: "#idCapture.tutorial.ready",
4288
+ actions: assign({ error: ({ event }) => String(event.error) })
4289
+ }]
3264
4290
  }
3265
- } },
3266
- initializingStream: { invoke: {
3267
- id: "tutorialInitCamera",
3268
- src: "initializeCamera",
3269
- input: ({ context }) => ({
3270
- config: context.config,
3271
- deepsightService: context.deepsightService
3272
- }),
3273
- onDone: {
3274
- target: "ready",
3275
- actions: "setStreamAndCapturer"
3276
- },
3277
- onError: [{
3278
- target: "#idCapture.tutorial.ready",
3279
- guard: "isPermissionDeniedError",
3280
- actions: assign({ permissionResult: () => {
3281
- return "denied";
3282
- } })
3283
- }, {
3284
- target: "#idCapture.tutorial.ready",
3285
- actions: assign({ error: ({ event }) => String(event.error) })
3286
- }]
3287
- } },
4291
+ },
3288
4292
  ready: { type: "final" }
3289
4293
  }
3290
4294
  },
@@ -3416,9 +4420,10 @@ const idCaptureMachine = setup({
3416
4420
  requesting: { invoke: {
3417
4421
  id: "requestPermission",
3418
4422
  src: "requestPermission",
3419
- input: ({ context }) => ({
4423
+ input: ({ context, self }) => ({
3420
4424
  permissionResult: context.permissionResult === "refresh" ? void 0 : context.permissionResult,
3421
- requestMotionPermission: context.config.ds === true
4425
+ requestMotionPermission: context.config.ds === true,
4426
+ acceptStream: (value) => getIdProtectedCaptureController(self).storePreAcquiredStream(value)
3422
4427
  }),
3423
4428
  onDone: [
3424
4429
  {
@@ -3443,63 +4448,64 @@ const idCaptureMachine = setup({
3443
4448
  },
3444
4449
  capture: {
3445
4450
  initial: "checkingStream",
3446
- entry: ["trackCameraId", "preloadRecordingProvider"],
3447
- exit: ["stopMediaRecording", "clearRecordingSession"],
3448
- on: { SET_FRAME_RECT: { actions: "setFrameRect" } },
3449
- states: {
3450
- checkingStream: { always: [
4451
+ entry: [
4452
+ "trackCameraId",
4453
+ "preloadRecordingProvider",
4454
+ "initializeProtectedProvider",
4455
+ "bindProtectedAttempt",
4456
+ "startProtectedAttempt"
4457
+ ],
4458
+ exit: [
4459
+ "stopMediaRecording",
4460
+ "clearRecordingSession",
4461
+ "clearProtectedAttempt"
4462
+ ],
4463
+ invoke: {
4464
+ id: "protectedAttempt",
4465
+ src: "protectedAttempt"
4466
+ },
4467
+ on: {
4468
+ QUIT: { target: ".releasingForClose" },
4469
+ BACKGROUND: { actions: "forwardProtectedBackground" },
4470
+ FOREGROUND: { actions: "forwardProtectedForeground" },
4471
+ PROTECTED_ATTEMPT_READY_SIGNAL: {
4472
+ target: ".choosingDetectionStrategy",
4473
+ actions: "setProtectedAttemptReady"
4474
+ },
4475
+ PROTECTED_ATTEMPT_FAILED: [
4476
+ { guard: "isProtectedManualRefusal" },
3451
4477
  {
3452
- target: "initializingDeepsight",
3453
- guard: "needsDeepsightInit"
4478
+ target: ".uploadError",
4479
+ guard: "isProtectedUploadFailure",
4480
+ actions: "setProtectedUploadFailure"
3454
4481
  },
3455
4482
  {
3456
- target: "choosingDetectionStrategy",
3457
- guard: "hasInitializedCamera"
3458
- },
3459
- { target: "initializing" }
3460
- ] },
3461
- initializingDeepsight: { invoke: {
3462
- id: "initDeepsightCapture",
3463
- src: "initializeDeepsightSession",
3464
- input: ({ context }) => ({
3465
- ds: context.config.ds,
3466
- dependencies: context.dependencies,
3467
- disableIpify: context.disableIpify
3468
- }),
3469
- onDone: {
3470
- target: "checkingStream",
3471
- actions: assign({
3472
- deepsightService: ({ event }) => event.output,
3473
- deepsightInitAttempted: () => true
3474
- })
3475
- },
3476
- onError: {
3477
- target: "checkingStream",
3478
- actions: [assign({ deepsightInitAttempted: () => true }), () => console.warn("Deepsight initialization failed in capture")]
3479
- }
3480
- } },
3481
- initializing: { invoke: {
3482
- id: "initializeCamera",
3483
- src: "initializeCamera",
3484
- input: ({ context }) => ({
3485
- config: context.config,
3486
- deepsightService: context.deepsightService,
3487
- stream: context.stream,
3488
- cameraFallbackLevel: context.cameraFallbackLevel
3489
- }),
3490
- onDone: {
3491
- target: "choosingDetectionStrategy",
3492
- actions: "setStreamAndCapturer"
3493
- },
3494
- onError: [{
3495
- target: "#idCapture.permissions",
3496
- guard: "isPermissionDeniedError",
3497
- actions: assign({ permissionResult: () => "denied" })
3498
- }, {
3499
4483
  target: "#idCapture.error",
3500
- actions: assign({ error: ({ event }) => String(event.error) })
3501
- }]
3502
- } },
4484
+ actions: "setProtectedFailure"
4485
+ }
4486
+ ],
4487
+ PROTECTED_PRESENTATION_FAILED: {
4488
+ target: ".uploadError",
4489
+ actions: "setProtectedUploadFailure"
4490
+ },
4491
+ PROTECTED_PREVIEW_READY: { actions: "storeProtectedPreview" },
4492
+ SET_FRAME_RECT: { actions: "setFrameRect" }
4493
+ },
4494
+ states: {
4495
+ releasingForClose: {
4496
+ entry: "releaseProtectedForClose",
4497
+ always: "#idCapture.closed"
4498
+ },
4499
+ releasingForRetry: {
4500
+ entry: "releaseProtectedForRetry",
4501
+ always: {
4502
+ target: "#idCapture.capture",
4503
+ reenter: true
4504
+ }
4505
+ },
4506
+ checkingStream: { always: { target: "initializing" } },
4507
+ initializingDeepsight: { always: { target: "initializing" } },
4508
+ initializing: {},
3503
4509
  choosingDetectionStrategy: {
3504
4510
  entry: ["reportCaptureOptimizingAssignment"],
3505
4511
  always: [{
@@ -3514,43 +4520,19 @@ const idCaptureMachine = setup({
3514
4520
  actions: assign({ detectionStatus: () => "manualCapture" })
3515
4521
  }],
3516
4522
  entry: [assign({ detectionStatus: () => "detecting" })],
3517
- invoke: [{
3518
- id: "startRecording",
3519
- src: "startRecording",
3520
- input: ({ context }) => ({
3521
- config: context.config,
3522
- stream: context.stream,
3523
- existing: context.recordingSession,
3524
- currentMode: context.currentMode
3525
- }),
3526
- onDone: { actions: assign({ recordingSession: ({ context, event }) => {
3527
- return event.output ?? context.recordingSession;
3528
- } }) },
3529
- onError: { actions: "discardFailedRecordingSession" }
3530
- }, {
3531
- id: "runDetection",
3532
- src: "runDetection",
3533
- input: ({ context }) => ({
3534
- frameCapturer: context.frameCapturer,
3535
- provider: context.provider,
3536
- config: context.config,
3537
- currentMode: context.currentMode,
3538
- detectionArea: context.detectionArea ?? context.config.detectionArea
3539
- })
3540
- }],
3541
4523
  on: {
3542
4524
  DETECTION_UPDATE: { actions: "setDetectionStatus" },
3543
- DETECTION_RESET_READY: { actions: assign({ resetDetection: ({ event }) => event.reset }) },
3544
- DETECTION_SUCCESS: {
4525
+ PROTECTED_AUTO_CAPTURE: { actions: ["storeProtectedAutoQuality", "requestProtectedAutoCommit"] },
4526
+ PROTECTED_AUTO_COMMITTED_SIGNAL: {
3545
4527
  target: "capturing",
3546
- actions: assign({ qualityElements: ({ event }) => event.qualityElements })
4528
+ actions: "storeProtectedCommit"
3547
4529
  },
3548
4530
  BARCODE_DECODED: [{
3549
- target: "capturingBarcode",
4531
+ target: "submittingBarcodePreCapture",
3550
4532
  guard: "canRunBarcodeFastPath",
3551
4533
  actions: ["storeDecodedBarcode", "setBarcodeFastPathAttempted"]
3552
4534
  }, { actions: "storeDecodedBarcode" }],
3553
- MANUAL_CAPTURE: { target: "capturingManual" },
4535
+ MANUAL_CAPTURE: {},
3554
4536
  SWITCH_TO_MANUAL_CAPTURE: {
3555
4537
  target: "manualCaptureWaiting",
3556
4538
  actions: assign({
@@ -3583,40 +4565,15 @@ const idCaptureMachine = setup({
3583
4565
  guard: "hasManualCaptureTriggered",
3584
4566
  actions: assign({ detectionStatus: () => "manualCapture" })
3585
4567
  }],
3586
- invoke: [{
3587
- id: "startRecording",
3588
- src: "startRecording",
3589
- input: ({ context }) => ({
3590
- config: context.config,
3591
- stream: context.stream,
3592
- existing: context.recordingSession,
3593
- currentMode: context.currentMode
3594
- }),
3595
- onDone: { actions: assign({ recordingSession: ({ context, event }) => {
3596
- return event.output ?? context.recordingSession;
3597
- } }) },
3598
- onError: { actions: "discardFailedRecordingSession" }
3599
- }, {
3600
- id: "runDetection",
3601
- src: "runDetection",
3602
- input: ({ context }) => ({
3603
- frameCapturer: context.frameCapturer,
3604
- provider: context.provider,
3605
- config: context.config,
3606
- currentMode: context.currentMode,
3607
- detectionArea: context.detectionArea ?? context.config.detectionArea,
3608
- optimizingAfterMs: context.captureWindowMs
3609
- })
3610
- }],
3611
4568
  on: {
3612
- DETECTION_SUCCESS: {
4569
+ PROTECTED_AUTO_CAPTURE: { actions: ["storeProtectedAutoQuality", "requestProtectedAutoCommit"] },
4570
+ PROTECTED_AUTO_COMMITTED_SIGNAL: {
3613
4571
  target: "capturing",
3614
- actions: assign({ qualityElements: ({ event }) => event.qualityElements })
4572
+ actions: "storeProtectedCommit"
3615
4573
  },
3616
- DETECTION_RESET_READY: { actions: assign({ resetDetection: ({ event }) => event.reset }) },
3617
4574
  ID_TYPE_CHANGE: { actions: "setIdType" },
3618
4575
  ORIENTATION_CHANGE: { actions: "setOrientation" },
3619
- MANUAL_CAPTURE: { target: "#idCapture.capture.capturingManual" },
4576
+ MANUAL_CAPTURE: {},
3620
4577
  SWITCH_TO_MANUAL_CAPTURE: {
3621
4578
  target: "#idCapture.capture.manualCaptureWaiting",
3622
4579
  actions: assign({
@@ -3631,34 +4588,28 @@ const idCaptureMachine = setup({
3631
4588
  DETECTION_UPDATE: { actions: "setDetectionStatus" },
3632
4589
  ID_SIDE_CHANGE: { actions: assign({ detectionStatus: ({ context, event }) => getDetectionStatusFromSideChange(context, event.side) }) },
3633
4590
  BARCODE_DECODED: [{
3634
- target: "#idCapture.capture.capturingBarcode",
4591
+ target: "#idCapture.capture.submittingBarcodePreCapture",
3635
4592
  guard: "canRunBarcodeFastPath",
3636
4593
  actions: ["storeDecodedBarcode", "setBarcodeFastPathAttempted"]
3637
4594
  }, { actions: "storeDecodedBarcode" }],
3638
- DETECTION_OPTIMIZING: {
4595
+ PROTECTED_OPTIMIZING: {
3639
4596
  target: "optimizing",
3640
- guard: "hasCandidateFrame",
3641
- actions: "setCandidateFrame"
4597
+ actions: "storeProtectedCandidate"
3642
4598
  }
3643
4599
  } },
3644
4600
  optimizing: {
3645
4601
  exit: "clearOptimizingPhase",
3646
4602
  on: {
3647
4603
  BARCODE_DECODED: { actions: "storeDecodedBarcode" },
3648
- SWITCH_TO_MANUAL_CAPTURE: {
3649
- guard: "hasStashedCommit",
3650
- target: "#idCapture.capture.detectingWithOptimization.optimizing.released"
3651
- },
3652
- MANUAL_CAPTURE: {
3653
- guard: "hasStashedCommit",
3654
- target: "#idCapture.capture.detectingWithOptimization.optimizing.released"
3655
- }
4604
+ SWITCH_TO_MANUAL_CAPTURE: { actions: "releaseStashedCommitIfPresent" },
4605
+ MANUAL_CAPTURE: { actions: "releaseStashedCommitIfPresent" },
4606
+ PROTECTED_RELEASE_STASHED_COMMIT: { target: "#idCapture.capture.detectingWithOptimization.optimizing.released" }
3656
4607
  },
3657
4608
  initial: "holding",
3658
4609
  states: {
3659
4610
  holding: {
3660
4611
  after: { optimizingDwell: { target: "released" } },
3661
- on: { DETECTION_SUCCESS: { actions: "stashCommittedFrame" } }
4612
+ on: { PROTECTED_AUTO_COMMITTED_SIGNAL: {} }
3662
4613
  },
3663
4614
  released: {
3664
4615
  entry: "releaseStashedCommit",
@@ -3671,34 +4622,23 @@ const idCaptureMachine = setup({
3671
4622
  }
3672
4623
  }
3673
4624
  },
3674
- manualCaptureWaiting: {
3675
- invoke: {
3676
- id: "startRecording",
3677
- src: "startRecording",
3678
- input: ({ context }) => ({
3679
- config: context.config,
3680
- stream: context.stream,
3681
- existing: context.recordingSession,
3682
- currentMode: context.currentMode
3683
- }),
3684
- onDone: { actions: assign({ recordingSession: ({ context, event }) => {
3685
- return event.output ?? context.recordingSession;
3686
- } }) },
3687
- onError: { actions: "discardFailedRecordingSession" }
3688
- },
3689
- on: { MANUAL_CAPTURE: { target: "capturingManual" } }
3690
- },
4625
+ manualCaptureWaiting: { on: {
4626
+ MANUAL_CAPTURE: { actions: "requestProtectedManualCommit" },
4627
+ PROTECTED_MANUAL_COMMITTED_SIGNAL: {
4628
+ target: "capturingManual",
4629
+ actions: "storeProtectedCommit"
4630
+ }
4631
+ } },
3691
4632
  capturing: {
3692
- entry: [
3693
- "captureImage",
3694
- "storeCapturedCanvasInProvider",
3695
- "storeCapturedImage",
3696
- "pauseRecordingForAttempt"
3697
- ],
3698
- always: [
4633
+ entry: "finalizeProtectedAttempt",
4634
+ on: { PROTECTED_ATTEMPT_FINALIZED_SIGNAL: [
3699
4635
  {
3700
4636
  target: "submittingBarcode",
3701
- guard: and(["hasCapturedImage", "shouldSubmitBarcode"])
4637
+ guard: "shouldSubmitBarcode"
4638
+ },
4639
+ {
4640
+ target: "uploading",
4641
+ guard: not("isCaptureOnlyDelivery")
3702
4642
  },
3703
4643
  {
3704
4644
  target: "uploading",
@@ -3706,12 +4646,9 @@ const idCaptureMachine = setup({
3706
4646
  },
3707
4647
  {
3708
4648
  target: "uploadError",
3709
- actions: assign(({ context }) => ({
3710
- uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
3711
- attemptsRemaining: context.attemptsRemaining - 1
3712
- }))
4649
+ actions: "setProtectedUploadFailure"
3713
4650
  }
3714
- ]
4651
+ ] }
3715
4652
  },
3716
4653
  submittingBarcode: { invoke: {
3717
4654
  id: "submitBarcode",
@@ -3727,15 +4664,7 @@ const idCaptureMachine = setup({
3727
4664
  },
3728
4665
  onError: { target: "uploading" }
3729
4666
  } },
3730
- capturingBarcode: {
3731
- entry: [
3732
- "captureImage",
3733
- "storeCapturedCanvasInProvider",
3734
- "storeCapturedImage",
3735
- "pauseRecordingForAttempt"
3736
- ],
3737
- always: [{ target: "submittingBarcodePreCapture" }]
3738
- },
4667
+ capturingBarcode: { always: [{ target: "submittingBarcodePreCapture" }] },
3739
4668
  submittingBarcodePreCapture: { invoke: {
3740
4669
  id: "submitBarcode",
3741
4670
  src: "submitBarcode",
@@ -3752,13 +4681,8 @@ const idCaptureMachine = setup({
3752
4681
  onError: { target: "choosingDetectionStrategy" }
3753
4682
  } },
3754
4683
  capturingManual: {
3755
- entry: [
3756
- "captureLatestFrame",
3757
- "storeCapturedCanvasInProvider",
3758
- "storeCapturedImage",
3759
- "pauseRecordingForAttempt"
3760
- ],
3761
- always: [
4684
+ entry: "finalizeProtectedAttempt",
4685
+ on: { PROTECTED_ATTEMPT_FINALIZED_SIGNAL: [
3762
4686
  {
3763
4687
  target: "optimizingHold",
3764
4688
  guard: and([
@@ -3767,6 +4691,10 @@ const idCaptureMachine = setup({
3767
4691
  "supportsOptimizingStatus"
3768
4692
  ])
3769
4693
  },
4694
+ {
4695
+ target: "uploading",
4696
+ guard: not("isCaptureOnlyDelivery")
4697
+ },
3770
4698
  {
3771
4699
  target: "uploading",
3772
4700
  guard: "hasCapturedImage"
@@ -3778,44 +4706,35 @@ const idCaptureMachine = setup({
3778
4706
  attemptsRemaining: context.attemptsRemaining - 1
3779
4707
  }))
3780
4708
  }
3781
- ]
4709
+ ] }
3782
4710
  },
3783
4711
  optimizingHold: { after: { optimizingDwell: { target: "uploading" } } },
3784
4712
  uploading: {
3785
- entry: assign({ uploadProgress: () => 0 }),
4713
+ entry: [
4714
+ assign({ uploadProgress: () => 1 }),
4715
+ "requestProtectedUpload",
4716
+ "bindCaptureOnlyDelivery"
4717
+ ],
3786
4718
  invoke: [{
3787
4719
  id: "uploadProgressTicker",
3788
4720
  src: "uploadProgressTicker"
3789
4721
  }, {
3790
- id: "uploadIdImage",
3791
- src: "uploadIdImage",
3792
- input: ({ context, self }) => {
3793
- const canvas = context.provider?.getOriginalCapturedCanvas();
3794
- if (!canvas) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
3795
- const type = context.currentMode === "back" ? "back" : "front";
3796
- return {
3797
- canvas,
3798
- type,
3799
- qualityElements: context.qualityElements,
3800
- onProgress: (progress) => {
3801
- self.send({
3802
- type: "UPLOAD_PROGRESS",
3803
- progress
3804
- });
3805
- },
3806
- deepsightService: context.deepsightService,
3807
- stream: context.stream,
3808
- isSecondId: context.config.isSecondId ?? false,
3809
- onlyFront: type === "front" && (context.config.onlyFront ?? false),
3810
- ageAssurance: context.config.ageAssurance,
3811
- dependencies: context.dependencies,
3812
- captureResolution: context.captureResolution,
3813
- cameraFallbackLevel: context.cameraFallbackLevel,
3814
- extractIdFace: context.config.extractIdFace,
3815
- croppedCanvas: context.provider?.getCapturedCanvas() ?? null
3816
- };
3817
- },
3818
- onDone: {
4722
+ id: "captureOnlyDelivery",
4723
+ src: "captureOnlyDelivery",
4724
+ input: ({ context }) => {
4725
+ if (!isCaptureOnlyConfig(context.config)) return { mode: "protected" };
4726
+ return { mode: "captureOnly" };
4727
+ }
4728
+ }],
4729
+ after: { [UPLOAD_TIMEOUT_MS]: {
4730
+ target: "uploadError",
4731
+ actions: assign(({ context }) => ({
4732
+ uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
4733
+ attemptsRemaining: context.attemptsRemaining - 1
4734
+ }))
4735
+ } },
4736
+ on: {
4737
+ CAPTURE_ONLY_DELIVERY_DONE: {
3819
4738
  target: "uploadCompleted",
3820
4739
  actions: [assign({
3821
4740
  uploadResponse: ({ event }) => event.output,
@@ -3823,22 +4742,17 @@ const idCaptureMachine = setup({
3823
4742
  skipProcessId: ({ context, event }) => event.output.idValidationCompleted === true || context.skipProcessId
3824
4743
  }), "storeCapturedImage"]
3825
4744
  },
3826
- onError: {
4745
+ CAPTURE_ONLY_DELIVERY_FAILED: {
3827
4746
  target: "uploadError",
3828
4747
  actions: assign(({ context, event }) => ({
3829
4748
  uploadError: getIdErrorCodeFromUnknown(event.error) ?? ID_ERROR_CODES.UPLOAD_ERROR,
3830
4749
  attemptsRemaining: context.attemptsRemaining - 1
3831
4750
  }))
3832
- }
3833
- }],
3834
- after: { [UPLOAD_TIMEOUT_MS]: {
3835
- target: "uploadError",
3836
- actions: assign(({ context }) => ({
3837
- uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
3838
- attemptsRemaining: context.attemptsRemaining - 1
3839
- }))
3840
- } },
3841
- on: {
4751
+ },
4752
+ PROTECTED_UPLOAD_DONE_SIGNAL: {
4753
+ target: "uploadCompleted",
4754
+ actions: "storeProtectedUploadResponse"
4755
+ },
3842
4756
  UPLOAD_PROGRESS: { actions: assign({ uploadProgress: ({ context, event }) => Math.max(context.uploadProgress ?? 0, Math.round(event.progress / 100 * 80)) }) },
3843
4757
  UPLOAD_PROGRESS_TICK: { actions: assign({ uploadProgress: ({ context }) => Math.min(95, (context.uploadProgress ?? 0) + 1) }) }
3844
4758
  }
@@ -3881,7 +4795,7 @@ const idCaptureMachine = setup({
3881
4795
  ] },
3882
4796
  uploadError: { on: { CONTINUE_FROM_ERROR: [
3883
4797
  {
3884
- target: "checkingStream",
4798
+ target: "releasingForRetry",
3885
4799
  guard: "hasAttemptsRemaining",
3886
4800
  actions: [
3887
4801
  "resetDetection",
@@ -3905,7 +4819,6 @@ const idCaptureMachine = setup({
3905
4819
  }
3906
4820
  ] } },
3907
4821
  success: {
3908
- entry: ["stopMediaRecording", "clearRecordingSession"],
3909
4822
  exit: "clearBarcodeFastPathCaptured",
3910
4823
  on: { NEXT_STEP: [
3911
4824
  {
@@ -3946,28 +4859,8 @@ const idCaptureMachine = setup({
3946
4859
  cameraInit: {
3947
4860
  initial: "checking",
3948
4861
  states: {
3949
- checking: { always: [{
3950
- target: "ready",
3951
- guard: "hasStream"
3952
- }, { target: "initializingStream" }] },
3953
- initializingStream: { invoke: {
3954
- id: "frontFinishedInitCamera",
3955
- src: "initializeCamera",
3956
- input: ({ context }) => ({
3957
- config: context.config,
3958
- deepsightService: context.deepsightService
3959
- }),
3960
- onDone: {
3961
- target: "ready",
3962
- actions: "setStreamAndCapturer"
3963
- },
3964
- onError: {
3965
- target: "ready",
3966
- actions: () => {
3967
- console.warn("Camera initialization failed during flip transition");
3968
- }
3969
- }
3970
- } },
4862
+ checking: { always: "ready" },
4863
+ initializingStream: { always: "ready" },
3971
4864
  ready: { type: "final" }
3972
4865
  }
3973
4866
  },
@@ -3983,10 +4876,6 @@ const idCaptureMachine = setup({
3983
4876
  }
3984
4877
  },
3985
4878
  onDone: [{
3986
- target: "#idCapture.capture.choosingDetectionStrategy",
3987
- guard: "hasStream",
3988
- actions: "clearFlipPreview"
3989
- }, {
3990
4879
  target: "#idCapture.capture",
3991
4880
  actions: "clearFlipPreview"
3992
4881
  }]
@@ -3998,28 +4887,8 @@ const idCaptureMachine = setup({
3998
4887
  cameraInit: {
3999
4888
  initial: "checking",
4000
4889
  states: {
4001
- checking: { always: [{
4002
- target: "ready",
4003
- guard: "hasStream"
4004
- }, { target: "initializingStream" }] },
4005
- initializingStream: { invoke: {
4006
- id: "backFinishedInitCamera",
4007
- src: "initializeCamera",
4008
- input: ({ context }) => ({
4009
- config: context.config,
4010
- deepsightService: context.deepsightService
4011
- }),
4012
- onDone: {
4013
- target: "ready",
4014
- actions: "setStreamAndCapturer"
4015
- },
4016
- onError: {
4017
- target: "ready",
4018
- actions: () => {
4019
- console.warn("Camera initialization failed during flip transition");
4020
- }
4021
- }
4022
- } },
4890
+ checking: { always: "ready" },
4891
+ initializingStream: { always: "ready" },
4023
4892
  ready: { type: "final" }
4024
4893
  }
4025
4894
  },
@@ -4035,10 +4904,6 @@ const idCaptureMachine = setup({
4035
4904
  }
4036
4905
  },
4037
4906
  onDone: [{
4038
- target: "#idCapture.capture.choosingDetectionStrategy",
4039
- guard: "hasStream",
4040
- actions: "clearFlipPreview"
4041
- }, {
4042
4907
  target: "#idCapture.capture",
4043
4908
  actions: "clearFlipPreview"
4044
4909
  }]
@@ -4140,25 +5005,16 @@ const idCaptureMachine = setup({
4140
5005
  entry: [
4141
5006
  "stopMediaRecording",
4142
5007
  "stopMediaStream",
4143
- "disposeProvider",
4144
- "cleanupDeepsight"
5008
+ "disposeProvider"
4145
5009
  ],
4146
5010
  type: "final"
4147
5011
  },
4148
5012
  closed: {
4149
- entry: [
4150
- "stopMediaStream",
4151
- "disposeProvider",
4152
- "cleanupDeepsight"
4153
- ],
5013
+ entry: ["stopMediaStream", "disposeProvider"],
4154
5014
  type: "final"
4155
5015
  },
4156
5016
  error: {
4157
- entry: [
4158
- "stopMediaStream",
4159
- "disposeProvider",
4160
- "cleanupDeepsight"
4161
- ],
5017
+ entry: ["stopMediaStream", "disposeProvider"],
4162
5018
  on: { RESET: {
4163
5019
  target: "idle",
4164
5020
  actions: "resetContext"
@@ -4242,7 +5098,7 @@ const idCaptureMachine = setup({
4242
5098
  invoke: {
4243
5099
  id: "readPendingDigitalIdRedirect",
4244
5100
  src: "readPendingDigitalIdRedirect",
4245
- input: ({ context }) => ({ storage: context.dependencies?.storage ?? new BrowserStorageProvider() }),
5101
+ input: ({ context }) => ({ storage: resolveIdCaptureDependencies(context.dependencies).storage }),
4246
5102
  onDone: [
4247
5103
  {
4248
5104
  guard: "hasPendingDigitalIdRedirect",
@@ -4276,7 +5132,7 @@ const idCaptureMachine = setup({
4276
5132
  returnUrl: context.config.digitalIdReturnUrl,
4277
5133
  country: method?.country
4278
5134
  },
4279
- storage: context.dependencies?.storage ?? new BrowserStorageProvider(),
5135
+ storage: resolveIdCaptureDependencies(context.dependencies).storage,
4280
5136
  autoStart: true,
4281
5137
  resume: resuming ? {
4282
5138
  verificationId: context.digitalIdResumeVerificationId,
@@ -4362,5 +5218,21 @@ const idCaptureMachine = setup({
4362
5218
  }
4363
5219
  }
4364
5220
  });
5221
+ /**
5222
+ * Composes the protected children around one controller without placing that
5223
+ * authority in any actor input or public machine context.
5224
+ */
5225
+ function provideIdProtectedCaptureController(machine, controller) {
5226
+ return machine.provide({
5227
+ actors: {
5228
+ protectedAttempt: createProtectedIdAttemptActor(controller),
5229
+ protectedControllerLifetime: createProtectedIdControllerLifetimeActor(controller)
5230
+ },
5231
+ actions: { attachProtectedController: assign(({ self }) => {
5232
+ attachIdProtectedCaptureController(self, controller);
5233
+ return {};
5234
+ }) }
5235
+ });
5236
+ }
4365
5237
  //#endregion
4366
- export { isRedirectFlowType as a, MANUAL_UPLOAD_MAX_FILE_SIZE_BYTES as c, DIGITAL_ID_REGISTRY as i, MANUAL_UPLOAD_MAX_RETRIES as l, getManualUploadTabs as n, resolveDigitalIdImplementation as o, DIGITAL_ID_DOCUMENT_TYPES as r, MANUAL_UPLOAD_EXHAUSTION_FORWARD_MS as s, idCaptureMachine as t, resolveWalletPlatform as u };
5238
+ export { createIdProtectedCaptureController as a, DIGITAL_ID_DOCUMENT_TYPES as c, resolveDigitalIdImplementation as d, MANUAL_UPLOAD_EXHAUSTION_FORWARD_MS as f, resolveWalletPlatform as h, createIdCaptureOnlyDeliveryActor as i, DIGITAL_ID_REGISTRY as l, MANUAL_UPLOAD_MAX_RETRIES as m, provideIdProtectedCaptureController as n, findIdProtectedCaptureController as o, MANUAL_UPLOAD_MAX_FILE_SIZE_BYTES as p, attachIdProtectedCaptureController as r, getManualUploadTabs as s, idCaptureMachine as t, isRedirectFlowType as u };