@incodetech/core 2.3.2 → 2.4.0-rc.1

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 (271) hide show
  1. package/THIRD_PARTY_NOTICES.txt +1243 -3
  2. package/dist/Actor-DpTj5PFb.d.ts +1 -0
  3. package/dist/{BaseWasmProvider-Dyi6dKp1.d.ts → BaseWasmProvider-Bq3OHo7P.d.ts} +2 -2
  4. package/dist/BrowserDiagnosticsEnvelopeProvider-BQca6KPG.esm.js +43 -0
  5. package/dist/{FaceDetectionProvider-DtmtCKa9.esm.js → FaceDetectionProvider-BIImDZrc.esm.js} +125 -10
  6. package/dist/HttpRequestError-Bh0pNGSC.esm.js +35 -0
  7. package/dist/{IIdCaptureCapability-RNiFhvXR.d.ts → IIdCaptureCapability-CGcEt2Cw.d.ts} +2 -2
  8. package/dist/{IWasmUtilCapability-CNWRl7jr.d.ts → IWasmUtilCapability-GF2n7sfm.d.ts} +28 -5
  9. package/dist/{Manager-CQQ99QOI.d.ts → Manager-C9T5amUN.d.ts} +7 -1
  10. package/dist/{MotionSensorProvider-DyhbM4Si.esm.js → MotionSensorProvider-DYxh8TjR.esm.js} +1 -1
  11. package/dist/{ekycStateMachine-UeJRO8_p.esm.js → PhoneValidationProvider-SZiOS0jB.esm.js} +232 -827
  12. package/dist/{StreamCanvasCapture-_8gdW5Lk.d.ts → StreamCanvasCapture-7JRf-2ck.d.ts} +2 -2
  13. package/dist/{safeStorage-BdU9_m2E.esm.js → WasmUtilProvider-DtJuBdt-.esm.js} +111 -49
  14. package/dist/{addressSearch-DyHGgeX8.esm.js → addressSearch-DPjIKJrX.esm.js} +142 -4
  15. package/dist/{advanceableErrors-CpF2cBy6.esm.js → advanceableErrors-q_pfU3Cn.esm.js} +5 -4
  16. package/dist/ae-signature.d.ts +2 -2
  17. package/dist/ae-signature.esm.js +1 -1
  18. package/dist/antifraud.d.ts +1 -1
  19. package/dist/antifraud.esm.js +2 -2
  20. package/dist/{antifraudManager-DNkw66oW.esm.js → antifraudManager-CaweCYnY.esm.js} +3 -3
  21. package/dist/{antifraudManager-BY36bS12.d.ts → antifraudManager-DbGCvlJs.d.ts} +2 -2
  22. package/dist/{antifraudStateMachine-DBeq47D2.esm.js → antifraudStateMachine-DRl0UVyA.esm.js} +3 -4
  23. package/dist/{endpoints-B-3Q-LUw.esm.js → api-Cz90EHyu.esm.js} +151 -1
  24. package/dist/{apiError-CmGEmaGU.esm.js → apiError-D5u6HIDG.esm.js} +2 -1
  25. package/dist/authentication.d.ts +3 -3
  26. package/dist/authentication.esm.js +2 -2
  27. package/dist/{authenticationManager-CboRCl_N.d.ts → authenticationManager-BIZC55Bi.d.ts} +2 -8
  28. package/dist/{authenticationManager-BFjSjtQ_.esm.js → authenticationManager-CB3YW6EK.esm.js} +6 -6
  29. package/dist/{authenticationStateMachine-Dfo34ZiI.esm.js → authenticationStateMachine-BJJhLmdH.esm.js} +5 -6
  30. package/dist/avatar.d.ts +1 -1
  31. package/dist/{backCameraStream-B0-YgP_y.esm.js → backCameraStream-CMvXj4X4.esm.js} +1 -1
  32. package/dist/camera.esm.js +1 -1
  33. package/dist/certificate-issuance.d.ts +1 -1
  34. package/dist/certificate-issuance.esm.js +2 -2
  35. package/dist/{certificateIssuanceManager-C3-CI0_s.esm.js → certificateIssuanceManager-BfeMv2PU.esm.js} +3 -3
  36. package/dist/{certificateIssuanceManager-DCgEidHz.d.ts → certificateIssuanceManager-Dr8A4Lgr.d.ts} +2 -3
  37. package/dist/{certificateIssuanceStateMachine-CxqS_nD-.esm.js → certificateIssuanceStateMachine-Frgjbjw8.esm.js} +4 -5
  38. package/dist/consent.d.ts +1 -1
  39. package/dist/consent.esm.js +2 -2
  40. package/dist/{consentManager-B6E1XJDw.esm.js → consentManager-CvEu8h_W.esm.js} +3 -3
  41. package/dist/{consentManager-2h_w6wFc.d.ts → consentManager-Cv_0QKLD.d.ts} +7 -8
  42. package/dist/{consentStateMachine-DTSfdGiW.esm.js → consentStateMachine-DJTgN_-1.esm.js} +3 -4
  43. package/dist/{createCameraCycleOwnership-DnwTl1ad.esm.js → createCameraCycleOwnership-Dj_LHUvN.esm.js} +71 -66
  44. package/dist/{createFaceAvatar-BlcTYgJv.d.ts → createFaceAvatar-lYX0VTYs.d.ts} +1 -1
  45. package/dist/cross-document-data-match.d.ts +1 -1
  46. package/dist/cross-document-data-match.esm.js +1 -1
  47. package/dist/{crossDocumentDataMatchManager-9XhFVZ5S.esm.js → crossDocumentDataMatchManager-Cu141CE8.esm.js} +4 -5
  48. package/dist/{crossDocumentDataMatchManager-Dq8Z3SOg.d.ts → crossDocumentDataMatchManager-yCWCOzcU.d.ts} +2 -2
  49. package/dist/curp-validation.d.ts +1 -1
  50. package/dist/curp-validation.esm.js +2 -2
  51. package/dist/{curpValidationManager-DbIMYFcJ.esm.js → curpValidationManager-B-z9q5V3.esm.js} +15 -22
  52. package/dist/{curpValidationManager-DNuZn9fn.d.ts → curpValidationManager-C9sqoh9n.d.ts} +3 -3
  53. package/dist/{curpValidationStateMachine-g5kMfuLj.esm.js → curpValidationStateMachine-vDEFLMPw.esm.js} +4 -5
  54. package/dist/custom-fields.d.ts +1 -1
  55. package/dist/custom-fields.esm.js +1 -1
  56. package/dist/custom-watchlist.d.ts +1 -1
  57. package/dist/custom-watchlist.esm.js +2 -2
  58. package/dist/{customFieldsManager-8-W2KY4n.d.ts → customFieldsManager-CF-cO8ap.d.ts} +2 -2
  59. package/dist/{customFieldsManager-CzWZTkW-.esm.js → customFieldsManager-D1ex2cFM.esm.js} +4 -5
  60. package/dist/{customWatchlistManager-DWTED5Ur.esm.js → customWatchlistManager-CTI-SGk0.esm.js} +3 -3
  61. package/dist/{customWatchlistManager-PBWHr_oM.d.ts → customWatchlistManager-DolUswyR.d.ts} +2 -2
  62. package/dist/{customWatchlistStateMachine-DOQou6TW.esm.js → customWatchlistStateMachine-hmRd0f2I.esm.js} +3 -4
  63. package/dist/deepsightLoader-DZMdYSl-.esm.js +55 -0
  64. package/dist/{deepsightLoader-v-dLejS-.esm.js → deepsightService-tGP53cSS.esm.js} +51 -56
  65. package/dist/device.esm.js +1 -1
  66. package/dist/document-capture.d.ts +2 -2
  67. package/dist/document-capture.esm.js +2 -2
  68. package/dist/{documentCaptureManager-BRPxqYD2.esm.js → documentCaptureManager-DUJwpKC5.esm.js} +17 -18
  69. package/dist/{documentCaptureManager-lEtCM320.d.ts → documentCaptureManager-DkyDtm3Y.d.ts} +11 -34
  70. package/dist/{documentCaptureStateMachine-D312sSIu.esm.js → documentCaptureStateMachine-B8i7GnLh.esm.js} +55 -59
  71. package/dist/dynamic-forms.d.ts +2 -2
  72. package/dist/dynamic-forms.esm.js +2 -2
  73. package/dist/{dynamicFormsManager-C-9Jhnd3.esm.js → dynamicFormsManager-Bxmv_9PB.esm.js} +3 -3
  74. package/dist/{dynamicFormsManager-D5R_uxLQ.d.ts → dynamicFormsManager-C0dHs1ty.d.ts} +3 -3
  75. package/dist/{dynamicFormsStateMachine-DGI_F2PP.esm.js → dynamicFormsStateMachine-CoOWTzyM.esm.js} +4 -5
  76. package/dist/ekyb.d.ts +5 -5
  77. package/dist/ekyb.esm.js +3 -3
  78. package/dist/{ekybManager-DeoMcZEz.esm.js → ekybManager-BBkdPxUY.esm.js} +23 -27
  79. package/dist/{ekybManager-BwRuGAwt.d.ts → ekybManager-BaZ_7CEu.d.ts} +4 -4
  80. package/dist/{ekybStateMachine-D1IJ1Z72.esm.js → ekybStateMachine-n4Iz9iLc.esm.js} +338 -14
  81. package/dist/ekyc.d.ts +2 -2
  82. package/dist/ekyc.esm.js +3 -3
  83. package/dist/{ekycManager-BX2d46FY.esm.js → ekycManager-BO-Lui3t.esm.js} +29 -36
  84. package/dist/{ekycManager-DLzDuBzI.d.ts → ekycManager-DW-ynodd.d.ts} +3 -3
  85. package/dist/ekycStateMachine-CDnI_NRq.esm.js +1443 -0
  86. package/dist/{ekycUtils-D6wfSpPN.esm.js → ekycUtils-zY8CdKJL.esm.js} +53 -3
  87. package/dist/electronic-signature.d.ts +1 -1
  88. package/dist/electronic-signature.esm.js +1 -1
  89. package/dist/{electronicSignatureManager-yYUc30na.esm.js → electronicSignatureManager-BtWCrz5_.esm.js} +10 -11
  90. package/dist/{electronicSignatureManager-Ds-KEVTq.d.ts → electronicSignatureManager-DTSLOvKR.d.ts} +2 -3
  91. package/dist/email.d.ts +1 -1
  92. package/dist/email.esm.js +2 -2
  93. package/dist/{emailManager-bR1N5rV_.esm.js → emailManager-ButFktRm.esm.js} +20 -26
  94. package/dist/{emailManager-CADXW_9X.d.ts → emailManager-Dy6omJVp.d.ts} +2 -2
  95. package/dist/{emailStateMachine-Bc_6oIYw.esm.js → emailStateMachine-BXtlC0NV.esm.js} +3 -4
  96. package/dist/{events-ijlbX_Wx.esm.js → events-Du2A4-Qr.esm.js} +203 -8
  97. package/dist/events.d.ts +3 -2
  98. package/dist/events.esm.js +1 -1
  99. package/dist/extensibility.d.ts +38 -38
  100. package/dist/extensibility.esm.js +36 -36
  101. package/dist/{externalFetch-BKBThAUC.esm.js → externalFetch-DrR9xshk.esm.js} +3 -2
  102. package/dist/face-match.d.ts +1 -1
  103. package/dist/face-match.esm.js +2 -2
  104. package/dist/{faceCaptureManagerFactory-m-7ae67q.d.ts → faceCaptureManagerFactory-1hzeS_rB.d.ts} +11 -51
  105. package/dist/{faceCaptureManagerFactory-B6i9QcAk.esm.js → faceCaptureManagerFactory-CwZUeYfq.esm.js} +19 -54
  106. package/dist/{faceCaptureSetup-DU1mjRr6.esm.js → faceCaptureSetup-CYbdKNlI.esm.js} +104 -396
  107. package/dist/{faceMatchManager-BnvrZM2F.d.ts → faceMatchManager-DIgG-UFS.d.ts} +1 -2
  108. package/dist/{faceMatchManager-Bmv0HzuY.esm.js → faceMatchManager-DWFUQjMQ.esm.js} +3 -3
  109. package/dist/{faceMatchStateMachine-mRIHQ3dA.esm.js → faceMatchStateMachine-CUCvlydD.esm.js} +6 -5
  110. package/dist/{feature-management-C8CuN0os.esm.js → feature-management-rTfjzdDa.esm.js} +3 -3
  111. package/dist/feature-management.esm.js +1 -1
  112. package/dist/field-comparison.d.ts +2 -2
  113. package/dist/field-comparison.esm.js +1 -1
  114. package/dist/{fieldComparisonManager-CBvEGxFx.d.ts → fieldComparisonManager-CUX4gza2.d.ts} +6 -3
  115. package/dist/{fieldComparisonManager-CC-klG0h.esm.js → fieldComparisonManager-v3knTYV_.esm.js} +35 -18
  116. package/dist/fiscal-qr.d.ts +1 -1
  117. package/dist/fiscal-qr.esm.js +1 -1
  118. package/dist/{fiscalQrManager-B1STmT6z.d.ts → fiscalQrManager-D0Iy7EKR.d.ts} +2 -2
  119. package/dist/fiscalQrManager-Dt9fHG7h.esm.js +6798 -0
  120. package/dist/flow-events.d.ts +1 -1
  121. package/dist/flow.d.ts +14 -13
  122. package/dist/flow.esm.js +14 -14
  123. package/dist/{flowCompletionService-DccgtDnm.esm.js → flowCompletionService-oiK1PjPr.esm.js} +2 -3
  124. package/dist/{flowServices-CdsWsppG.esm.js → flowServices-CTc-zqfN.esm.js} +11 -8
  125. package/dist/geolocation.d.ts +1 -1
  126. package/dist/geolocation.esm.js +2 -2
  127. package/dist/{geolocationManager-CGHBxtMb.esm.js → geolocationManager-BOdtVuR8.esm.js} +6 -7
  128. package/dist/{geolocationManager-Cz744E5S.d.ts → geolocationManager-Dn24g1fl.d.ts} +2 -3
  129. package/dist/{geolocationStateMachine-CdEb1Gn0.esm.js → geolocationStateMachine-BnGuZ3hn.esm.js} +4 -5
  130. package/dist/{getDeviceClass-5NYZg9s6.esm.js → getDeviceClass-bsWdEDxP.esm.js} +1 -1
  131. package/dist/{getParsedUserAgent-B2iFDql1.esm.js → getParsedUserAgent-DhQPeKqw.esm.js} +1 -1
  132. package/dist/government-validation.d.ts +1 -1
  133. package/dist/government-validation.esm.js +2 -2
  134. package/dist/{governmentValidationManager-D02vT4yY.d.ts → governmentValidationManager-B82zOut8.d.ts} +3 -3
  135. package/dist/{governmentValidationManager-CacFOtKO.esm.js → governmentValidationManager-BtXWHICu.esm.js} +3 -3
  136. package/dist/{governmentValidationStateMachine-CoaZOSTP.esm.js → governmentValidationStateMachine-COL-Vs48.esm.js} +3 -4
  137. package/dist/home.d.ts +3 -26
  138. package/dist/home.esm.js +2 -2
  139. package/dist/http.esm.js +1 -1
  140. package/dist/id-ocr.d.ts +1 -1
  141. package/dist/id-ocr.esm.js +2 -2
  142. package/dist/id-selfie-flow.d.ts +6 -9
  143. package/dist/id-selfie-flow.esm.js +23 -28
  144. package/dist/id-verification.d.ts +1 -1
  145. package/dist/id-verification.esm.js +2 -2
  146. package/dist/id.d.ts +4 -4
  147. package/dist/id.esm.js +4 -205
  148. package/dist/{idCaptureDefaults-Dl64Trv5.esm.js → idCaptureDefaults-BGsG2OAs.esm.js} +7 -5
  149. package/dist/{idCaptureManager-BHwL2Z0V.d.ts → idCaptureManager-0M02hSuC.d.ts} +242 -98
  150. package/dist/{idCaptureManager-BNmVJQkd.esm.js → idCaptureManager-BKedt48Y.esm.js} +276 -210
  151. package/dist/{idCaptureServices-dtDG2HXS.esm.js → idCaptureServices-D0p_RFqk.esm.js} +21 -40
  152. package/dist/{idCaptureStateMachine-BbiZD3uf.esm.js → idCaptureStateMachine-CxwoN5ff.esm.js} +992 -872
  153. package/dist/{idOcrManager-ByWuQ4ej.esm.js → idOcrManager-D3-HNMXP.esm.js} +3 -3
  154. package/dist/{idOcrManager-CFlDwUvN.d.ts → idOcrManager-DT3kPIB3.d.ts} +3 -4
  155. package/dist/{idOcrStateMachine-Bt_abXQQ.esm.js → idOcrStateMachine-DyENX6Ki.esm.js} +3 -4
  156. package/dist/{idVerificationManager-BB4Pj_N6.d.ts → idVerificationManager-BJapZfpy.d.ts} +2 -3
  157. package/dist/{idVerificationManager-DT42c77H.esm.js → idVerificationManager-iWz7Avkr.esm.js} +3 -3
  158. package/dist/{idVerificationStateMachine-DbDVIh9t.esm.js → idVerificationStateMachine-CL1kUdBe.esm.js} +3 -4
  159. package/dist/identity-reuse.d.ts +2 -2
  160. package/dist/identity-reuse.esm.js +2 -2
  161. package/dist/{identityReuseManager-BXEBBI3N.d.ts → identityReuseManager-B1birg5X.d.ts} +162 -44
  162. package/dist/{identityReuseManager-yG3acUrQ.esm.js → identityReuseManager-CD1ucp1d.esm.js} +26 -13
  163. package/dist/{identityReuseStateMachine-BhAkuIra.esm.js → identityReuseStateMachine-DfV81wQC.esm.js} +89 -24
  164. package/dist/{index-CeaC32qI.d.ts → index-CKQ9QBZP.d.ts} +2 -2
  165. package/dist/index-DpTj5PFb.d.ts +1 -0
  166. package/dist/index.d.ts +1 -1
  167. package/dist/index.esm.js +1 -1
  168. package/dist/{invokeOnCaptureCallback-BjzPjJ_p.esm.js → invokeOnCaptureCallback-DYrkOXO6.esm.js} +1 -1
  169. package/dist/lamejs-B6TzhU5q.esm.js +10 -0
  170. package/dist/mandatory-consent.d.ts +2 -2
  171. package/dist/mandatory-consent.esm.js +2 -2
  172. package/dist/{mandatoryConsentManager-7dI0D08q.esm.js → mandatoryConsentManager-Cwqx1nK7.esm.js} +3 -3
  173. package/dist/{mandatoryConsentManager-DFVm_i2B.d.ts → mandatoryConsentManager-okPILEru.d.ts} +8 -9
  174. package/dist/{mandatoryConsentStateMachine-DC5Mo6M1.esm.js → mandatoryConsentStateMachine-pXWqAHo_.esm.js} +3 -4
  175. package/dist/{multipartRecordingService-mwKFTlhR.esm.js → multipartRecordingService-CLwrF5wD.esm.js} +1 -2
  176. package/dist/{permissionGuards-D6TfVop2.esm.js → permissionGuards-gcy3U0SL.esm.js} +1 -1
  177. package/dist/{permissionServices-EY3_Vx6c.esm.js → permissionServices-Ca-tSNhT.esm.js} +34 -3
  178. package/dist/phone-number.d.ts +24 -0
  179. package/dist/phone-number.esm.js +1922 -0
  180. package/dist/phone.d.ts +1 -1
  181. package/dist/phone.esm.js +2 -2
  182. package/dist/{phoneManager-BBU8Lq0O.d.ts → phoneManager-BO-LTih5.d.ts} +16 -3
  183. package/dist/{phoneManager-IjhSv5XS.esm.js → phoneManager-KKUq-Vgo.esm.js} +31 -22
  184. package/dist/{phoneStateMachine-CXdXufKg.esm.js → phoneStateMachine-D2QWS2cu.esm.js} +38 -20
  185. package/dist/phoneValidation-DnBcNUNh.esm.js +48 -0
  186. package/dist/qe-signature.d.ts +2 -2
  187. package/dist/qe-signature.esm.js +1 -1
  188. package/dist/redirect-to-mobile.d.ts +1 -1
  189. package/dist/redirect-to-mobile.esm.js +2 -2
  190. package/dist/{redirectToMobileManager-BD2PvT5B.d.ts → redirectToMobileManager-CVsvpVbQ.d.ts} +2 -2
  191. package/dist/{redirectToMobileManager-FLEWJtv8.esm.js → redirectToMobileManager-uHW3dyuQ.esm.js} +4 -4
  192. package/dist/{redirectToMobileStateMachine-CSWWgUTO.esm.js → redirectToMobileStateMachine-b-H-ipGM.esm.js} +4 -5
  193. package/dist/{rolldown-runtime-FDOR9p9I.esm.js → rolldown-runtime-B-1-B7_t.esm.js} +10 -1
  194. package/dist/{runChildModule-B2Y_hlix.esm.js → runChildModule-CPa0i2_C.esm.js} +13 -12
  195. package/dist/safeStorage-UxDrnjwa.esm.js +35 -0
  196. package/dist/{sdkVersion-N1UiyFiY.esm.js → sdkVersion-BtROs6OA.esm.js} +1 -1
  197. package/dist/selfie.d.ts +4 -4
  198. package/dist/selfie.esm.js +12 -12
  199. package/dist/selfieErrorUtils-sMc0MaNh.esm.js +21 -0
  200. package/dist/{selfieManager-BSZG891S.d.ts → selfieManager-BJdNu_nd.d.ts} +2 -8
  201. package/dist/{selfieManager-AS6wfUvZ.esm.js → selfieManager-Ch9xJbWJ.esm.js} +6 -6
  202. package/dist/selfieStateMachine-DT8QCfKR.esm.js +41 -0
  203. package/dist/{session-BESZc05k.esm.js → session-CNNTckUS.esm.js} +5 -6
  204. package/dist/session.d.ts +15 -3
  205. package/dist/session.esm.js +30 -5
  206. package/dist/sessionInitializer-DVWMJh25.esm.js +973 -0
  207. package/dist/{setup-BuPbxpwF.esm.js → setup-B7ehQ-lf.esm.js} +155 -140
  208. package/dist/{setup-Cyko6cAn.d.ts → setup-YPKTUCY5.d.ts} +42 -15
  209. package/dist/signature.d.ts +1 -1
  210. package/dist/signature.esm.js +2 -2
  211. package/dist/{signatureManager-DGBSWz2_.d.ts → signatureManager-BpVt_Jv7.d.ts} +2 -2
  212. package/dist/{signatureManager-B72np4ZO.esm.js → signatureManager-f_dPAGXn.esm.js} +7 -11
  213. package/dist/{signatureStateMachine-ce_XMA3l.esm.js → signatureStateMachine-C-nUq_oq.esm.js} +3 -4
  214. package/dist/splitTrackedName-3zJRd2RD.esm.js +19 -0
  215. package/dist/{src-35ekGRzK.esm.js → src-DJAJjRYQ.esm.js} +1 -1
  216. package/dist/{stats-CZcy2J53.esm.js → stats-BEvexnKC.esm.js} +1 -2
  217. package/dist/stats.esm.js +1 -1
  218. package/dist/{sessionInitializer-CnhAkMUN.esm.js → thumbmark.esm-CRc0cYvz.esm.js} +33 -776
  219. package/dist/{trueSightService-M5VpCo96.esm.js → trueSightService-BzLdXpmJ.esm.js} +1391 -709
  220. package/dist/trust-graph.d.ts +1 -2
  221. package/dist/trust-graph.esm.js +2 -2
  222. package/dist/{types-C0oWGUzg.d.ts → types-0L-eFDmr.d.ts} +1 -1
  223. package/dist/{types-C1g9itHE.d.ts → types-B6HsSzpk.d.ts} +6 -7
  224. package/dist/{types-DEGVXwsk2.d.ts → types-BAxn7MpY.d.ts} +110 -62
  225. package/dist/{types-BfgmV7s7.d.ts → types-BQA7lRDV.d.ts} +1 -1
  226. package/dist/{types-Cx-1zo-i.d.ts → types-BvLo3U4u.d.ts} +1 -1
  227. package/dist/{types-Bx-BWveD.d.ts → types-CK8Jbjkp.d.ts} +1 -1
  228. package/dist/{types-CxGdmzxa.d.ts → types-Ckir_BEs.d.ts} +1 -1
  229. package/dist/{types-BjSly8cp.d.ts → types-DUnFZ08M.d.ts} +1 -1
  230. package/dist/{types-ESdtnHtg.d.ts → types-yPH8lAH-.d.ts} +3 -4
  231. package/dist/types-zOgZqMni.d.ts +6 -0
  232. package/dist/{urlUtils-D6phjq1-.esm.js → urlUtils-6jsE6rKp.esm.js} +9 -1
  233. package/dist/video-selfie.d.ts +6 -9
  234. package/dist/video-selfie.esm.js +1390 -1349
  235. package/dist/{videoSelfieCameraWarmer-q9ZjvXu9.esm.js → videoSelfieCameraWarmer-DWoFBJAn.esm.js} +10 -11
  236. package/dist/{videoSelfieFlowModuleMachine-BUe2uACW.esm.js → videoSelfieFlowModuleMachine-BCFVHBQ1.esm.js} +1 -1
  237. package/dist/{videoSelfieStepDetector-ChYgFM15.d.ts → videoSelfieStepDetector-CGsTltqB.d.ts} +4 -4
  238. package/dist/videoTrimByCopy-BFfAiNpv.esm.js +15901 -0
  239. package/dist/{warmup-ChDwHSel.esm.js → warmup-BElYWRh7.esm.js} +348 -11
  240. package/dist/{warmup-Cis2p6O0.d.ts → warmup-BO57syF6.d.ts} +8 -0
  241. package/dist/wasm.d.ts +2 -2
  242. package/dist/wasm.esm.js +3 -4
  243. package/dist/{wasmDsFlagSync-DYrLUEII.esm.js → wasmDsFlagSync-CYj5LL9z.esm.js} +1 -1
  244. package/dist/{wasmWebClient-B7baE-Wf.esm.js → wasmWebClient-BjZw_7OB.esm.js} +570 -33
  245. package/dist/watchlist-for-business.d.ts +1 -1
  246. package/dist/watchlist-for-business.esm.js +2 -2
  247. package/dist/watchlist.d.ts +1 -1
  248. package/dist/watchlist.esm.js +2 -2
  249. package/dist/{watchlistForBusinessManager-CsnJnG-C.d.ts → watchlistForBusinessManager-B3GDPZrr.d.ts} +2 -2
  250. package/dist/{watchlistForBusinessManager-CIMkpEhu.esm.js → watchlistForBusinessManager-CZZOFkGl.esm.js} +7 -5
  251. package/dist/{watchlistForBusinessStateMachine-CJ-MvUre.esm.js → watchlistForBusinessStateMachine-B66E7jud.esm.js} +3 -4
  252. package/dist/{watchlistManager-DLXef90v.d.ts → watchlistManager-DJQOd8Pw.d.ts} +2 -2
  253. package/dist/{watchlistManager-CixcLNDT.esm.js → watchlistManager-DXtX94SN.esm.js} +3 -3
  254. package/dist/{watchlistStateMachine-CBqk2w-h.esm.js → watchlistStateMachine-McALifag.esm.js} +3 -4
  255. package/dist/workflow.d.ts +34 -5
  256. package/dist/workflow.esm.js +64 -47
  257. package/dist/{xstate-actors.esm-BXeQ-Ucz.esm.js → xstate-actors.esm-8Gzepa_A.esm.js} +1 -1
  258. package/dist/{xstate.esm-BuFLFuzz.esm.js → xstate.esm-DSajW7CJ.esm.js} +24 -10
  259. package/package.json +13 -5
  260. package/dist/Actor-iTq7YY48.d.ts +0 -1
  261. package/dist/BrowserDiagnosticsCryptoProvider-BuzqvbhN.esm.js +0 -77
  262. package/dist/StateMachine-EDpYWy0i.d.ts +0 -6
  263. package/dist/api-Bx3gKW_y.esm.js +0 -129
  264. package/dist/fiscalQrManager-DbCVMIBx.esm.js +0 -34143
  265. package/dist/index-B8weJxmL.d.ts +0 -1
  266. package/dist/selfieErrorUtils-UiQ46EBa.esm.js +0 -116
  267. package/dist/selfieStateMachine-DY7vJVNY.esm.js +0 -71
  268. package/dist/types-BN5RPPIZ.esm.js +0 -34
  269. package/dist/types-Bt9RuRqt.d.ts +0 -6
  270. package/dist/virtualCameraCheck-Cjab7FTI.esm.js +0 -48
  271. /package/dist/{types-BSBzhHRH.d.ts → types-BSBzhHRH2.d.ts} +0 -0
@@ -1,31 +1,32 @@
1
- import { c as trueSightHintTransition, l as trueSightLog, m as isTrueSightTesting, r as noteTrueSightCaptureActivity } from "./trueSightService-M5VpCo96.esm.js";
2
- import { r as WasmUtilProvider } from "./safeStorage-BdU9_m2E.esm.js";
3
- import { l as BrowserStorageProvider } from "./setup-BuPbxpwF.esm.js";
4
- import { t as BrowserTimerProvider } from "./BrowserTimerProvider-BXhgNxLt.esm.js";
5
- import { s as exposeExperimentOnce } from "./feature-management-C8CuN0os.esm.js";
6
1
  import { n as revokeObjectURL, t as getTimestamp } from "./IBrowserEnvironmentCapability-DEaRRk-R.esm.js";
7
- import { r as getToken, t as api } from "./api-Bx3gKW_y.esm.js";
8
- import { n as eventModuleNames, s as recordExperimentAssignmentOnce } from "./events-ijlbX_Wx.esm.js";
9
- import { t as getDisableIpify } from "./sessionInitializer-CnhAkMUN.esm.js";
10
- import { t as endpoints } from "./endpoints-B-3Q-LUw.esm.js";
11
- import { t as addDeviceStats } from "./stats-CZcy2J53.esm.js";
2
+ import { l as isGoogleWalletSignedRequestsEnabled, u as BrowserStorageProvider } from "./setup-B7ehQ-lf.esm.js";
3
+ import { t as WasmUtilProvider } from "./WasmUtilProvider-DtJuBdt-.esm.js";
4
+ import { n as eventModuleNames, o as createManagerInstrumentation, s as recordExperimentAssignmentOnce, y as addEvent } from "./events-Du2A4-Qr.esm.js";
5
+ import { r as getToken, t as api, u as endpoints } from "./api-Cz90EHyu.esm.js";
6
+ import { l as trueSightHintTransition, r as noteTrueSightCaptureActivity, u as trueSightLog, y as isTrueSightTesting } from "./trueSightService-BzLdXpmJ.esm.js";
7
+ import { t as getDisableIpify } from "./sessionInitializer-DVWMJh25.esm.js";
8
+ import { t as BrowserTimerProvider } from "./BrowserTimerProvider-BXhgNxLt.esm.js";
9
+ import { s as exposeExperimentOnce } from "./feature-management-rTfjzdDa.esm.js";
10
+ import { t as addDeviceStats } from "./stats-BEvexnKC.esm.js";
12
11
  import { a as isIOS, s as isIPad, t as isAndroid, u as isMacOsSafari } from "./platform-DkcRc9JV.esm.js";
13
- import "./session-BESZc05k.esm.js";
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";
15
- import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-BXeQ-Ucz.esm.js";
16
- import { n as getUrlSearchParam, t as buildUrlWithSearchParams } from "./urlUtils-D6phjq1-.esm.js";
12
+ import "./session-CNNTckUS.esm.js";
13
+ import { c as not, i as assign, l as raise, n as enqueueActions, o as and, r as sendTo, s as createActor, t as setup, u as createManager } from "./xstate.esm-DSajW7CJ.esm.js";
14
+ import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-8Gzepa_A.esm.js";
15
+ import { n as getUrlSearchParam, t as buildUrlWithSearchParams } from "./urlUtils-6jsE6rKp.esm.js";
17
16
  import { r as getCameraReacquisitionConstraints } from "./camera-sIa9ceW2.esm.js";
18
- import { n as invokeRequestPermissionActor, t as checkPermission } from "./permissionServices-EY3_Vx6c.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-DnwTl1ad.esm.js";
17
+ import { i as requestMotionPermission, n as invokeRequestPermissionActor, o as requiresMotionPermissionUserGesture, t as checkPermission } from "./permissionServices-Ca-tSNhT.esm.js";
18
+ import { a as flagIdManualReview, n as runProtectedAttempt, o as PROTECTED_CAPTURE_TARGET, r as cameraCycleProvider, t as createCameraCycleOwnership } from "./createCameraCycleOwnership-Dj_LHUvN.esm.js";
20
19
  import { t as IncodeCanvas } from "./canvas-BxFzWTt-.esm.js";
21
- import { n as needsMotionPrimeOnlyFromPermission, r as resolveStoredPermissionResult, t as isReadyForCaptureFromPermission } from "./permissionGuards-D6TfVop2.esm.js";
22
- import { o as StreamCanvasProcessingSession } from "./multipartRecordingService-mwKFTlhR.esm.js";
20
+ import { n as needsMotionPrimeOnlyFromPermission, r as resolveStoredPermissionResult, t as isReadyForCaptureFromPermission } from "./permissionGuards-gcy3U0SL.esm.js";
21
+ import { o as StreamCanvasProcessingSession } from "./multipartRecordingService-CLwrF5wD.esm.js";
23
22
  import { t as sleep } from "./ITimerCapability-DgdDzp8G.esm.js";
24
23
  import { t as getBrowser } from "./getBrowser-Cbe1C4Tn.esm.js";
24
+ import { a as isBackCameraLabel } from "./backCameraStream-CMvXj4X4.esm.js";
25
25
  import { n as getWindowDimensions } from "./getDeviceClass-CeBiEayS.esm.js";
26
- import { n as runVirtualCameraCheck, t as frameSources } from "./virtualCameraCheck-Cjab7FTI.esm.js";
27
- import { n as DEFAULT_ID_CAPTURE_THRESHOLDS, r as IdCaptureProvider } from "./idCaptureDefaults-Dl64Trv5.esm.js";
28
- import { C as getIdCameraStream, S as OPTIMIZING_WATCHDOG_MARGIN_MS, _ as validateUploadResponse, a as getRealQualityValue, b as resolveIdCaptureThresholds, c as pauseRecordingForAttempt, d as resumeRecordingAfterPause, f as startRecordingSession, g as uploadManualIdFile, h as uploadIdImage, i as finalizeRecordingSession, l as prepareRecordingForCameraRetry, m as stopStream, o as initializeIdCapture, p as stopRecording, r as disposeRecordingSession, s as isSuspendableRecordingSession, t as addBarcode, u as processId, v as ID_ERROR_CODES, x as CAPTURE_OPTIMIZING } from "./idCaptureServices-dtDG2HXS.esm.js";
26
+ import { n as frameSources, r as runVirtualCameraCheck } from "./deepsightService-tGP53cSS.esm.js";
27
+ import { t as getDeviceClass } from "./getDeviceClass-bsWdEDxP.esm.js";
28
+ import { n as DEFAULT_ID_CAPTURE_THRESHOLDS, r as IdCaptureProvider } from "./idCaptureDefaults-BGsG2OAs.esm.js";
29
+ import { C as getIdCameraStream, S as OPTIMIZING_WATCHDOG_MARGIN_MS, _ as ID_ERROR_CODES, a as getRealQualityValue, b as resolveIdCaptureThresholds, c as pauseRecordingForAttempt, d as resumeRecordingAfterPause, f as startRecordingSession, g as validateUploadResponse, h as uploadIdImage, i as finalizeRecordingSession, l as prepareRecordingForCameraRetry, m as stopStream, o as initializeIdCapture, p as stopRecording, r as disposeRecordingSession, s as isSuspendableRecordingSession, t as addBarcode, u as processId, x as CAPTURE_OPTIMIZING, y as resolveCountdownStepMs } from "./idCaptureServices-D0p_RFqk.esm.js";
29
30
  import { t as StreamCanvasCapture } from "./StreamCanvasCapture-C9jtgBdT.esm.js";
30
31
  //#region src/modules/id/digitalUpload/digitalUploadErrors.ts
31
32
  const TERMINAL_FAIL_REASONS = /* @__PURE__ */ new Set(["DIGITAL_ID_REQUESTED_BUT_OTHER_PROVIDED", "ID_TYPE_UNACCEPTABLE"]);
@@ -89,7 +90,7 @@ const uploadDigitalIdActor = fromPromise(async ({ input, signal }) => {
89
90
  extractIdFace: input.extractIdFace
90
91
  });
91
92
  });
92
- const getUploadResponseFromEvent$1 = (event) => {
93
+ const getUploadResponseFromEvent = (event) => {
93
94
  if (!("output" in event)) return;
94
95
  return event.output;
95
96
  };
@@ -153,10 +154,10 @@ const digitalUploadMachine = setup({
153
154
  isSelectedFileTooLarge: ({ event }) => event.type === "FILE_PICKED" && event.file.size > 5242880,
154
155
  isSelectedFileMimeInvalid: ({ event }) => event.type === "FILE_PICKED" && !DIGITAL_UPLOAD_ALLOWED_MIME_TYPES.includes(event.file.type),
155
156
  hasSelectedFile: ({ context }) => context.file !== null,
156
- responseHasFailReason: ({ event }) => mapDigitalIdFailReason(getUploadResponseFromEvent$1(event)) !== null,
157
+ responseHasFailReason: ({ event }) => mapDigitalIdFailReason(getUploadResponseFromEvent(event)) !== null,
157
158
  hasAttemptsRemaining: ({ context }) => context.attemptsRemaining > 0,
158
159
  isTerminalFailReason: ({ event }) => {
159
- return isTerminalDigitalIdFailReason(getUploadResponseFromEvent$1(event));
160
+ return isTerminalDigitalIdFailReason(getUploadResponseFromEvent(event));
160
161
  },
161
162
  isCurrentFailReasonTerminal: ({ context }) => {
162
163
  const terminalReasons = /* @__PURE__ */ new Set(["DIGITAL_ID_REQUESTED_BUT_OTHER_PROVIDED", "ID_TYPE_UNACCEPTABLE"]);
@@ -316,14 +317,39 @@ const digitalUploadMachine = setup({
316
317
  //#region src/modules/id/deviceWallet/deviceWalletServices.ts
317
318
  /** Both wallet endpoint families are versioned via header, not path. */
318
319
  const API_VERSION_HEADERS = { "api-version": "1.0" };
320
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
319
321
  /**
320
- * Fetches the wallet request payload for the platform and wraps it in the
321
- * Digital Credentials API request shape.
322
+ * Turns the Google request payload into DC API request options without ever
323
+ * touching the payload itself — a signed request's JWS is covered by its
324
+ * signature, so any edit makes the wallet reject it. The backend decides the
325
+ * protocol; the SDK never derives it.
326
+ *
327
+ * - `{ requests: [...] }` — full DC API envelope (signed-capable backend):
328
+ * passed through as-is.
329
+ * - `{ protocol, data }` — an already-enveloped single request: not re-wrapped,
330
+ * only placed in the `requests` array (defensive, for rollout shape drift).
331
+ * - anything else — legacy bare unsigned OIDC4VP/DCQL object: wrapped with
332
+ * `openid4vp-v1-unsigned`.
333
+ */
334
+ function toGoogleRequestOptions(payload) {
335
+ if (isRecord(payload) && Array.isArray(payload.requests)) return payload;
336
+ if (isRecord(payload) && typeof payload.protocol === "string") return { requests: [payload] };
337
+ return { requests: [{
338
+ protocol: "openid4vp-v1-unsigned",
339
+ data: payload
340
+ }] };
341
+ }
342
+ /**
343
+ * Fetches the wallet request payload for the platform as Digital Credentials
344
+ * API request options.
322
345
  *
323
346
  * Apple: POST with an empty body → `{ deviceRequest, encryptionInfo }`,
324
347
  * presented with protocol `org-iso-mdoc`.
325
- * Google: GET → the whole backend-built OIDC4VP/DCQL object (opaque here),
326
- * presented with protocol `openid4vp-v1-unsigned`.
348
+ * Google: GET with `signedSupported=true` → the backend-built DC API envelope
349
+ * (signed or unsigned, backend's choice), passed through verbatim — see
350
+ * {@link toGoogleRequestOptions}. With `setup({ deviceWallet: {
351
+ * googleSignedRequests: false } })` the SDK sends `signedSupported=false` and
352
+ * the backend answers with the legacy bare unsigned payload.
327
353
  */
328
354
  async function requestWalletData(platform, signal) {
329
355
  if (platform === "ios") {
@@ -333,23 +359,21 @@ async function requestWalletData(platform, signal) {
333
359
  });
334
360
  if (!res.ok) throw new Error(`POST ${endpoints.deviceWalletAppleRequest} failed: ${res.status}`);
335
361
  const data = res.data;
336
- return {
362
+ return { requests: [{
337
363
  protocol: "org-iso-mdoc",
338
364
  data: {
339
365
  deviceRequest: data.deviceRequest,
340
366
  encryptionInfo: data.encryptionInfo
341
367
  }
342
- };
368
+ }] };
343
369
  }
344
370
  const res = await api.get(endpoints.deviceWalletGoogleRequest, {
345
371
  headers: API_VERSION_HEADERS,
372
+ query: { signedSupported: isGoogleWalletSignedRequestsEnabled() },
346
373
  signal
347
374
  });
348
375
  if (!res.ok) throw new Error(`GET ${endpoints.deviceWalletGoogleRequest} failed: ${res.status}`);
349
- return {
350
- protocol: "openid4vp-v1-unsigned",
351
- data: res.data
352
- };
376
+ return toGoogleRequestOptions(res.data);
353
377
  }
354
378
  /**
355
379
  * Forwards the opaque wallet response for backend decryption. The body key
@@ -529,408 +553,221 @@ function resolveWalletPlatform(provider) {
529
553
  return isIOS() || isMacOsSafari() || isIPad() ? "ios" : "android";
530
554
  }
531
555
  //#endregion
532
- //#region src/modules/id/manualUpload/manualUploadErrors.ts
556
+ //#region src/modules/id/deviceWallet/instrumentation.ts
533
557
  /**
534
- * i18n keys for manual-upload user-facing error copy.
535
- * Keep in sync with `packages/ui/src/i18n/**.json` under the `manualIdUpload.*` namespace.
558
+ * Maps a device-wallet child-machine snapshot to a tracked screen name.
559
+ * Family is `deviceWallet.*` — emitted cross-family under the parent
560
+ * `idCapture` module token, same as `mapDigitalIdScreenName`. See
561
+ * `docs/patterns/INSTRUMENTATION.md`.
536
562
  */
537
- const MANUAL_UPLOAD_ERROR_KEYS = {
538
- wrongSide: "manualIdUpload.wrongSide",
539
- wrongDocument: "manualIdUpload.wrongDocument",
540
- fileTooBig: "manualIdUpload.fileTooBig",
541
- generic: "manualIdUpload.generic",
542
- lowSharpness: "manualIdUpload.lowSharpness",
543
- glareDetected: "manualIdUpload.glareDetected",
544
- readabilityIssue: "manualIdUpload.readabilityIssue"
563
+ function mapDeviceWalletScreenName(value, failReason) {
564
+ if (value === "presenting") return "deviceWallet.wallet.default";
565
+ if (value === "failed") {
566
+ if (failReason === "NO_CREDENTIAL") return "deviceWallet.error.nocredential";
567
+ if (failReason === "UNSUPPORTED_PLATFORM") return "deviceWallet.error.unsupported";
568
+ return "deviceWallet.error.default";
569
+ }
570
+ return "deviceWallet.loading.default";
571
+ }
572
+ //#endregion
573
+ //#region ../infra/src/providers/browser/BrowserDigitalCredentialsProvider.ts
574
+ var BrowserDigitalCredentialsProvider = class {
575
+ isAvailable() {
576
+ return typeof navigator !== "undefined" && typeof navigator.credentials?.get === "function" && typeof globalThis.DigitalCredential !== "undefined";
577
+ }
578
+ async get(request, options) {
579
+ return { response: (await navigator.credentials.get({
580
+ mediation: "required",
581
+ signal: options?.signal,
582
+ digital: request
583
+ }))?.data?.response };
584
+ }
545
585
  };
586
+ //#endregion
587
+ //#region src/modules/id/deviceWallet/deviceWalletActor.ts
546
588
  /**
547
- * Map the server's `failReason` (plus client-side passport/ID tab mismatch and
548
- * per-image quality flags) onto an i18n key. Mirrors v1's `shouldUploadAgain`
549
- * branch logic in
550
- * `ms-incodesmile-web/packages/incode-welcome/src/manualIdUpload/manuaIdUpload.tsx`,
551
- * extended to also reject responses where the backend flagged a per-image
552
- * quality issue (`correctSharpness`, `correctGlare`, or `readability`).
553
- * `idQualityAttemptApproved` is intentionally ignored — v1 did not treat it
554
- * as a manual-upload rejection signal.
555
- *
556
- * Quality flags are checked with `=== false` so missing/undefined values
557
- * (older backends, partial payloads) keep the legacy "trust the server"
558
- * behaviour and do not produce false positives.
559
- *
560
- * Returns `null` when the response represents a successful upload — the caller
561
- * should treat `null` as "no error, advance to next slot".
589
+ * Starts a device-wallet actor for headless use. The ID-Capture parent invokes
590
+ * `deviceWalletMachine` itself (and resolves the platform through the
591
+ * digital-ID funnel); this factory is the standalone entry point for
592
+ * integrators driving the wallet handshake on their own.
562
593
  */
563
- function mapFailReasonToErrorKey(response, expectedTab) {
564
- const failReason = response.failReason;
565
- if (failReason === "WRONG_DOCUMENT_SIDE") return MANUAL_UPLOAD_ERROR_KEYS.wrongSide;
566
- if (failReason === "WRONG_ONE_SIDED_DOCUMENT" || failReason === "UNKNOWN_DOCUMENT_TYPE") return MANUAL_UPLOAD_ERROR_KEYS.wrongDocument;
567
- if (failReason === "ID_TYPE_UNACCEPTABLE") return MANUAL_UPLOAD_ERROR_KEYS.wrongDocument;
568
- if (expectedTab === "passport" && response.typeOfId && response.typeOfId.toLowerCase() !== "passport") return MANUAL_UPLOAD_ERROR_KEYS.wrongDocument;
569
- if (expectedTab === "id" && response.typeOfId?.toLowerCase() === "passport") return MANUAL_UPLOAD_ERROR_KEYS.wrongDocument;
570
- if (response.correctSharpness === false) return MANUAL_UPLOAD_ERROR_KEYS.lowSharpness;
571
- if (response.correctGlare === false) return MANUAL_UPLOAD_ERROR_KEYS.glareDetected;
572
- if (response.readability === false) return MANUAL_UPLOAD_ERROR_KEYS.readabilityIssue;
573
- return null;
594
+ function createDeviceWalletActor(options = {}) {
595
+ return createActor(deviceWalletMachine, { input: {
596
+ platform: resolveWalletPlatform(options.provider),
597
+ credentials: options.credentials ?? new BrowserDigitalCredentialsProvider(),
598
+ services: options.services
599
+ } }).start();
574
600
  }
575
601
  //#endregion
576
- //#region src/modules/id/manualUpload/types.ts
577
- const MANUAL_UPLOAD_MAX_RETRIES = 3;
578
- const MANUAL_UPLOAD_MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
579
- const MANUAL_UPLOAD_EXHAUSTION_FORWARD_MS = 5e3;
580
- //#endregion
581
- //#region src/modules/id/manualUpload/manualUploadServices.ts
582
- const stripDataUrlPrefix = (dataUrl) => {
583
- const commaIndex = dataUrl.indexOf(",");
584
- return commaIndex === -1 ? dataUrl : dataUrl.slice(commaIndex + 1);
585
- };
586
- const getBase64PayloadByteLength = (payload) => {
587
- const normalized = payload.replace(/\s/g, "");
588
- let padding = 0;
589
- if (normalized.endsWith("==")) padding = 2;
590
- else if (normalized.endsWith("=")) padding = 1;
591
- return Math.max(0, Math.floor(normalized.length * 3 / 4) - padding);
592
- };
593
- const isBase64PayloadWithinSize = (payload, maxBytes) => getBase64PayloadByteLength(payload) <= maxBytes;
594
- const fileToBase64Payload = async (file) => {
595
- const dataUrl = await imageFileToDataUrl(file);
596
- return stripDataUrlPrefix(dataUrl);
597
- };
598
- //#endregion
599
- //#region src/modules/id/manualUpload/manualUploadStateMachine.ts
600
- const uploadManualFileActor = fromPromise(async ({ input, signal }) => {
601
- const base64Image = await fileToBase64Payload(input.file);
602
- if (!isBase64PayloadWithinSize(base64Image, 5242880)) return { type: "fileTooLarge" };
602
+ //#region src/modules/id/deviceWallet/deviceWalletManager.ts
603
+ /**
604
+ * Absolute tracked-screen name for a device-wallet snapshot. Emitted verbatim
605
+ * on `event.screen` under the parent `idCapture` module token — the wallet is
606
+ * an ID-Capture sub-flow, so it does not own an analytics module of its own
607
+ * (see `docs/patterns/INSTRUMENTATION.md`).
608
+ */
609
+ function getDeviceWalletScreenName(snapshot) {
610
+ return mapDeviceWalletScreenName(snapshot.value, snapshot.context?.failReason ?? null);
611
+ }
612
+ function mapState(snapshot) {
613
+ const typedSnapshot = snapshot;
614
+ const { context } = typedSnapshot;
615
+ const { platform } = context;
616
+ if (typedSnapshot.matches("presenting")) return {
617
+ status: "presenting",
618
+ platform
619
+ };
620
+ if (typedSnapshot.matches("decrypting")) return {
621
+ status: "decrypting",
622
+ platform
623
+ };
624
+ if (typedSnapshot.matches("finished") && context.response) return {
625
+ status: "finished",
626
+ platform,
627
+ decision: context.response.decision,
628
+ response: context.response
629
+ };
630
+ if (typedSnapshot.matches("failed")) return {
631
+ status: "failed",
632
+ platform,
633
+ reason: context.failReason ?? "WALLET_ERROR"
634
+ };
635
+ if (typedSnapshot.matches("closed")) return {
636
+ status: "closed",
637
+ platform
638
+ };
603
639
  return {
604
- type: "uploaded",
605
- response: await uploadManualIdFile({
606
- side: input.side,
607
- base64Image,
608
- retry: input.retry,
609
- onlyFront: input.onlyFront,
610
- isSecondId: input.isSecondId,
611
- extractIdFace: input.extractIdFace,
612
- signal
613
- })
640
+ status: "requesting",
641
+ platform
614
642
  };
615
- });
616
- const getUploadOutputFromEvent = (event) => {
617
- if ("output" in event) return event.output;
618
- };
619
- const getUploadResponseFromEvent = (event) => {
620
- const output = getUploadOutputFromEvent(event);
621
- return output?.type === "uploaded" ? output.response : void 0;
622
- };
623
- const manualUploadMachine = setup({
624
- types: {
625
- context: {},
626
- events: {},
627
- input: {},
628
- output: {}
629
- },
630
- actors: { uploadManualFile: uploadManualFileActor },
631
- actions: {
632
- setManualUploadActiveTab: assign(({ context, event }) => {
633
- if (event.type !== "MANUAL_UPLOAD_TAB_CHANGED") return {};
634
- return {
635
- activeTab: event.tab,
636
- errorMessage: null,
637
- errorSide: null,
638
- retriesLeft: context.captureAttempts
639
- };
640
- }),
641
- resetManualUpload: assign(({ context }) => ({
642
- frontFile: null,
643
- backFile: null,
644
- passportFile: null,
645
- frontUploaded: false,
646
- backUploaded: false,
647
- passportUploaded: false,
648
- skipBackFromServer: false,
649
- errorMessage: null,
650
- errorSide: null,
651
- retriesLeft: context.captureAttempts
652
- })),
653
- storeSelectedFile: assign(({ event }) => {
654
- if (event.type !== "MANUAL_UPLOAD_FILE_SELECTED") return {};
655
- const updated = {
656
- errorMessage: null,
657
- errorSide: null
658
- };
659
- if (event.side === "front") updated.frontFile = event.file;
660
- else if (event.side === "back") updated.backFile = event.file;
661
- else updated.passportFile = event.file;
662
- return updated;
663
- }),
664
- markFrontUploaded: assign(() => ({
665
- frontUploaded: true,
666
- errorMessage: null,
667
- errorSide: null
668
- })),
669
- markBackUploaded: assign(() => ({
670
- backUploaded: true,
671
- errorMessage: null,
672
- errorSide: null
673
- })),
674
- markPassportUploaded: assign(() => ({
675
- passportUploaded: true,
676
- errorMessage: null,
677
- errorSide: null
678
- })),
679
- recordSkipBackFromResponse: assign(({ event }) => {
680
- const response = getUploadResponseFromEvent(event);
681
- if (!response) return {};
682
- if (!response.skipBackIdCapture) return {};
683
- return { skipBackFromServer: true };
684
- }),
685
- decrementManualUploadRetries: assign(({ context }) => ({ retriesLeft: Math.max(0, context.retriesLeft - 1) })),
686
- setManualUploadErrorFromResponse: assign(({ context, event }) => {
687
- const response = getUploadResponseFromEvent(event);
688
- if (!response) return {};
689
- return { errorMessage: mapFailReasonToErrorKey(response, context.activeTab) ?? "manualIdUpload.generic" };
690
- }),
691
- setManualUploadGenericError: assign(() => ({ errorMessage: "manualIdUpload.generic" })),
692
- setManualUploadFileTooLargeError: assign(({ event }) => {
693
- return {
694
- errorMessage: "manualIdUpload.fileTooBig",
695
- errorSide: event.type === "MANUAL_UPLOAD_FILE_SELECTED" ? event.side : null
696
- };
697
- }),
698
- markErrorSideFront: assign(() => ({ errorSide: "front" })),
699
- markErrorSideBack: assign(() => ({ errorSide: "back" })),
700
- markErrorSidePassport: assign(() => ({ errorSide: "passport" })),
701
- clearManualUploadError: assign(() => ({
702
- errorMessage: null,
703
- errorSide: null
704
- }))
705
- },
706
- guards: {
707
- isAllowedTabChange: ({ context, event }) => event.type === "MANUAL_UPLOAD_TAB_CHANGED" && (!context.onlyBack || event.tab === "id"),
708
- isSelectedFileTooLarge: ({ context, event }) => {
709
- if (event.type !== "MANUAL_UPLOAD_FILE_SELECTED") return false;
710
- if (context.onlyBack && event.side !== "back") return false;
711
- return event.file.size > MANUAL_UPLOAD_MAX_FILE_SIZE_BYTES;
712
- },
713
- isFrontFileSelection: ({ context, event }) => !context.onlyBack && event.type === "MANUAL_UPLOAD_FILE_SELECTED" && event.side === "front",
714
- isBackFileSelection: ({ event }) => event.type === "MANUAL_UPLOAD_FILE_SELECTED" && event.side === "back",
715
- isPassportFileSelection: ({ context, event }) => !context.onlyBack && event.type === "MANUAL_UPLOAD_FILE_SELECTED" && event.side === "passport",
716
- manualUploadRetriesExhausted: ({ context }) => context.retriesLeft <= 0,
717
- canContinueFromIdTab: ({ context }) => {
718
- if (context.activeTab !== "id") return false;
719
- if (context.onlyBack) return context.backUploaded;
720
- if (!context.frontUploaded) return false;
721
- if (context.onlyFront) return true;
722
- if (context.skipBackFromServer) return true;
723
- return context.backUploaded;
724
- },
725
- canContinueFromPassportTab: ({ context }) => !context.onlyBack && context.activeTab === "passport" && context.passportUploaded,
726
- manualUploadResponseHasError: ({ context, event }) => {
727
- const response = getUploadResponseFromEvent(event);
728
- if (!response) return false;
729
- return mapFailReasonToErrorKey(response, context.activeTab) !== null;
730
- },
731
- manualUploadPayloadTooLarge: ({ event }) => getUploadOutputFromEvent(event)?.type === "fileTooLarge"
732
- }
733
- }).createMachine({
734
- id: "manualUpload",
735
- initial: "selecting",
736
- context: ({ input }) => ({
737
- onlyFront: input.onlyFront,
738
- onlyBack: input.onlyBack ?? false,
739
- isSecondId: input.isSecondId,
740
- captureAttempts: input.captureAttempts,
741
- extractIdFace: input.extractIdFace,
742
- frontFile: null,
743
- backFile: null,
744
- passportFile: null,
745
- activeTab: "id",
746
- retriesLeft: input.captureAttempts,
747
- errorMessage: null,
748
- errorSide: null,
749
- skipBackFromServer: false,
750
- frontUploaded: false,
751
- backUploaded: false,
752
- passportUploaded: false
753
- }),
754
- on: {
755
- MANUAL_UPLOAD_TAB_CHANGED: {
756
- guard: "isAllowedTabChange",
757
- target: ".selecting",
758
- actions: ["setManualUploadActiveTab"]
759
- },
760
- MANUAL_UPLOAD_RESET: {
761
- target: ".selecting",
762
- actions: ["resetManualUpload"]
763
- }
764
- },
765
- states: {
766
- selecting: { on: {
767
- MANUAL_UPLOAD_FILE_SELECTED: [
768
- {
769
- guard: "isSelectedFileTooLarge",
770
- actions: ["setManualUploadFileTooLargeError"]
771
- },
772
- {
773
- guard: "isFrontFileSelection",
774
- target: "uploadingFront",
775
- actions: ["storeSelectedFile"]
776
- },
777
- {
778
- guard: "isBackFileSelection",
779
- target: "uploadingBack",
780
- actions: ["storeSelectedFile"]
781
- },
782
- {
783
- guard: "isPassportFileSelection",
784
- target: "uploadingPassport",
785
- actions: ["storeSelectedFile"]
786
- }
787
- ],
788
- MANUAL_UPLOAD_CONTINUE: [{
789
- guard: "canContinueFromIdTab",
790
- target: "finished"
791
- }, {
792
- guard: "canContinueFromPassportTab",
793
- target: "finished"
794
- }]
795
- } },
796
- uploadingFront: { invoke: {
797
- id: "uploadManualFile",
798
- src: "uploadManualFile",
799
- input: ({ context }) => ({
800
- side: "front",
801
- file: context.frontFile,
802
- retry: context.retriesLeft < context.captureAttempts,
803
- onlyFront: context.onlyFront,
804
- isSecondId: context.isSecondId,
805
- extractIdFace: context.extractIdFace
806
- }),
807
- onDone: [
808
- {
809
- guard: "manualUploadPayloadTooLarge",
810
- target: "selecting",
811
- actions: ["setManualUploadFileTooLargeError", "markErrorSideFront"]
812
- },
813
- {
814
- guard: "manualUploadResponseHasError",
815
- target: "checkRetries",
816
- actions: [
817
- "decrementManualUploadRetries",
818
- "setManualUploadErrorFromResponse",
819
- "markErrorSideFront"
820
- ]
821
- },
822
- {
823
- target: "selecting",
824
- actions: [
825
- "markFrontUploaded",
826
- "recordSkipBackFromResponse",
827
- "clearManualUploadError"
828
- ]
829
- }
830
- ],
831
- onError: {
832
- target: "checkRetries",
833
- actions: [
834
- "decrementManualUploadRetries",
835
- "setManualUploadGenericError",
836
- "markErrorSideFront"
837
- ]
838
- }
839
- } },
840
- uploadingBack: { invoke: {
841
- id: "uploadManualFile",
842
- src: "uploadManualFile",
843
- input: ({ context }) => ({
844
- side: "back",
845
- file: context.backFile,
846
- retry: context.retriesLeft < context.captureAttempts,
847
- onlyFront: false,
848
- isSecondId: context.isSecondId
849
- }),
850
- onDone: [
851
- {
852
- guard: "manualUploadPayloadTooLarge",
853
- target: "selecting",
854
- actions: ["setManualUploadFileTooLargeError", "markErrorSideBack"]
855
- },
856
- {
857
- guard: "manualUploadResponseHasError",
858
- target: "checkRetries",
859
- actions: [
860
- "decrementManualUploadRetries",
861
- "setManualUploadErrorFromResponse",
862
- "markErrorSideBack"
863
- ]
864
- },
865
- {
866
- target: "selecting",
867
- actions: ["markBackUploaded", "clearManualUploadError"]
868
- }
869
- ],
870
- onError: {
871
- target: "checkRetries",
872
- actions: [
873
- "decrementManualUploadRetries",
874
- "setManualUploadGenericError",
875
- "markErrorSideBack"
876
- ]
877
- }
878
- } },
879
- uploadingPassport: { invoke: {
880
- id: "uploadManualFile",
881
- src: "uploadManualFile",
882
- input: ({ context }) => ({
883
- side: "passport",
884
- file: context.passportFile,
885
- retry: context.retriesLeft < context.captureAttempts,
886
- onlyFront: false,
887
- isSecondId: context.isSecondId,
888
- extractIdFace: context.extractIdFace
889
- }),
890
- onDone: [
891
- {
892
- guard: "manualUploadPayloadTooLarge",
893
- target: "selecting",
894
- actions: ["setManualUploadFileTooLargeError", "markErrorSidePassport"]
895
- },
896
- {
897
- guard: "manualUploadResponseHasError",
898
- target: "checkRetries",
899
- actions: [
900
- "decrementManualUploadRetries",
901
- "setManualUploadErrorFromResponse",
902
- "markErrorSidePassport"
903
- ]
904
- },
905
- {
906
- target: "selecting",
907
- actions: ["markPassportUploaded", "clearManualUploadError"]
908
- }
909
- ],
910
- onError: {
911
- target: "checkRetries",
912
- actions: [
913
- "decrementManualUploadRetries",
914
- "setManualUploadGenericError",
915
- "markErrorSidePassport"
916
- ]
917
- }
918
- } },
919
- checkRetries: { always: [{
920
- guard: "manualUploadRetriesExhausted",
921
- target: "retriesExhausted"
922
- }, { target: "selecting" }] },
923
- retriesExhausted: { after: { [MANUAL_UPLOAD_EXHAUSTION_FORWARD_MS]: { target: "exhausted" } } },
924
- exhausted: {
925
- type: "final",
926
- output: { result: "exhausted" }
643
+ }
644
+ function createApi({ actor, trackElementClicked }) {
645
+ return {
646
+ /** Restarts the handshake after a failure. Needs a fresh user gesture. */
647
+ retry() {
648
+ trackElementClicked?.("retry");
649
+ actor.send({ type: "RETRY" });
927
650
  },
928
- finished: {
929
- type: "final",
930
- output: { result: "success" }
651
+ /**
652
+ * Abandons the wallet path from a failure and moves the manager to
653
+ * `closed`, so the consumer can fall back to a camera ID capture.
654
+ */
655
+ useAnotherMethod() {
656
+ trackElementClicked?.("useAnotherMethod");
657
+ actor.send({ type: "USE_ANOTHER_METHOD" });
931
658
  }
932
- }
933
- });
659
+ };
660
+ }
661
+ /**
662
+ * Creates a headless device-wallet (mDL) manager — the V2 equivalent of V1's
663
+ * `renderWallet`, with the intermediate states observable.
664
+ *
665
+ * Runs the Apple Wallet (web) / Google Wallet handshake with zero Incode UI,
666
+ * so integrators trigger it from their own button and render their own UX.
667
+ * Call it from a user gesture: iOS Safari requires transient user activation
668
+ * for `navigator.credentials.get()`.
669
+ *
670
+ * Requires an initialized session (`setup({ apiURL, token })`) — the request
671
+ * and decrypt calls are session-scoped.
672
+ *
673
+ * @example
674
+ * ```typescript
675
+ * import { setup } from '@incodetech/core';
676
+ * import { createDeviceWalletManager } from '@incodetech/core/id';
677
+ *
678
+ * setup({ apiURL, token });
679
+ *
680
+ * button.addEventListener('click', () => {
681
+ * const manager = createDeviceWalletManager();
682
+ * manager.subscribe((state) => {
683
+ * if (state.status === 'finished') manager.stop();
684
+ * if (state.status === 'failed') console.error(state.reason);
685
+ * });
686
+ * });
687
+ * ```
688
+ */
689
+ function createDeviceWalletManager(options = {}) {
690
+ return createDeviceWalletManagerFromActor(createDeviceWalletActor(options));
691
+ }
692
+ /**
693
+ * Creates a device-wallet manager from a pre-built actor. Use this when
694
+ * overriding the machine via `.provide()` for custom backends or tests.
695
+ */
696
+ function createDeviceWalletManagerFromActor(actor) {
697
+ return createManager({
698
+ actor,
699
+ mapState,
700
+ createApi,
701
+ instrumentation: createManagerInstrumentation(eventModuleNames.idCapture, { getEventScreenName: getDeviceWalletScreenName })
702
+ });
703
+ }
704
+ //#endregion
705
+ //#region src/modules/id/deviceWallet/renderWallet.ts
706
+ const FAIL_MESSAGES = {
707
+ UNSUPPORTED_PLATFORM: "Device wallet is not supported on this platform/browser",
708
+ NO_CREDENTIAL: "The wallet returned no digital ID credential",
709
+ WALLET_ERROR: "Device wallet request failed"
710
+ };
711
+ /**
712
+ * Headless device-wallet (mDL) API — the V1 `renderWallet` shape, ported.
713
+ *
714
+ * Runs the Apple Wallet (web) / Google Wallet handshake with zero Incode UI,
715
+ * so integrators trigger it from their own button and render their own UX.
716
+ * Resolves once the handshake settles; the outcome arrives through
717
+ * `onSuccess` / `onError` exactly as in V1.
718
+ *
719
+ * MUST be called from a user gesture (e.g. a click handler) — iOS/Safari
720
+ * requires transient user activation for `navigator.credentials.get()` to
721
+ * present the wallet sheet.
722
+ *
723
+ * Requires an initialized session (`setup({ apiURL, token })`). The verified
724
+ * attributes are NOT returned here: they land in the session and are read back
725
+ * through the standard OCR / score APIs. `onSuccess` receives the submission
726
+ * decision/status.
727
+ *
728
+ * Prefer {@link createDeviceWalletManager} for new integrations — it exposes
729
+ * the intermediate `requesting` / `presenting` / `decrypting` states (and a
730
+ * `retry()`), which this one-shot wrapper collapses.
731
+ *
732
+ * @example
733
+ * ```typescript
734
+ * button.addEventListener('click', () => {
735
+ * void renderWallet({
736
+ * onSuccess: (result) => console.log(result.decision),
737
+ * onError: (error) => console.error(error.code, error.message),
738
+ * });
739
+ * });
740
+ * ```
741
+ */
742
+ function renderWallet({ onSuccess, onError, ...actorOptions }) {
743
+ return new Promise((resolve) => {
744
+ const manager = createDeviceWalletManager(actorOptions);
745
+ const subscription = {};
746
+ let settled = false;
747
+ const settle = (state) => {
748
+ if (state.status === "requesting" || state.status === "presenting" || state.status === "decrypting") return false;
749
+ settled = true;
750
+ subscription.unsubscribe?.();
751
+ manager.stop();
752
+ if (state.status === "finished") onSuccess(state.response);
753
+ else if (state.status === "failed") onError({
754
+ code: state.reason,
755
+ message: FAIL_MESSAGES[state.reason]
756
+ });
757
+ else onError({
758
+ code: "WALLET_ERROR",
759
+ message: FAIL_MESSAGES.WALLET_ERROR
760
+ });
761
+ resolve();
762
+ return true;
763
+ };
764
+ if (settle(manager.getState())) return;
765
+ subscription.unsubscribe = manager.subscribe((state) => {
766
+ if (settled) return;
767
+ settle(state);
768
+ });
769
+ });
770
+ }
934
771
  //#endregion
935
772
  //#region src/modules/id/idCaptureGuards.ts
936
773
  function getPermissionOutput(event) {
@@ -941,7 +778,7 @@ function getPermissionOutput(event) {
941
778
  }
942
779
  const hasShowTutorialGuard = ({ context }) => context.config.showTutorial;
943
780
  const hasAgeAssuranceGuard = ({ context }) => context.config.ageAssurance === true;
944
- const hasShowDocumentChooserGuard = ({ context }) => (!context.config.onlyBack || context.config.manualUploadIdCapture === true) && (context.config.showDocumentChooserScreen ?? false);
781
+ const hasShowDocumentChooserGuard = ({ context }) => !context.config.onlyBack && (context.config.showDocumentChooserScreen ?? false);
945
782
  const isPermissionGrantedGuard = ({ event }) => {
946
783
  return getPermissionOutput(event) === "granted";
947
784
  };
@@ -959,6 +796,7 @@ const isPermissionDeniedErrorGuard = ({ event }) => {
959
796
  return false;
960
797
  };
961
798
  const isPermissionBlockedGuard = ({ context }) => context.permissionResult === "denied" || context.permissionResult === "refresh";
799
+ const requiresMotionPermissionUserGestureGuard = ({ context }) => requiresMotionPermissionUserGesture(context.config.ds);
962
800
  const hasAttemptsRemainingGuard = ({ context }) => context.attemptsRemaining > 0;
963
801
  const hasCapturedImageGuard = ({ context }) => {
964
802
  return context.provider?.getCapturedCanvas() !== null;
@@ -994,11 +832,9 @@ const isFrontModeGuard = ({ context }) => context.currentMode === "front" || con
994
832
  * server answer exists, and
995
833
  * 2. where you do, still self-correct once the answer arrives.
996
834
  *
997
- * Two variants qualify under (1): the captureOnly machine
998
- * (`idCaptureOnlyStateMachine` — no upload, so no answer ever exists) and the
999
- * manual-upload sub-machine (slot visibility precedes any upload, and it ORs
1000
- * `skipBackFromServer` afterwards to satisfy (2)). This guard is on neither
1001
- * path — the answer is already in hand by the time it runs.
835
+ * The captureOnly machine qualifies under (1) because it has no upload and no
836
+ * server answer. This guard is not on that path, so the answer is already in
837
+ * hand by the time it runs.
1002
838
  */
1003
839
  const shouldContinueToBackGuard = ({ context }) => {
1004
840
  if (context.currentMode !== "front" && context.currentMode !== "passport") return false;
@@ -1038,10 +874,8 @@ const supportsOptimizingStatusGuard = ({ context }) => context.config.supportsOp
1038
874
  const isDeepsightEnabledGuard = ({ context }) => context.dependencies !== void 0;
1039
875
  const isDeepsightReadyGuard = ({ context }) => context.deepsightService !== void 0;
1040
876
  const needsDeepsightInitGuard = ({ context }) => context.dependencies !== void 0 && context.deepsightService === void 0 && !context.deepsightInitAttempted;
1041
- const hasOnlyManualUploadAvailableGuard = ({ context }) => context.availableDocumentTypes.length === 1 && context.availableDocumentTypes[0] === "manualIdUpload";
1042
877
  /** No capture option is enabled — the ID module is misconfigured. */
1043
878
  const hasNoDocumentTypesAvailableGuard = ({ context }) => context.availableDocumentTypes.length === 0;
1044
- const isManualIdUploadSelectedGuard = ({ event }) => event.type === "SELECT_DOCUMENT" && event.documentType === "manualIdUpload";
1045
879
  const isDigitalIdUploadSelectedGuard = ({ event }) => event.type === "SELECT_DOCUMENT" && event.documentType === "digitalIdUpload";
1046
880
  const isDigitalIdRedirectSelectedGuard = ({ event }) => event.type === "SELECT_DOCUMENT" && event.documentType === "digitalIdRedirect";
1047
881
  /** The Digital ID (device wallet) chooser tile was picked. */
@@ -1112,237 +946,25 @@ const forceFinishAfterProcessingGuard = ({ context }) => context.forceFinishAfte
1112
946
  * `MAX_EXPIRED_FIRST_SIDE_ATTEMPTS` (decoupled for V1 parity — see ENG-46592).
1113
947
  * The two expired caps are therefore different by design.
1114
948
  */
1115
- const isProcessingExpiredExhaustedGuard = ({ context }) => context.processingExpiredAttempts + 1 >= context.config.captureAttempts;
1116
- /**
1117
- * True when capturing the back side of a two-sided (front+back) ID flow.
1118
- * Used to redirect an expired-back capture to front rather than retrying
1119
- * the same back side (Figma Story #2.1).
1120
- * Excludes onlyBack and usSmartCapture — those are single-side back flows
1121
- * with no front to redirect to.
1122
- */
1123
- const isBackModeInMultiSideFlowGuard = ({ context }) => context.currentMode === "back" && !context.config.onlyBack && !context.config.usSmartCapture;
1124
- /**
1125
- * True once the user has already been redirected from an expired second-side
1126
- * (back) capture to the first side (front) for their one extra attempt
1127
- * (V1's `expiredRedirected`). Used to make the expired-back→front redirect a
1128
- * one-shot: any subsequent expired upload routes to `expiredExhausted` instead
1129
- * of redirecting again, so total captures stay within the backend's
1130
- * max-attempts/OCR cap (ENG-46592 / re-respects ENG-45666).
1131
- */
1132
- const wasExpiredRedirectedGuard = ({ context }) => context.expiredRedirected === true;
1133
- const hasManualCaptureTriggeredGuard = ({ context }) => context.manualCaptureTriggered;
1134
- //#endregion
1135
- //#region src/modules/id/digitalIdRegistry.ts
1136
- /**
1137
- * True when a digital-ID method uses a full-page web-redirect flow (Trinsic
1138
- * iDIN/FTN). Accepts both the module `FlowType` value (`REDIRECT`) and the
1139
- * backend value (`WEB_REDIRECT`). (Lives here rather than in
1140
- * `idCaptureHelpers` to keep the registry free of circular imports; the
1141
- * helpers re-export it.)
1142
- */
1143
- function isRedirectFlowType(flowType) {
1144
- return flowType === "REDIRECT" || flowType === "WEB_REDIRECT";
1145
- }
1146
- const DIGITAL_ID_REGISTRY = {
1147
- mDL: [{
1148
- kind: "incodeWallet",
1149
- documentType: "digitalId",
1150
- configFlag: "deviceWallet"
1151
- }, {
1152
- kind: "trinsicRedirect",
1153
- documentType: "digitalIdRedirect",
1154
- providerId: "trinsic_mdl_placeholder",
1155
- method: "MDL"
1156
- }],
1157
- iDIN: [{
1158
- kind: "trinsicRedirect",
1159
- documentType: "digitalIdRedirect",
1160
- providerId: "trinsic_netherlands_idin",
1161
- method: "IDIN"
1162
- }],
1163
- FTN: [{
1164
- kind: "trinsicRedirect",
1165
- documentType: "digitalIdRedirect",
1166
- providerId: "trinsic_finnish_trust_network",
1167
- method: "FTN"
1168
- }]
1169
- };
1170
- const DIGITAL_ID_DOCUMENT_TYPES = Object.keys(DIGITAL_ID_REGISTRY);
1171
- /**
1172
- * Tolerant view over `config.digitalIds`: accepts both the mapped array
1173
- * (`IdCaptureDigitalIdMethod[]`) and the raw backend object
1174
- * (`{ enabled, methods: [...] }`). Extracted from the old
1175
- * `getDigitalIdRedirectMethod` cast so every consumer parses it the same way.
1176
- */
1177
- function normalizeDigitalIdMethods(config) {
1178
- const raw = config.digitalIds;
1179
- return Array.isArray(raw) ? raw : raw?.methods ?? [];
1180
- }
1181
- /** Whether a funnel entry is activated by config + platform capabilities. */
1182
- function isEntryActive(entry, config, platform) {
1183
- if (entry.kind === "incodeWallet") return config[entry.configFlag] === true && platform.credentialsAvailable;
1184
- return findConfiguredMethod(entry, config) !== void 0;
1185
- }
1186
- /**
1187
- * The configured `digitalIds` method matching a Trinsic funnel entry — by
1188
- * persisted method name, or (mapped shape) by provider id on a redirect
1189
- * flowType. Mirrors the old first-match semantics; `methods[].enabled` is
1190
- * deliberately still not consulted (pre-existing behavior).
1191
- */
1192
- function findConfiguredMethod(entry, config) {
1193
- return normalizeDigitalIdMethods(config).find((method) => method.method?.toUpperCase() === entry.method || method.provider === entry.providerId && !!method.flowType && isRedirectFlowType(method.flowType));
1194
- }
1195
- /** Normalizes a raw configured method into the mapped shape for the child input. */
1196
- function toMappedMethod(entry, method) {
1197
- if (method.flowType && isRedirectFlowType(method.flowType)) return method;
1198
- return {
1199
- method: method.method,
1200
- provider: entry.providerId,
1201
- flowType: "WEB_REDIRECT",
1202
- country: method.country ?? method.countries?.[0],
1203
- displayName: method.displayName
1204
- };
1205
- }
1206
- /**
1207
- * Resolves the implementation that should fulfil a digital-ID document type:
1208
- * the first funnel entry that is active under the given config and platform.
1209
- * `undefined` when nothing can run it (the chooser then shows no tile for it).
1210
- */
1211
- function resolveDigitalIdImplementation(docType, config, platform) {
1212
- for (const entry of DIGITAL_ID_REGISTRY[docType]) {
1213
- if (!isEntryActive(entry, config, platform)) continue;
1214
- if (entry.kind === "incodeWallet") return { kind: "incodeWallet" };
1215
- const method = findConfiguredMethod(entry, config);
1216
- return {
1217
- kind: "trinsicRedirect",
1218
- providerId: entry.providerId,
1219
- method: method ? toMappedMethod(entry, method) : void 0
1220
- };
1221
- }
1222
- }
1223
- //#endregion
1224
- //#region src/modules/id/idCaptureHelpers.ts
1225
- /**
1226
- * Persisted method name → catalog provider id, derived from the digital-ID
1227
- * registry's Trinsic entries (single source of truth — was a hardcoded map
1228
- * here before the registry existed).
1229
- */
1230
- const REDIRECT_PROVIDER_BY_METHOD = (() => {
1231
- const map = {};
1232
- for (const docType of DIGITAL_ID_DOCUMENT_TYPES) for (const entry of DIGITAL_ID_REGISTRY[docType]) if (entry.kind === "trinsicRedirect") map[entry.method] = entry.providerId;
1233
- return map;
1234
- })();
1235
- /**
1236
- * The first enabled web-redirect digital-ID method (iDIN-only for now), or
1237
- * `undefined` when none is configured. Drives both the `digitalIdRedirect`
1238
- * chooser option and the provider passed to the `digitalIdRedirectMachine`.
1239
- *
1240
- * Accepts both the mapped array (`IdCaptureDigitalIdMethod[]`) and the raw
1241
- * backend `configuration.digitalIds` object (`{ enabled, methods: [...] }`);
1242
- * for the raw shape it derives `provider`/`flowType` from the persisted method
1243
- * name via the digital-ID registry (`IDIN` → `trinsic_netherlands_idin`,
1244
- * `WEB_REDIRECT`).
1245
- */
1246
- function getDigitalIdRedirectMethod(config) {
1247
- for (const method of normalizeDigitalIdMethods(config)) {
1248
- if (method.flowType && isRedirectFlowType(method.flowType)) return method;
1249
- const provider = method.method ? REDIRECT_PROVIDER_BY_METHOD[method.method.toUpperCase()] : void 0;
1250
- if (provider) return {
1251
- method: method.method,
1252
- provider,
1253
- flowType: "WEB_REDIRECT",
1254
- country: method.country ?? method.countries?.[0],
1255
- displayName: method.displayName
1256
- };
1257
- }
1258
- }
1259
- /**
1260
- * Map of config properties received from the backend to adjusted, internal document types.
1261
- */
1262
- const DOCUMENT_TYPES_CONFIG_TO_ADJUSTED_MAP = [
1263
- ["enableId", "id"],
1264
- ["enablePassport", "passport"],
1265
- ["manualUploadIdCapture", "manualIdUpload"],
1266
- ["digitalIdsUpload", "digitalIdUpload"]
1267
- ];
1268
- /**
1269
- * Returns the list of document types enabled by config for the chooser screen, in display order.
1270
- * Used to populate chooser state so UI and headless consumers can render the correct options.
1271
- *
1272
- * Digital-ID document types (device wallet, Trinsic web-redirect) come from
1273
- * the digital-ID registry funnel: each registry document type contributes the
1274
- * tile of its first active implementation. A `deviceWallet: true` config on a
1275
- * platform without the Credentials API therefore shows the Trinsic fallback
1276
- * tile when one is configured — or no tile at all, instead of a dead-end one.
1277
- *
1278
- * @param config - ID capture configuration (from flow or init)
1279
- * @param platform - Platform capabilities consulted by the digital-ID funnel.
1280
- * Defaults to no Credentials API for pure-node callers; the machine passes
1281
- * the real capability check.
1282
- * @returns Array of IdDocumentType values that should be shown in the document type chooser
1283
- */
1284
- function getAvailableDocumentTypes(config, platform = { credentialsAvailable: false }) {
1285
- if (config.onlyBack && config.manualUploadIdCapture) return config.enableId ? ["id", "manualIdUpload"] : ["manualIdUpload"];
1286
- const adjustedDocumentTypes = [];
1287
- for (const [configProperty, adjustedType] of DOCUMENT_TYPES_CONFIG_TO_ADJUSTED_MAP) if (config[configProperty]) adjustedDocumentTypes.push(adjustedType);
1288
- for (const docType of DIGITAL_ID_DOCUMENT_TYPES) {
1289
- const route = resolveDigitalIdImplementation(docType, config, platform);
1290
- if (!route) continue;
1291
- const documentType = route.kind === "incodeWallet" ? "digitalId" : "digitalIdRedirect";
1292
- if (!adjustedDocumentTypes.includes(documentType)) adjustedDocumentTypes.push(documentType);
1293
- }
1294
- return adjustedDocumentTypes;
1295
- }
949
+ const isProcessingExpiredExhaustedGuard = ({ context }) => context.processingExpiredAttempts + 1 >= context.config.captureAttempts;
1296
950
  /**
1297
- * Which manual-upload tabs to show. Falls back to both tabs when neither
1298
- * `enableId` nor `enablePassport` is set (no accepted documents).
951
+ * True when capturing the back side of a two-sided (front+back) ID flow.
952
+ * Used to redirect an expired-back capture to front rather than retrying
953
+ * the same back side (Figma Story #2.1).
954
+ * Excludes onlyBack and usSmartCapture — those are single-side back flows
955
+ * with no front to redirect to.
1299
956
  */
1300
- function getManualUploadTabs(config) {
1301
- if (config.onlyBack) return {
1302
- showIdTab: true,
1303
- showPassportTab: false
1304
- };
1305
- const showIdTab = !!config.enableId;
1306
- const showPassportTab = !!config.enablePassport;
1307
- if (!showIdTab && !showPassportTab) return {
1308
- showIdTab: true,
1309
- showPassportTab: true
1310
- };
1311
- return {
1312
- showIdTab,
1313
- showPassportTab
1314
- };
1315
- }
957
+ const isBackModeInMultiSideFlowGuard = ({ context }) => context.currentMode === "back" && !context.config.onlyBack && !context.config.usSmartCapture;
1316
958
  /**
1317
- * Computes `idCaptureMachine`'s terminal output — the downstream module-skip
1318
- * signal read by the Flow orchestrator (via `runChildModule`'s `MODULE_COMPLETE`).
1319
- *
1320
- * `skipFaceMatch` is set ONLY for the US Smart Capture *camera back-only* path:
1321
- * a back image was captured on-device (`capturedImages.back`) and no front was
1322
- * (`!capturedImages.front`) — "no front, no ID face, no match".
1323
- *
1324
- * Requiring a captured back is deliberate and load-bearing: the manual-upload
1325
- * and digital-ID-upload paths submit a full ID (front face present) but do NOT
1326
- * populate `capturedImages` (only the camera path does). Gating on a captured
1327
- * back excludes those upload paths so a real ID-vs-selfie match is never
1328
- * silently skipped. `capturedImages.front`/`forceFrontIdCapture` alone are not
1329
- * enough (a later front upload clears the request; uploads set neither field).
1330
- *
1331
- * Always `false` outside US Smart Capture. The selfie/face capture itself is
1332
- * unaffected — only the ID-vs-selfie match is skipped.
1333
- *
1334
- * Also `true` on the Digital ID web-redirect (Trinsic iDIN/FTN) COMPLETED path
1335
- * when the scheme returned no portrait (`isDigitalIdRedirect && !portraitPresent`,
1336
- * FM-1/FM-2): there is no ID face to match the selfie against.
959
+ * True once the user has already been redirected from an expired second-side
960
+ * (back) capture to the first side (front) for their one extra attempt
961
+ * (V1's `expiredRedirected`). Used to make the expired-back→front redirect a
962
+ * one-shot: any subsequent expired upload routes to `expiredExhausted` instead
963
+ * of redirecting again, so total captures stay within the backend's
964
+ * max-attempts/OCR cap (ENG-46592 / re-respects ENG-45666).
1337
965
  */
1338
- function computeIdCaptureOutput(context) {
1339
- const frontIdCaptured = Boolean(context.capturedImages.front);
1340
- const backIdCaptured = Boolean(context.capturedImages.back);
1341
- return {
1342
- frontIdCaptured,
1343
- skipFaceMatch: context.isDigitalIdRedirect === true && context.portraitPresent !== true || context.config.usSmartCapture === true && backIdCaptured && !frontIdCaptured
1344
- };
1345
- }
966
+ const wasExpiredRedirectedGuard = ({ context }) => context.expiredRedirected === true;
967
+ const hasManualCaptureTriggeredGuard = ({ context }) => context.manualCaptureTriggered;
1346
968
  //#endregion
1347
969
  //#region src/modules/id/idDetectionNotificationController.ts
1348
970
  const NOTIFICATION_DURATION_MS = 500;
@@ -1418,9 +1040,26 @@ async function requestIdCapturePermission(input) {
1418
1040
  cameraFallbackLevel: fallbackLevel
1419
1041
  };
1420
1042
  }
1043
+ /**
1044
+ * Resolves and primes Deepsight motion without touching camera permission.
1045
+ * IDC-03 invokes this directly from the tutorial CTA's event macrostep so
1046
+ * iOS keeps the transient user activation required by DeviceMotion.
1047
+ */
1048
+ async function requestIdCaptureMotionPermission() {
1049
+ const result = await requestMotionPermission();
1050
+ return result === "not-required" ? "granted" : result;
1051
+ }
1421
1052
  const requestPermissionActor = fromPromise(async ({ input }) => {
1422
1053
  return requestIdCapturePermission(input);
1423
1054
  });
1055
+ /**
1056
+ * Takes no `AbortSignal` on purpose: the work it awaits is the OS-owned
1057
+ * `DeviceMotionEvent.requestPermission()` prompt, which exposes no
1058
+ * cancellation. XState still discards the settled result if the state is
1059
+ * left, and `primeWasmMotionSensor` is idempotent, so an abandoned request
1060
+ * cannot mis-drive the machine.
1061
+ */
1062
+ const requestMotionPermissionActor = fromPromise(() => requestIdCaptureMotionPermission());
1424
1063
  fromPromise(async ({ input }) => {
1425
1064
  return initializeIdCapture(input);
1426
1065
  });
@@ -1781,7 +1420,7 @@ fromPromise(async ({ input }) => {
1781
1420
  });
1782
1421
  fromPromise(async ({ input }) => {
1783
1422
  if (!input.dependencies) return;
1784
- const { loadDeepsightSession } = await import("./deepsightLoader-v-dLejS-.esm.js");
1423
+ const { loadDeepsightSession } = await import("./deepsightLoader-DZMdYSl-.esm.js");
1785
1424
  return loadDeepsightSession({
1786
1425
  ds: input.ds,
1787
1426
  storage: input.dependencies.storage,
@@ -2224,7 +1863,7 @@ function cleanup(service) {
2224
1863
  service.cleanup();
2225
1864
  } catch {}
2226
1865
  }
2227
- function createIdMetadataPreparationCoordinator(load = async (input) => (await import("./deepsightLoader-v-dLejS-.esm.js")).loadDeepsightSession(input)) {
1866
+ function createIdMetadataPreparationCoordinator(load = async (input) => (await import("./deepsightLoader-DZMdYSl-.esm.js")).loadDeepsightSession(input)) {
2228
1867
  let disposed = false;
2229
1868
  let preparation;
2230
1869
  let service;
@@ -2257,6 +1896,46 @@ function createIdMetadataPreparationCoordinator(load = async (input) => (await i
2257
1896
  };
2258
1897
  }
2259
1898
  //#endregion
1899
+ //#region src/modules/id/idCaptureOnlyConfig.ts
1900
+ /**
1901
+ * The `IdCaptureConfig` keys the captureOnly flow supports.
1902
+ */
1903
+ const ID_CAPTURE_ONLY_CONFIG_KEYS = [
1904
+ "showTutorial",
1905
+ "captureAttempts",
1906
+ "autoCaptureTimeout",
1907
+ "deviceIdleTimeout",
1908
+ "enableId",
1909
+ "enablePassport",
1910
+ "onlyFront",
1911
+ "onlyBack",
1912
+ "showDocumentChooserScreen"
1913
+ ];
1914
+ /**
1915
+ * `onCapture` marks a config as captureOnly. It is not part of
1916
+ * `IdCaptureConfig`, but JavaScript callers (and `<incode-id>`'s `config`
1917
+ * property) can still pass it.
1918
+ */
1919
+ function hasOnCaptureCallback(config) {
1920
+ return typeof config.onCapture === "function";
1921
+ }
1922
+ /** Keeps the supported keys and `onCapture`, dropping backend-only options. */
1923
+ function pickIdCaptureOnlyConfig(config) {
1924
+ return {
1925
+ ...Object.fromEntries(ID_CAPTURE_ONLY_CONFIG_KEYS.filter((key) => config[key] !== void 0).map((key) => [key, config[key]])),
1926
+ onCapture: config.onCapture
1927
+ };
1928
+ }
1929
+ /**
1930
+ * Whether the captureOnly flow needs the back side after this capture. There
1931
+ * is no backend answer (`skipBackIdCapture`), so `onlyFront` decides. Drives
1932
+ * both the `shouldContinueToBack` guard and the manager's `needsBackCapture`.
1933
+ */
1934
+ function captureOnlyNeedsBack({ currentMode, config }) {
1935
+ if (currentMode !== "front" && currentMode !== "passport") return false;
1936
+ return !config.onlyFront && !config.onlyBack && !config.usSmartCapture;
1937
+ }
1938
+ //#endregion
2260
1939
  //#region src/modules/id/idProtectedCapture.ts
2261
1940
  const ID_PROTECTED_CAPTURE_CONTROLLER = Symbol("idProtectedCaptureController");
2262
1941
  function attachIdProtectedCaptureController(owner, controller) {
@@ -2294,12 +1973,12 @@ function streamUsesBackCamera(stream) {
2294
1973
  const track = stream.getVideoTracks()[0];
2295
1974
  if (!track) return;
2296
1975
  const settings = typeof track.getSettings === "function" ? track.getSettings() : {};
2297
- const constrainedFacingMode = typeof track.getConstraints === "function" ? track.getConstraints().facingMode : void 0;
1976
+ const constrainedFacingMode = getDeviceClass() !== "desktop" && typeof track.getConstraints === "function" ? track.getConstraints().facingMode : void 0;
2298
1977
  const facingMode = settings.facingMode ?? (typeof constrainedFacingMode === "string" ? constrainedFacingMode : void 0);
2299
1978
  if (facingMode === "environment") return true;
2300
1979
  if (facingMode === "user") return false;
2301
1980
  const label = typeof track.label === "string" ? track.label.toLowerCase() : "";
2302
- if (label.includes("back") || label.includes("rear")) return true;
1981
+ if (isBackCameraLabel(label)) return true;
2303
1982
  if (label.includes("front") || label.includes("user")) return false;
2304
1983
  }
2305
1984
  function createIdProtectedCaptureController(dependencies = {}) {
@@ -2400,6 +2079,7 @@ function createIdProtectedCaptureController(dependencies = {}) {
2400
2079
  };
2401
2080
  const clearAttempt = () => {
2402
2081
  if (diagnosticAttempt) logDiagnostic("attempt-release", { committed });
2082
+ noteTrueSightCaptureActivity(false);
2403
2083
  attemptGeneration += 1;
2404
2084
  frameCapturer?.dispose();
2405
2085
  frameCapturer = void 0;
@@ -2480,6 +2160,7 @@ function createIdProtectedCaptureController(dependencies = {}) {
2480
2160
  provider = (dependencies.createProvider ?? createProtectedIdProvider)(context.config, context.currentMode);
2481
2161
  cycleOwnership = createCameraCycleOwnership(cameraCycleProvider);
2482
2162
  logDiagnostic("attempt-start", { aggregationMs: context.config?.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval });
2163
+ noteTrueSightCaptureActivity(true);
2483
2164
  },
2484
2165
  startMetadataPreparation(context) {
2485
2166
  const storage = context.dependencies?.storage;
@@ -2654,6 +2335,7 @@ function createIdProtectedCaptureController(dependencies = {}) {
2654
2335
  autoCommitted: storeCommit,
2655
2336
  manualCommitted: storeCommit,
2656
2337
  finalized(_recording, stream) {
2338
+ noteTrueSightCaptureActivity(false);
2657
2339
  if (!stream || disposed) {
2658
2340
  logDiagnostic("attempt-finalized");
2659
2341
  return;
@@ -2896,6 +2578,8 @@ function createProtectedIdCallbackSession(input) {
2896
2578
  let countdownTimers = [];
2897
2579
  let qualityElements;
2898
2580
  let isCapturing = false;
2581
+ let active = true;
2582
+ let paused = false;
2899
2583
  let lastReportedSide = null;
2900
2584
  let blurSignals = 0;
2901
2585
  let bestFrameSignals = 0;
@@ -2922,7 +2606,7 @@ function createProtectedIdCallbackSession(input) {
2922
2606
  };
2923
2607
  const startCountdown = () => {
2924
2608
  clearCountdown();
2925
- const stepMs = (input.context.config.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval) / 3;
2609
+ const stepMs = resolveCountdownStepMs(input.context.config);
2926
2610
  input.send({
2927
2611
  type: "COUNTER_VALUE_CHANGE",
2928
2612
  value: 3
@@ -2937,6 +2621,7 @@ function createProtectedIdCallbackSession(input) {
2937
2621
  }), stepMs * (index + 1)));
2938
2622
  };
2939
2623
  const setDetectionStatus = (status) => {
2624
+ trueSightHintTransition("module.id-capture", "idScan", status);
2940
2625
  input.send({
2941
2626
  type: "DETECTION_UPDATE",
2942
2627
  status
@@ -2946,114 +2631,139 @@ function createProtectedIdCallbackSession(input) {
2946
2631
  send: setDetectionStatus,
2947
2632
  isSuppressed: () => isReportedIdSideWrong(input.context.currentMode, lastReportedSide)
2948
2633
  });
2949
- return {
2950
- callbacks: {
2951
- onFarAway: () => {
2952
- if (!isCapturing) notifications.show("farAway");
2953
- },
2954
- onDetectionStarted: () => {},
2955
- onMaskChange: (_show, _mask, _top, orientation) => {
2956
- input.send({
2957
- type: "ORIENTATION_CHANGE",
2958
- orientation
2959
- });
2960
- },
2961
- onBlur: () => {
2962
- blurSignals += 1;
2963
- if (!isCapturing) notifications.show("blur");
2964
- },
2965
- onGlare: () => {
2966
- if (!isCapturing) notifications.show("glare");
2967
- },
2968
- onIdNotDetected: () => {
2969
- if (!isCapturing) setDetectionStatus("idNotDetected");
2970
- },
2971
- onSwitchToManualCapture: () => {
2972
- isCapturing = false;
2973
- notifications.clear();
2974
- clearOptimizingBoundary();
2975
- clearCountdown();
2976
- input.send({ type: input.controller.hasCommit() ? "PROTECTED_RELEASE_STASHED_COMMIT" : "SWITCH_TO_MANUAL_CAPTURE" });
2977
- },
2978
- onCapturing: () => {
2979
- const wasCapturing = isCapturing;
2980
- notifications.clear();
2981
- lastReportedSide = null;
2982
- isCapturing = true;
2983
- setDetectionStatus("capturing");
2984
- if (!wasCapturing) logDiagnostic("capture-window", {
2985
- aggregationMs: input.context.config.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval,
2986
- blurSignals
2987
- });
2988
- if (!wasCapturing && input.context.captureFeedbackMode === "countdown") startCountdown();
2989
- if (input.context.captureFeedbackMode === "optimizing" && input.context.config.supportsOptimizingStatus === true && input.context.captureWindowMs != null) {
2990
- clearOptimizingBoundary();
2991
- optimizingBoundary = timer.setTimeout(() => {
2992
- optimizingBoundary = null;
2993
- const presentation = input.controller.getPresentationCanvas();
2994
- if (presentation) {
2995
- presentation.release();
2996
- input.send({ type: "PROTECTED_OPTIMIZING" });
2997
- }
2998
- }, input.context.captureWindowMs);
2999
- }
3000
- },
3001
- onBestFrame: (blur, glare, orientation) => {
3002
- bestFrameSignals += 1;
3003
- bestBlur = bestBlur === void 0 ? blur : Math.min(bestBlur, blur);
3004
- qualityElements = {
3005
- glare,
3006
- sharpness: blur
3007
- };
3008
- if (orientation === "horizontal" || orientation === "vertical") input.send({
3009
- type: "ORIENTATION_CHANGE",
3010
- orientation
3011
- });
3012
- },
3013
- onCapture: (canvas) => {
3014
- isCapturing = false;
2634
+ const callbacks = {
2635
+ onFarAway: () => {
2636
+ if (!active || paused) return;
2637
+ if (!isCapturing) notifications.show("farAway");
2638
+ },
2639
+ onDetectionStarted: () => {},
2640
+ onMaskChange: (_show, _mask, _top, orientation) => {
2641
+ if (!active || paused) return;
2642
+ input.send({
2643
+ type: "ORIENTATION_CHANGE",
2644
+ orientation
2645
+ });
2646
+ },
2647
+ onBlur: () => {
2648
+ if (!active || paused) return;
2649
+ blurSignals += 1;
2650
+ if (!isCapturing) notifications.show("blur");
2651
+ },
2652
+ onGlare: () => {
2653
+ if (!active || paused) return;
2654
+ if (!isCapturing) notifications.show("glare");
2655
+ },
2656
+ onIdNotDetected: () => {
2657
+ if (!active || paused) return;
2658
+ if (!isCapturing) setDetectionStatus("idNotDetected");
2659
+ },
2660
+ onSwitchToManualCapture: () => {
2661
+ if (!active || paused) return;
2662
+ isCapturing = false;
2663
+ notifications.clear();
2664
+ clearOptimizingBoundary();
2665
+ clearCountdown();
2666
+ input.send({ type: input.controller.hasCommit() ? "PROTECTED_RELEASE_STASHED_COMMIT" : "SWITCH_TO_MANUAL_CAPTURE" });
2667
+ },
2668
+ onCapturing: () => {
2669
+ if (!active || paused) return;
2670
+ const wasCapturing = isCapturing;
2671
+ notifications.clear();
2672
+ lastReportedSide = null;
2673
+ isCapturing = true;
2674
+ setDetectionStatus("capturing");
2675
+ if (!wasCapturing) logDiagnostic("capture-window", {
2676
+ aggregationMs: input.context.config.thresholds?.framesAggregationInterval ?? DEFAULT_ID_CAPTURE_THRESHOLDS.framesAggregationInterval,
2677
+ blurSignals
2678
+ });
2679
+ if (!wasCapturing && input.context.captureFeedbackMode === "countdown") startCountdown();
2680
+ if (input.context.captureFeedbackMode === "optimizing" && input.context.config.supportsOptimizingStatus === true && input.context.captureWindowMs != null) {
3015
2681
  clearOptimizingBoundary();
3016
- clearCountdown();
3017
- logDiagnostic("capture-selected", {
3018
- blurSignals,
3019
- bestFrameSignals,
3020
- bestBlur,
3021
- selectedBlur: qualityElements?.sharpness,
3022
- selectedGlare: qualityElements?.glare
3023
- });
3024
- input.controller.storeAutoCaptureFrame(canvas);
3025
- input.send({
3026
- type: "PROTECTED_AUTO_CAPTURE",
3027
- qualityElements
3028
- });
3029
- },
3030
- onIdTypeChange: (idType) => {
3031
- input.send({
3032
- type: "ID_TYPE_CHANGE",
3033
- idType
3034
- });
3035
- },
3036
- onIdSideChange: (side) => {
3037
- if (isCapturing) return;
3038
- lastReportedSide = side;
3039
- if (isReportedIdSideWrong(input.context.currentMode, lastReportedSide)) notifications.clear();
3040
- input.send({
3041
- type: "ID_SIDE_CHANGE",
3042
- side
3043
- });
3044
- },
3045
- onCapturingCounterValueChange: () => {},
3046
- onBarcodeDecoded: (decoded) => {
3047
- input.send({
3048
- type: "BARCODE_DECODED",
3049
- decoded
3050
- });
2682
+ optimizingBoundary = timer.setTimeout(() => {
2683
+ optimizingBoundary = null;
2684
+ const presentation = input.controller.getPresentationCanvas();
2685
+ if (presentation) {
2686
+ presentation.release();
2687
+ input.send({ type: "PROTECTED_OPTIMIZING" });
2688
+ }
2689
+ }, input.context.captureWindowMs);
3051
2690
  }
3052
2691
  },
3053
- dispose: () => {
3054
- notifications.clear();
2692
+ onBestFrame: (blur, glare, orientation) => {
2693
+ if (!active || paused) return;
2694
+ bestFrameSignals += 1;
2695
+ bestBlur = bestBlur === void 0 ? blur : Math.min(bestBlur, blur);
2696
+ qualityElements = {
2697
+ glare,
2698
+ sharpness: blur
2699
+ };
2700
+ if (orientation === "horizontal" || orientation === "vertical") input.send({
2701
+ type: "ORIENTATION_CHANGE",
2702
+ orientation
2703
+ });
2704
+ },
2705
+ onCapture: (canvas) => {
2706
+ if (!active || paused) return;
2707
+ isCapturing = false;
3055
2708
  clearOptimizingBoundary();
3056
2709
  clearCountdown();
2710
+ logDiagnostic("capture-selected", {
2711
+ blurSignals,
2712
+ bestFrameSignals,
2713
+ bestBlur,
2714
+ selectedBlur: qualityElements?.sharpness,
2715
+ selectedGlare: qualityElements?.glare
2716
+ });
2717
+ input.controller.storeAutoCaptureFrame(canvas);
2718
+ input.send({
2719
+ type: "PROTECTED_AUTO_CAPTURE",
2720
+ qualityElements
2721
+ });
2722
+ },
2723
+ onIdTypeChange: (idType) => {
2724
+ if (!active || paused) return;
2725
+ input.send({
2726
+ type: "ID_TYPE_CHANGE",
2727
+ idType
2728
+ });
2729
+ },
2730
+ onIdSideChange: (side) => {
2731
+ if (!active || paused) return;
2732
+ if (isCapturing) return;
2733
+ lastReportedSide = side;
2734
+ if (isReportedIdSideWrong(input.context.currentMode, lastReportedSide)) notifications.clear();
2735
+ input.send({
2736
+ type: "ID_SIDE_CHANGE",
2737
+ side
2738
+ });
2739
+ },
2740
+ onCapturingCounterValueChange: () => {},
2741
+ onBarcodeDecoded: (decoded) => {
2742
+ if (!active || paused) return;
2743
+ input.send({
2744
+ type: "BARCODE_DECODED",
2745
+ decoded
2746
+ });
2747
+ }
2748
+ };
2749
+ const pause = () => {
2750
+ paused = true;
2751
+ notifications.clear();
2752
+ clearOptimizingBoundary();
2753
+ clearCountdown();
2754
+ isCapturing = false;
2755
+ qualityElements = void 0;
2756
+ lastReportedSide = null;
2757
+ };
2758
+ return {
2759
+ callbacks,
2760
+ pause,
2761
+ resume: () => {
2762
+ paused = false;
2763
+ },
2764
+ dispose: () => {
2765
+ active = false;
2766
+ pause();
3057
2767
  }
3058
2768
  };
3059
2769
  }
@@ -3068,7 +2778,7 @@ function createProtectedIdAttemptInput(input) {
3068
2778
  const preAcquired = controller.takePreAcquiredStream();
3069
2779
  let selectedVideoTrack = preAcquired?.stream.getVideoTracks()[0];
3070
2780
  const constraints = getCameraReacquisitionConstraints(selectedVideoTrack, preAcquired?.constraints);
3071
- const captureOnly = "onCapture" in context.config && typeof context.config.onCapture === "function";
2781
+ const captureOnly = hasOnCaptureCallback(context.config);
3072
2782
  const diagnosticAttempt = controller.getDiagnosticAttempt();
3073
2783
  const isAttemptActive = () => !controller.disposed && controller.getProvider() === provider && controller.getDiagnosticAttempt() === diagnosticAttempt;
3074
2784
  const logSelectedCamera = (stream, fallbackLevel, reacquisitionConstraints, preAcquiredStream) => {
@@ -3137,6 +2847,11 @@ function createProtectedIdAttemptInput(input) {
3137
2847
  ...timing
3138
2848
  });
3139
2849
  },
2850
+ pauseDetection: callbackSession.pause,
2851
+ resumeDetection(reset) {
2852
+ if (reset) provider.resetDetectionState();
2853
+ callbackSession.resume();
2854
+ },
3140
2855
  async configureWorkflow() {
3141
2856
  await provider.initialize({});
3142
2857
  configureIdCaptureProvider({
@@ -3199,52 +2914,383 @@ function createProtectedIdAttemptInput(input) {
3199
2914
  }
3200
2915
  };
3201
2916
  }
3202
- function protectedIdResultToCaptureResponse(result) {
3203
- 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;
2917
+ function protectedIdResultToCaptureResponse(result) {
2918
+ 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;
2919
+ return {
2920
+ sharpness: result.sharpness ?? 0,
2921
+ correctSharpness: result.sharpness != null && result.sharpness >= 10,
2922
+ glare: result.glare ?? 0,
2923
+ correctGlare: result.glare != null && result.glare >= 10,
2924
+ horizontalResolution: 0,
2925
+ classification: result.classification === true,
2926
+ readability: result.readability === true,
2927
+ typeOfId: result.typeOfId ?? "",
2928
+ issueYear: 0,
2929
+ issueName: "",
2930
+ sessionStatus: result.sessionStatus ?? "",
2931
+ countryCode: result.countryCode ?? "",
2932
+ skipBackIdCapture: result.skipBackIdCapture,
2933
+ skipGlareFront: result.skipGlareFront,
2934
+ skipGlareBack: result.skipGlareBack,
2935
+ acceptedDocuments: result.acceptedDocuments ? [...result.acceptedDocuments] : void 0,
2936
+ forceFrontIdCapture: result.forceFrontIdCapture,
2937
+ idValidationCompleted: result.idValidationCompleted,
2938
+ showMandatoryConsent: result.showMandatoryConsent,
2939
+ regulationType,
2940
+ failReason: result.failReason,
2941
+ side: result.side,
2942
+ idQualityAttemptApproved: result.idQualityAttemptApproved,
2943
+ isDocumentExpired: result.isDocumentExpired,
2944
+ success: result.success
2945
+ };
2946
+ }
2947
+ function getProtectedIdUploadRequest(context, sessionToken = getToken()) {
2948
+ const target = resolveProtectedIdTarget(context.config, context.currentMode);
2949
+ const front = context.currentMode !== "back";
2950
+ const params = { captureType: context.manualCaptureTriggered ? "MANUAL" : "AUTO" };
2951
+ if (front && context.config.onlyFront === true) params.onlyFront = true;
2952
+ if (front && context.config.extractIdFace === false) params.extractIdFace = false;
2953
+ const sharpness = context.qualityElements?.sharpness;
2954
+ if (sharpness !== void 0 && sharpness !== 0 && Number.isFinite(sharpness)) params.sharpness = getRealQualityValue(sharpness);
2955
+ return {
2956
+ target,
2957
+ config: {
2958
+ ...sessionToken ? { headers: { "X-Incode-Hardware-Id": sessionToken } } : {},
2959
+ params
2960
+ }
2961
+ };
2962
+ }
2963
+ //#endregion
2964
+ //#region src/modules/id/digitalIdRegistry.ts
2965
+ /**
2966
+ * True when a digital-ID method uses a full-page web-redirect flow (Trinsic
2967
+ * iDIN/FTN). Accepts both the module `FlowType` value (`REDIRECT`) and the
2968
+ * backend value (`WEB_REDIRECT`). (Lives here rather than in
2969
+ * `idCaptureHelpers` to keep the registry free of circular imports; the
2970
+ * helpers re-export it.)
2971
+ */
2972
+ function isRedirectFlowType(flowType) {
2973
+ return flowType === "REDIRECT" || flowType === "WEB_REDIRECT";
2974
+ }
2975
+ const DIGITAL_ID_REGISTRY = {
2976
+ mDL: [{
2977
+ kind: "incodeWallet",
2978
+ documentType: "digitalId"
2979
+ }, {
2980
+ kind: "trinsicRedirect",
2981
+ documentType: "digitalIdRedirect",
2982
+ providerId: "trinsic_mdl_placeholder",
2983
+ method: "MDL"
2984
+ }],
2985
+ iDIN: [{
2986
+ kind: "trinsicRedirect",
2987
+ documentType: "digitalIdRedirect",
2988
+ providerId: "trinsic_netherlands_idin",
2989
+ method: "IDIN"
2990
+ }],
2991
+ FTN: [{
2992
+ kind: "trinsicRedirect",
2993
+ documentType: "digitalIdRedirect",
2994
+ providerId: "trinsic_finnish_trust_network",
2995
+ method: "FTN"
2996
+ }]
2997
+ };
2998
+ const DIGITAL_ID_DOCUMENT_TYPES = Object.keys(DIGITAL_ID_REGISTRY);
2999
+ /**
3000
+ * Single place that widens `config.digitalIds` to the shapes the backend
3001
+ * actually sends — the mapped array or the raw `{ enabled, methods }` object.
3002
+ * Shared so the cast is written once.
3003
+ */
3004
+ function asDigitalIdsConfig(config) {
3005
+ return config.digitalIds;
3006
+ }
3007
+ /**
3008
+ * Persisted method name → catalog provider id, derived from the registry's
3009
+ * Trinsic entries (single source of truth). Lets the SDK launch iDIN / FTN
3010
+ * from a backend that does not yet send `providerId` on each method.
3011
+ * TODO(roadmap: fully-catalog-driven): remove once every backend enriches
3012
+ * `digitalIds.methods[]` with `providerId` + `flowType`.
3013
+ */
3014
+ const REDIRECT_PROVIDER_BY_METHOD = (() => {
3015
+ const map = {};
3016
+ for (const docType of DIGITAL_ID_DOCUMENT_TYPES) for (const entry of DIGITAL_ID_REGISTRY[docType]) if (entry.kind === "trinsicRedirect") map[entry.method] = entry.providerId;
3017
+ return map;
3018
+ })();
3019
+ /**
3020
+ * Redirect providers that are the fallback leg of a wallet-first funnel
3021
+ * (mDL): they are reached through the `digitalId` tile's funnel resolution,
3022
+ * not offered as a standalone chooser card.
3023
+ */
3024
+ const WALLET_FUNNEL_REDIRECT_PROVIDER_IDS = new Set(DIGITAL_ID_DOCUMENT_TYPES.flatMap((docType) => {
3025
+ const entries = DIGITAL_ID_REGISTRY[docType];
3026
+ if (!entries.some((entry) => entry.kind === "incodeWallet")) return [];
3027
+ return entries.flatMap((entry) => entry.kind === "trinsicRedirect" ? [entry.providerId] : []);
3028
+ }));
3029
+ /**
3030
+ * True for a redirect provider that gets its own chooser card (iDIN, FTN, any
3031
+ * catalog-enriched redirect method) — as opposed to the mDL wallet fallback,
3032
+ * which only surfaces through the Digital ID tile.
3033
+ */
3034
+ function isStandaloneRedirectProvider(providerId) {
3035
+ return !WALLET_FUNNEL_REDIRECT_PROVIDER_IDS.has(providerId);
3036
+ }
3037
+ /**
3038
+ * The provider id a configured method launches with, when it is a
3039
+ * web-redirect method; `undefined` for wallets / other flow shapes.
3040
+ *
3041
+ * Resolution order:
3042
+ * 1. an explicit redirect `flowType` with a `provider` (mapped shape) or
3043
+ * `providerId` (catalog-enriched raw shape) — backend-authoritative;
3044
+ * 2. otherwise the persisted method name via the registry (legacy raw shape).
3045
+ */
3046
+ function resolveRedirectProviderId(method) {
3047
+ if (method.flowType) {
3048
+ if (!isRedirectFlowType(method.flowType)) return;
3049
+ const explicit = method.provider ?? method.providerId;
3050
+ if (explicit) return explicit;
3051
+ }
3052
+ return method.method ? REDIRECT_PROVIDER_BY_METHOD[method.method.toUpperCase()] : void 0;
3053
+ }
3054
+ /**
3055
+ * Tolerant view over `config.digitalIds`: accepts both the mapped array
3056
+ * (`IdCaptureDigitalIdMethod[]`) and the raw backend object
3057
+ * (`{ enabled, methods: [...] }`). Extracted from the old
3058
+ * `getDigitalIdRedirectMethod` cast so every consumer parses it the same way.
3059
+ *
3060
+ * Honors both dashboard toggles: a raw object with `enabled: false` yields no
3061
+ * methods, and individual methods with `enabled: false` are dropped (absent
3062
+ * `enabled` counts as on). This is what "enabled for the flow / workflow"
3063
+ * means — for the redirect chooser cards as much as for the wallet tile.
3064
+ */
3065
+ function normalizeDigitalIdMethods(config) {
3066
+ const raw = asDigitalIdsConfig(config);
3067
+ if (!Array.isArray(raw) && raw?.enabled === false) return [];
3068
+ return (Array.isArray(raw) ? raw : raw?.methods ?? []).filter((method) => method.enabled !== false);
3069
+ }
3070
+ /**
3071
+ * Persisted method names of the Incode-native device wallets — the backend
3072
+ * `DigitalIdMethodCatalog` entries of type `MDL`. A catalog-enriched method
3073
+ * carries `type: 'MDL'` instead of relying on the name; both are honoured.
3074
+ */
3075
+ const DEVICE_WALLET_METHODS = /* @__PURE__ */ new Set([
3076
+ "APPLE_WALLET",
3077
+ "GOOGLE_WALLET",
3078
+ "CA_DMV_WALLET"
3079
+ ]);
3080
+ /**
3081
+ * True for a configured method the native device-wallet handshake can fulfil.
3082
+ * Redirect methods (iDIN, FTN) never count, whatever their name or type — they
3083
+ * must not surface a wallet tile with no wallet behind it.
3084
+ *
3085
+ * Classifies by *kind* only. Whether a method is switched on is already
3086
+ * settled by `normalizeDigitalIdMethods`, which drops disabled methods before
3087
+ * any caller walks the list.
3088
+ */
3089
+ function isDeviceWalletMethod(method) {
3090
+ if (method.flowType && isRedirectFlowType(method.flowType)) return false;
3091
+ if (method.type?.toUpperCase() === "MDL") return true;
3092
+ return method.method !== void 0 && DEVICE_WALLET_METHODS.has(method.method.toUpperCase());
3093
+ }
3094
+ /**
3095
+ * Whether a device wallet is enabled for this ID-capture config: the coarse
3096
+ * `deviceWallet` flag, or an enabled wallet method in `config.digitalIds`.
3097
+ *
3098
+ * The dashboard persists a wallet only in `digitalIds.methods` and leaves the
3099
+ * legacy `deviceWallet` / `appleWallet` booleans at their stored `false`. The
3100
+ * backend derives the booleans from `digitalIds` for the flow response, but
3101
+ * the workflow-node response historically shipped them raw — so a workflow
3102
+ * with Apple Wallet enabled arrived here as `deviceWallet: false`, and the
3103
+ * tile was dropped while SDK 1 (which reads `digitalIds` itself) showed it.
3104
+ * Mirrors the backend's own `deriveDeviceWalletFlag`, minus its "unknown
3105
+ * method counts as a wallet" fallback: an unrecognised method must not
3106
+ * launch a handshake that cannot know what to request.
3107
+ */
3108
+ function isDeviceWalletEnabled(config) {
3109
+ if (config.deviceWallet === true) return true;
3110
+ return hasEnabledDeviceWalletMethod(config);
3111
+ }
3112
+ /** Whether a funnel entry is activated by config + platform capabilities. */
3113
+ function isEntryActive(entry, config, platform) {
3114
+ if (entry.kind === "incodeWallet") return isDeviceWalletEnabled(config) && platform.credentialsAvailable;
3115
+ return findConfiguredMethod(entry, config) !== void 0;
3116
+ }
3117
+ /**
3118
+ * The configured `digitalIds` method matching a Trinsic funnel entry — by
3119
+ * persisted method name, or (mapped shape) by provider id on a redirect
3120
+ * flowType. First match wins; disabled methods are already dropped by
3121
+ * `normalizeDigitalIdMethods`.
3122
+ */
3123
+ function findConfiguredMethod(entry, config) {
3124
+ return normalizeDigitalIdMethods(config).find((method) => method.method?.toUpperCase() === entry.method || method.provider === entry.providerId && !!method.flowType && isRedirectFlowType(method.flowType));
3125
+ }
3126
+ /** Normalizes a raw configured method into the mapped shape for the child input. */
3127
+ function toMappedMethod(entry, method) {
3128
+ if (method.flowType && isRedirectFlowType(method.flowType)) return method;
3129
+ return {
3130
+ method: method.method,
3131
+ provider: entry.providerId,
3132
+ flowType: "WEB_REDIRECT",
3133
+ country: method.country ?? method.countries?.[0],
3134
+ displayName: method.displayName,
3135
+ logoUrl: method.logoUrl
3136
+ };
3137
+ }
3138
+ /**
3139
+ * True when `config.digitalIds` carries a device-wallet method that is switched
3140
+ * on, honouring both dashboard toggles: the `digitalIds.enabled` master switch
3141
+ * and each method's own `enabled` flag. An absent `enabled` counts as on, the
3142
+ * tolerant reading the rest of this module uses.
3143
+ *
3144
+ * Both toggles are applied by `normalizeDigitalIdMethods`, so this only has to
3145
+ * ask whether any surviving method is a wallet.
3146
+ */
3147
+ function hasEnabledDeviceWalletMethod(config) {
3148
+ return normalizeDigitalIdMethods(config).some(isDeviceWalletMethod);
3149
+ }
3150
+ /**
3151
+ * Resolves the implementation that should fulfil a digital-ID document type:
3152
+ * the first funnel entry that is active under the given config and platform.
3153
+ * `undefined` when nothing can run it (the chooser then shows no tile for it).
3154
+ */
3155
+ function resolveDigitalIdImplementation(docType, config, platform) {
3156
+ for (const entry of DIGITAL_ID_REGISTRY[docType]) {
3157
+ if (!isEntryActive(entry, config, platform)) continue;
3158
+ if (entry.kind === "incodeWallet") return { kind: "incodeWallet" };
3159
+ const method = findConfiguredMethod(entry, config);
3160
+ return {
3161
+ kind: "trinsicRedirect",
3162
+ providerId: entry.providerId,
3163
+ method: method ? toMappedMethod(entry, method) : void 0
3164
+ };
3165
+ }
3166
+ }
3167
+ //#endregion
3168
+ //#region src/modules/id/idCaptureHelpers.ts
3169
+ /**
3170
+ * Normalizes a configured method into the mapped redirect shape for a
3171
+ * resolved provider. The already-mapped shape is returned as-is; the raw
3172
+ * backend shape gets `provider` + `WEB_REDIRECT` filled in.
3173
+ */
3174
+ function toRedirectMethod(method, provider) {
3175
+ if (method.provider && method.flowType) return method;
3204
3176
  return {
3205
- sharpness: result.sharpness ?? 0,
3206
- correctSharpness: result.sharpness != null && result.sharpness >= 10,
3207
- glare: result.glare ?? 0,
3208
- correctGlare: result.glare != null && result.glare >= 10,
3209
- horizontalResolution: 0,
3210
- classification: result.classification === true,
3211
- readability: result.readability === true,
3212
- typeOfId: result.typeOfId ?? "",
3213
- issueYear: 0,
3214
- issueName: "",
3215
- sessionStatus: result.sessionStatus ?? "",
3216
- countryCode: result.countryCode ?? "",
3217
- skipBackIdCapture: result.skipBackIdCapture,
3218
- skipGlareFront: result.skipGlareFront,
3219
- skipGlareBack: result.skipGlareBack,
3220
- acceptedDocuments: result.acceptedDocuments ? [...result.acceptedDocuments] : void 0,
3221
- forceFrontIdCapture: result.forceFrontIdCapture,
3222
- idValidationCompleted: result.idValidationCompleted,
3223
- showMandatoryConsent: result.showMandatoryConsent,
3224
- regulationType,
3225
- failReason: result.failReason,
3226
- side: result.side,
3227
- idQualityAttemptApproved: result.idQualityAttemptApproved,
3228
- isDocumentExpired: result.isDocumentExpired,
3229
- success: result.success
3177
+ method: method.method,
3178
+ provider,
3179
+ flowType: "WEB_REDIRECT",
3180
+ country: method.country ?? method.countries?.[0],
3181
+ displayName: method.displayName,
3182
+ logoUrl: method.logoUrl
3230
3183
  };
3231
3184
  }
3232
- function getProtectedIdUploadRequest(context, sessionToken = getToken()) {
3233
- const target = resolveProtectedIdTarget(context.config, context.currentMode);
3234
- const front = context.currentMode !== "back";
3235
- const params = {};
3236
- if (front && context.config.onlyFront === true) params.onlyFront = true;
3237
- if (front && context.config.extractIdFace === false) params.extractIdFace = false;
3238
- const sharpness = context.qualityElements?.sharpness;
3239
- if (sharpness !== void 0 && sharpness !== 0 && Number.isFinite(sharpness)) params.sharpness = getRealQualityValue(sharpness);
3240
- const config = {
3241
- ...sessionToken ? { headers: { "X-Incode-Hardware-Id": sessionToken } } : {},
3242
- ...Object.keys(params).length > 0 ? { params } : {}
3185
+ /**
3186
+ * The enabled web-redirect digital-ID method to launch — the one matching
3187
+ * `providerId` when given (a chooser card was picked), else the first
3188
+ * configured redirect method — or `undefined` when none is configured.
3189
+ * Drives the provider and country passed to the `digitalIdRedirectMachine`.
3190
+ *
3191
+ * Accepts both the mapped array (`IdCaptureDigitalIdMethod[]`) and the raw
3192
+ * backend `configuration.digitalIds` object (`{ enabled, methods: [...] }`);
3193
+ * for the raw shape the provider comes from the backend-enriched
3194
+ * `providerId`/`flowType` when present, else from the persisted method name
3195
+ * via the digital-ID registry (`IDIN` → `trinsic_netherlands_idin`).
3196
+ */
3197
+ function getDigitalIdRedirectMethod(config, providerId) {
3198
+ for (const method of normalizeDigitalIdMethods(config)) {
3199
+ const provider = resolveRedirectProviderId(method);
3200
+ if (!provider || providerId !== void 0 && provider !== providerId) continue;
3201
+ return toRedirectMethod(method, provider);
3202
+ }
3203
+ }
3204
+ /**
3205
+ * The digital-ID web-redirect providers that get their own chooser card: one
3206
+ * per enabled redirect method in `config.digitalIds`, in backend (dashboard)
3207
+ * order, deduped by provider. Wallet-funnel fallbacks (the Trinsic mDL leg)
3208
+ * are excluded — they surface through the Digital ID tile instead.
3209
+ *
3210
+ * Card display data (`displayName`, `logoUrl`, `country`) is whatever the
3211
+ * backend enriched the method with; the UI falls back to generic copy / icon
3212
+ * when absent.
3213
+ */
3214
+ function getAvailableDigitalIdRedirectOptions(config) {
3215
+ const options = [];
3216
+ for (const method of normalizeDigitalIdMethods(config)) {
3217
+ const providerId = resolveRedirectProviderId(method);
3218
+ if (!providerId || !isStandaloneRedirectProvider(providerId) || options.some((option) => option.providerId === providerId)) continue;
3219
+ options.push({
3220
+ providerId,
3221
+ method: method.method,
3222
+ displayName: method.displayName,
3223
+ logoUrl: method.logoUrl,
3224
+ country: method.country ?? method.countries?.[0]
3225
+ });
3226
+ }
3227
+ return options;
3228
+ }
3229
+ /**
3230
+ * Map of config properties received from the backend to adjusted, internal document types.
3231
+ */
3232
+ const DOCUMENT_TYPES_CONFIG_TO_ADJUSTED_MAP = [
3233
+ ["enableId", "id"],
3234
+ ["enablePassport", "passport"],
3235
+ ["digitalIdsUpload", "digitalIdUpload"]
3236
+ ];
3237
+ /**
3238
+ * Returns the list of document types enabled by config for the chooser screen, in display order.
3239
+ * Used to populate chooser state so UI and headless consumers can render the correct options.
3240
+ *
3241
+ * Digital-ID document types (device wallet, Trinsic web-redirect) come from
3242
+ * the digital-ID registry funnel: each registry document type contributes the
3243
+ * tile of its first active implementation. A `deviceWallet: true` config on a
3244
+ * platform without the Credentials API therefore shows the Trinsic fallback
3245
+ * tile when one is configured — or no tile at all, instead of a dead-end one.
3246
+ *
3247
+ * @param config - ID capture configuration (from flow or init)
3248
+ * @param platform - Platform capabilities consulted by the digital-ID funnel.
3249
+ * Defaults to no Credentials API for pure-node callers; the machine passes
3250
+ * the real capability check.
3251
+ * @returns Array of IdDocumentType values that should be shown in the document type chooser
3252
+ */
3253
+ function getAvailableDocumentTypes(config, platform = { credentialsAvailable: false }) {
3254
+ const adjustedDocumentTypes = [];
3255
+ for (const [configProperty, adjustedType] of DOCUMENT_TYPES_CONFIG_TO_ADJUSTED_MAP) if (config[configProperty]) adjustedDocumentTypes.push(adjustedType);
3256
+ for (const docType of DIGITAL_ID_DOCUMENT_TYPES) {
3257
+ const route = resolveDigitalIdImplementation(docType, config, platform);
3258
+ if (!route) continue;
3259
+ const documentType = route.kind === "incodeWallet" ? "digitalId" : "digitalIdRedirect";
3260
+ if (!adjustedDocumentTypes.includes(documentType)) adjustedDocumentTypes.push(documentType);
3261
+ }
3262
+ if (!adjustedDocumentTypes.includes("digitalIdRedirect") && getAvailableDigitalIdRedirectOptions(config).length > 0) adjustedDocumentTypes.push("digitalIdRedirect");
3263
+ return adjustedDocumentTypes;
3264
+ }
3265
+ /**
3266
+ * Computes `idCaptureMachine`'s terminal output — the downstream module-skip
3267
+ * signal read by the Flow orchestrator (via `runChildModule`'s `MODULE_COMPLETE`).
3268
+ *
3269
+ * `skipFaceMatch` is set ONLY for the US Smart Capture *camera back-only* path:
3270
+ * a back image was captured on-device (`capturedImages.back`) and no front was
3271
+ * (`!capturedImages.front`) — "no front, no ID face, no match".
3272
+ *
3273
+ * Requiring a captured back is deliberate and load-bearing: the digital-ID
3274
+ * upload path submits a full ID (front face present) but does NOT
3275
+ * populate `capturedImages` (only the camera path does). Gating on a captured
3276
+ * back excludes those upload paths so a real ID-vs-selfie match is never
3277
+ * silently skipped. `capturedImages.front`/`forceFrontIdCapture` alone are not
3278
+ * enough (a later front upload clears the request; uploads set neither field).
3279
+ *
3280
+ * Always `false` outside US Smart Capture. The selfie/face capture itself is
3281
+ * unaffected — only the ID-vs-selfie match is skipped.
3282
+ *
3283
+ * Also `true` on the Digital ID web-redirect (Trinsic iDIN/FTN) COMPLETED path
3284
+ * when the scheme returned no portrait (`isDigitalIdRedirect && !portraitPresent`,
3285
+ * FM-1/FM-2): there is no ID face to match the selfie against.
3286
+ */
3287
+ function computeIdCaptureOutput(context) {
3288
+ const frontIdCaptured = Boolean(context.capturedImages.front);
3289
+ const backIdCaptured = Boolean(context.capturedImages.back);
3290
+ return {
3291
+ frontIdCaptured,
3292
+ skipFaceMatch: context.isDigitalIdRedirect === true && context.portraitPresent !== true || context.config.usSmartCapture === true && backIdCaptured && !frontIdCaptured
3243
3293
  };
3244
- return Object.keys(config).length > 0 ? {
3245
- target,
3246
- config
3247
- } : { target };
3248
3294
  }
3249
3295
  //#endregion
3250
3296
  //#region src/modules/digital-id-redirect/pollCadence.ts
@@ -3765,9 +3811,6 @@ function getIdErrorCodeFromUnknown(error) {
3765
3811
  function asPublicIdEvent(event) {
3766
3812
  return event;
3767
3813
  }
3768
- function isCaptureOnlyConfig(config) {
3769
- return "onCapture" in config && typeof config.onCapture === "function";
3770
- }
3771
3814
  function protectedFailure(event) {
3772
3815
  return event.type === "PROTECTED_ATTEMPT_FAILED" ? event : null;
3773
3816
  }
@@ -3781,13 +3824,13 @@ const idCaptureMachine = setup({
3781
3824
  actors: {
3782
3825
  checkPermission: checkPermissionActor,
3783
3826
  requestPermission: requestPermissionActor,
3827
+ requestMotionPermission: requestMotionPermissionActor,
3784
3828
  protectedAttempt: createProtectedIdAttemptActor(),
3785
3829
  protectedControllerLifetime: createProtectedIdControllerLifetimeActor(),
3786
3830
  captureOnlyDelivery: createIdCaptureOnlyDeliveryActor(),
3787
3831
  uploadProgressTicker: uploadProgressTickerActor,
3788
3832
  processId: processIdActor,
3789
3833
  submitBarcode: submitBarcodeActor,
3790
- manualUploadMachine,
3791
3834
  digitalUploadMachine,
3792
3835
  digitalIdRedirectMachine,
3793
3836
  deviceWalletMachine,
@@ -3804,7 +3847,7 @@ const idCaptureMachine = setup({
3804
3847
  return {};
3805
3848
  }),
3806
3849
  startProtectedMetadataPreparation: ({ context, self }) => {
3807
- if (!isCaptureOnlyConfig(context.config)) getIdProtectedCaptureController(self).startMetadataPreparation(context);
3850
+ if (!hasOnCaptureCallback(context.config)) getIdProtectedCaptureController(self).startMetadataPreparation(context);
3808
3851
  },
3809
3852
  bindProtectedControllerLifetime: sendTo("protectedControllerLifetime", ({ self }) => ({
3810
3853
  type: "BIND_PROTECTED_ID_CONTROLLER",
@@ -3845,7 +3888,7 @@ const idCaptureMachine = setup({
3845
3888
  requestProtectedManualCommit: sendTo("protectedAttempt", { type: "MANUAL_CAPTURE" }),
3846
3889
  finalizeProtectedAttempt: sendTo("protectedAttempt", { type: "FINALIZE" }),
3847
3890
  requestProtectedUpload: enqueueActions(({ context, enqueue }) => {
3848
- if (isCaptureOnlyConfig(context.config)) return;
3891
+ if (hasOnCaptureCallback(context.config)) return;
3849
3892
  enqueue(sendTo("protectedAttempt", {
3850
3893
  type: "UPLOAD_PROTECTED",
3851
3894
  request: getProtectedIdUploadRequest(context)
@@ -3863,6 +3906,25 @@ const idCaptureMachine = setup({
3863
3906
  type: "RELEASE",
3864
3907
  reason: "retry"
3865
3908
  }),
3909
+ pauseProtectedDetection: sendTo("protectedAttempt", { type: "PAUSE_DETECTION" }),
3910
+ resumeProtectedDetection: sendTo("protectedAttempt", ({ context }) => ({
3911
+ type: "RESUME_DETECTION",
3912
+ reset: !context.manualCaptureTriggered
3913
+ })),
3914
+ resetAutomaticCapture: assign({
3915
+ manualCaptureTriggered: false,
3916
+ detectionStatus: "idle",
3917
+ counterValue: 0,
3918
+ decodedBarcode: null,
3919
+ barcodeFastPathAttempted: false,
3920
+ qualityElements: void 0
3921
+ }),
3922
+ trackSwitchToAutoCapture: ({ context }) => {
3923
+ addEvent({
3924
+ code: context.currentMode === "back" ? "switchToAutoCaptureBackId" : "switchToAutoCaptureFrontId",
3925
+ module: eventModuleNames.idCapture
3926
+ });
3927
+ },
3866
3928
  forwardProtectedBackground: sendTo("protectedAttempt", { type: "BACKGROUND" }),
3867
3929
  forwardProtectedForeground: sendTo("protectedAttempt", { type: "FOREGROUND" }),
3868
3930
  storeProtectedAutoQuality: assign(({ event }) => event.type === "PROTECTED_AUTO_CAPTURE" ? { qualityElements: event.qualityElements } : {}),
@@ -4047,9 +4109,12 @@ const idCaptureMachine = setup({
4047
4109
  performStopMediaRecording(context);
4048
4110
  },
4049
4111
  clearRecordingSession: assign({ recordingSession: () => void 0 }),
4050
- setSelectedDocument: assign({ selectedDocumentType: ({ event }) => {
4051
- if ("documentType" in event) return event.documentType;
4052
- } }),
4112
+ setSelectedDocument: assign({
4113
+ selectedDocumentType: ({ event }) => {
4114
+ if ("documentType" in event) return event.documentType;
4115
+ },
4116
+ selectedDigitalIdProviderId: ({ event }) => event.type === "SELECT_DOCUMENT" && event.documentType === "digitalIdRedirect" ? event.providerId : void 0
4117
+ }),
4053
4118
  setCurrentMode: assign({ currentMode: ({ event, context }) => getCurrentModeFromEvent(context, asPublicIdEvent(event)) }),
4054
4119
  storeCapturedImage: assign(({ context, event }) => getStoreCapturedImageValues(context, asPublicIdEvent(event))),
4055
4120
  setCandidateFrame: assign(({ context, event }) => getCandidateFrameValues(context, asPublicIdEvent(event))),
@@ -4190,6 +4255,22 @@ const idCaptureMachine = setup({
4190
4255
  digitalIdResumeProviderId: pending?.providerId
4191
4256
  };
4192
4257
  }),
4258
+ /**
4259
+ * Drops the inputs that belong to one digital-ID attempt: the return-leg
4260
+ * params and the recovered verificationId/providerId, plus the funnel
4261
+ * route. The `digitalIdRedirect` child copies what it needs on entry, so
4262
+ * once it exits nothing reads them — but left in place they are replayed
4263
+ * on the next redirect selection. The return leg would re-submit the
4264
+ * previous provider's result (the user picks iDIN and gets FTN's
4265
+ * cancelled screen back), and a stale route would override the card the
4266
+ * user just picked.
4267
+ */
4268
+ clearDigitalIdAttempt: assign({
4269
+ digitalIdReturnParams: () => void 0,
4270
+ digitalIdResumeVerificationId: () => void 0,
4271
+ digitalIdResumeProviderId: () => void 0,
4272
+ digitalIdRoute: () => void 0
4273
+ }),
4193
4274
  incrementProcessingExpiredAttempts: assign(({ context }) => ({ processingExpiredAttempts: context.processingExpiredAttempts + 1 })),
4194
4275
  setSkipProcessId: assign({ skipProcessId: () => true }),
4195
4276
  setExpiredFromUpload: assign({ expiredFromUpload: () => true }),
@@ -4199,7 +4280,7 @@ const idCaptureMachine = setup({
4199
4280
  clearExpiredFromUpload: assign({ expiredFromUpload: () => false })
4200
4281
  },
4201
4282
  guards: {
4202
- isCaptureOnlyDelivery: ({ context }) => isCaptureOnlyConfig(context.config),
4283
+ isCaptureOnlyDelivery: ({ context }) => hasOnCaptureCallback(context.config),
4203
4284
  isProtectedUploadFailure: ({ event }) => protectedFailure(event)?.failure.stage === "upload",
4204
4285
  isProtectedManualRefusal: ({ event }) => protectedFailure(event)?.failure.stage === "manualCommit",
4205
4286
  hasShowTutorial: hasShowTutorialGuard,
@@ -4216,6 +4297,7 @@ const idCaptureMachine = setup({
4216
4297
  }),
4217
4298
  isPermissionDeniedError: ({ event }) => isPermissionDeniedErrorGuard({ event: asPublicIdEvent(event) }),
4218
4299
  isPermissionBlocked: isPermissionBlockedGuard,
4300
+ requiresMotionPermissionUserGesture: requiresMotionPermissionUserGestureGuard,
4219
4301
  hasAttemptsRemaining: hasAttemptsRemainingGuard,
4220
4302
  hasCapturedImage: hasCapturedImageGuard,
4221
4303
  hasCandidateFrame: ({ event }) => hasCandidateFrameGuard({ event: asPublicIdEvent(event) }),
@@ -4229,9 +4311,7 @@ const idCaptureMachine = setup({
4229
4311
  isDeepsightEnabled: isDeepsightEnabledGuard,
4230
4312
  isDeepsightReady: isDeepsightReadyGuard,
4231
4313
  needsDeepsightInit: needsDeepsightInitGuard,
4232
- hasOnlyManualUploadAvailable: hasOnlyManualUploadAvailableGuard,
4233
4314
  hasNoDocumentTypesAvailable: hasNoDocumentTypesAvailableGuard,
4234
- isManualIdUploadSelected: ({ event }) => isManualIdUploadSelectedGuard({ event: asPublicIdEvent(event) }),
4235
4315
  isDigitalIdUploadSelected: ({ event }) => isDigitalIdUploadSelectedGuard({ event: asPublicIdEvent(event) }),
4236
4316
  isDigitalIdRedirectSelected: ({ event }) => isDigitalIdRedirectSelectedGuard({ event: asPublicIdEvent(event) }),
4237
4317
  isDigitalIdSelected: ({ event }) => isDigitalIdSelectedGuard({ event: asPublicIdEvent(event) }),
@@ -4254,7 +4334,6 @@ const idCaptureMachine = setup({
4254
4334
  forceFinishAfterProcessing: forceFinishAfterProcessingGuard,
4255
4335
  isBackModeInMultiSideFlow: isBackModeInMultiSideFlowGuard,
4256
4336
  wasExpiredRedirected: wasExpiredRedirectedGuard,
4257
- isManualUploadExhausted: ({ event }) => event.output?.result === "exhausted",
4258
4337
  isDigitalUploadExhausted: ({ event }) => event.output?.result === "exhausted",
4259
4338
  isDigitalUploadSuccessful: ({ event }) => event.output?.result === "success",
4260
4339
  isDigitalIdRedirectCompleted: ({ event }) => event.output?.status === "COMPLETED",
@@ -4303,9 +4382,13 @@ const idCaptureMachine = setup({
4303
4382
  },
4304
4383
  context: ({ input, self }) => {
4305
4384
  attachIdProtectedCaptureController(self, createIdProtectedCaptureController());
4385
+ const config = {
4386
+ ...input.config,
4387
+ captureAttempts: input.config.captureAttempts ?? 1
4388
+ };
4306
4389
  const currentMode = input.config.onlyBack ? "back" : !input.config.enableId && input.config.enablePassport ? "passport" : input.config.usSmartCapture ? "back" : "front";
4307
4390
  return {
4308
- config: input.config,
4391
+ config,
4309
4392
  captureFeedbackMode: input.captureFeedbackMode ?? "countdown",
4310
4393
  captureWindowMs: input.captureWindowMs,
4311
4394
  optimizingDwellMs: input.optimizingDwellMs,
@@ -4325,7 +4408,7 @@ const idCaptureMachine = setup({
4325
4408
  barcodeFastPathAttempted: false,
4326
4409
  barcodeFastPathCaptured: false,
4327
4410
  recordingSession: void 0,
4328
- attemptsRemaining: input.config.captureAttempts,
4411
+ attemptsRemaining: config.captureAttempts,
4329
4412
  expiredAttempts: 0,
4330
4413
  expiredRedirected: false,
4331
4414
  processingExpiredAttempts: 0,
@@ -4359,6 +4442,8 @@ const idCaptureMachine = setup({
4359
4442
  disableIpify: getDisableIpify(),
4360
4443
  deepsightInitAttempted: false,
4361
4444
  availableDocumentTypes: getAvailableDocumentTypes(input.config, { credentialsAvailable: input.dependencies?.credentials?.isAvailable() ?? false }),
4445
+ digitalIdRedirectOptions: getAvailableDigitalIdRedirectOptions(input.config),
4446
+ selectedDigitalIdProviderId: void 0,
4362
4447
  captureResolution: void 0,
4363
4448
  cameraFallbackLevel: void 0,
4364
4449
  usingBackCamera: void 0
@@ -4379,10 +4464,6 @@ const idCaptureMachine = setup({
4379
4464
  guard: "hasNoDocumentTypesAvailable",
4380
4465
  actions: [() => console.error(ID_CAPTURE_NO_DOCUMENT_TYPES_ERROR), assign({ error: () => ID_CAPTURE_NO_DOCUMENT_TYPES_ERROR })]
4381
4466
  },
4382
- {
4383
- target: "manualIdUpload",
4384
- guard: "hasOnlyManualUploadAvailable"
4385
- },
4386
4467
  {
4387
4468
  target: "chooser",
4388
4469
  guard: "hasShowDocumentChooser"
@@ -4413,11 +4494,6 @@ const idCaptureMachine = setup({
4413
4494
  guard: "isDigitalIdUploadSelected",
4414
4495
  actions: ["setSelectedDocument"]
4415
4496
  },
4416
- {
4417
- target: "manualIdUpload",
4418
- guard: "isManualIdUploadSelected",
4419
- actions: ["setSelectedDocument"]
4420
- },
4421
4497
  {
4422
4498
  target: "ageVerification",
4423
4499
  guard: "hasAgeAssurance",
@@ -4494,6 +4570,14 @@ const idCaptureMachine = setup({
4494
4570
  }]
4495
4571
  },
4496
4572
  on: {
4573
+ CONTINUE_TO_PERMISSION: [{
4574
+ target: "requestingMotionPermission",
4575
+ guard: "requiresMotionPermissionUserGesture",
4576
+ actions: "trackContinue"
4577
+ }, {
4578
+ target: "#idCapture.permissions",
4579
+ actions: "trackContinue"
4580
+ }],
4497
4581
  NEXT_STEP: {
4498
4582
  target: "#idCapture.capture",
4499
4583
  actions: "trackContinue"
@@ -4506,6 +4590,14 @@ const idCaptureMachine = setup({
4506
4590
  },
4507
4591
  initializingCamera: {
4508
4592
  type: "parallel",
4593
+ on: { CONTINUE_TO_PERMISSION: [{
4594
+ target: "requestingMotionPermission",
4595
+ guard: "requiresMotionPermissionUserGesture",
4596
+ actions: "trackContinue"
4597
+ }, {
4598
+ target: "#idCapture.permissions",
4599
+ actions: "trackContinue"
4600
+ }] },
4509
4601
  states: {
4510
4602
  cameraInit: {
4511
4603
  initial: "initializingStream",
@@ -4547,6 +4639,14 @@ const idCaptureMachine = setup({
4547
4639
  onDone: { target: "#idCapture.capture" }
4548
4640
  },
4549
4641
  ready: { on: {
4642
+ CONTINUE_TO_PERMISSION: [{
4643
+ target: "requestingMotionPermission",
4644
+ guard: "requiresMotionPermissionUserGesture",
4645
+ actions: "trackContinue"
4646
+ }, {
4647
+ target: "#idCapture.permissions",
4648
+ actions: "trackContinue"
4649
+ }],
4550
4650
  NEXT_STEP: {
4551
4651
  target: "waitingForPermission",
4552
4652
  actions: "trackContinue"
@@ -4587,6 +4687,15 @@ const idCaptureMachine = setup({
4587
4687
  actions: assign({ permissionResult: ({ event }) => event.output })
4588
4688
  }
4589
4689
  ]
4690
+ } },
4691
+ requestingMotionPermission: { invoke: {
4692
+ id: "requestTutorialMotionPermission",
4693
+ src: "requestMotionPermission",
4694
+ onDone: [{
4695
+ target: "#idCapture.permissions.denied",
4696
+ guard: ({ event }) => event.output === "denied"
4697
+ }, { target: "#idCapture.permissions" }],
4698
+ onError: { target: "#idCapture.permissions.denied" }
4590
4699
  } }
4591
4700
  }
4592
4701
  },
@@ -4642,6 +4751,7 @@ const idCaptureMachine = setup({
4642
4751
  } },
4643
4752
  motionOnly: { on: {
4644
4753
  REQUEST_PERMISSION: "requesting",
4754
+ GO_TO_LEARN_MORE: "learnMore",
4645
4755
  BACK: {
4646
4756
  target: "#idCapture.tutorial.ready",
4647
4757
  guard: "hasShowTutorial"
@@ -4755,6 +4865,7 @@ const idCaptureMachine = setup({
4755
4865
  }],
4756
4866
  entry: [assign({ detectionStatus: () => "detecting" })],
4757
4867
  on: {
4868
+ OPEN_HELP: { target: "help" },
4758
4869
  DETECTION_UPDATE: { actions: "setDetectionStatus" },
4759
4870
  PROTECTED_AUTO_CAPTURE: { actions: ["storeProtectedAutoQuality", "requestProtectedAutoCommit"] },
4760
4871
  PROTECTED_AUTO_COMMITTED_SIGNAL: {
@@ -4819,6 +4930,7 @@ const idCaptureMachine = setup({
4819
4930
  initial: "searching",
4820
4931
  states: {
4821
4932
  searching: { on: {
4933
+ OPEN_HELP: { target: "#idCapture.capture.help" },
4822
4934
  DETECTION_UPDATE: { actions: "setDetectionStatus" },
4823
4935
  ID_SIDE_CHANGE: { actions: assign({ detectionStatus: ({ context, event }) => getDetectionStatusFromSideChange(context, event.side) }) },
4824
4936
  BARCODE_DECODED: [{
@@ -4856,7 +4968,34 @@ const idCaptureMachine = setup({
4856
4968
  }
4857
4969
  }
4858
4970
  },
4971
+ help: {
4972
+ entry: "pauseProtectedDetection",
4973
+ on: {
4974
+ CLOSE_HELP: [{
4975
+ guard: "hasManualCaptureTriggered",
4976
+ target: "manualCaptureWaiting",
4977
+ actions: "resumeProtectedDetection"
4978
+ }, {
4979
+ target: "choosingDetectionStrategy",
4980
+ actions: ["resetAutomaticCapture", "resumeProtectedDetection"]
4981
+ }],
4982
+ SWITCH_TO_MANUAL_CAPTURE: { actions: assign({
4983
+ manualCaptureTriggered: true,
4984
+ detectionStatus: "manualCapture"
4985
+ }) },
4986
+ SWITCH_TO_AUTO_CAPTURE: {
4987
+ guard: "hasManualCaptureTriggered",
4988
+ target: "releasingForRetry",
4989
+ actions: ["trackSwitchToAutoCapture", "resetAutomaticCapture"]
4990
+ }
4991
+ }
4992
+ },
4859
4993
  manualCaptureWaiting: { on: {
4994
+ OPEN_HELP: { target: "help" },
4995
+ SWITCH_TO_AUTO_CAPTURE: {
4996
+ target: "releasingForRetry",
4997
+ actions: ["trackSwitchToAutoCapture", "resetAutomaticCapture"]
4998
+ },
4860
4999
  MANUAL_CAPTURE: { actions: "requestProtectedManualCommit" },
4861
5000
  PROTECTED_MANUAL_COMMITTED_SIGNAL: {
4862
5001
  target: "capturingManual",
@@ -4962,7 +5101,7 @@ const idCaptureMachine = setup({
4962
5101
  id: "captureOnlyDelivery",
4963
5102
  src: "captureOnlyDelivery",
4964
5103
  input: ({ context }) => {
4965
- if (!isCaptureOnlyConfig(context.config)) return { mode: "protected" };
5104
+ if (!hasOnCaptureCallback(context.config)) return { mode: "protected" };
4966
5105
  return { mode: "captureOnly" };
4967
5106
  }
4968
5107
  }],
@@ -5264,35 +5403,6 @@ const idCaptureMachine = setup({
5264
5403
  actions: "resetContext"
5265
5404
  } }
5266
5405
  },
5267
- manualIdUpload: {
5268
- invoke: {
5269
- id: "manualIdUpload",
5270
- src: "manualUploadMachine",
5271
- input: ({ context }) => ({
5272
- isSecondId: context.config.isSecondId ?? false,
5273
- onlyFront: context.config.onlyFront ?? false,
5274
- onlyBack: context.config.onlyBack ?? false,
5275
- captureAttempts: context.config.captureAttempts,
5276
- extractIdFace: context.config.extractIdFace
5277
- }),
5278
- onSnapshot: { actions: () => {} },
5279
- onDone: [{
5280
- guard: "isManualUploadExhausted",
5281
- target: "#idCapture.finished"
5282
- }, { target: "#idCapture.processing" }],
5283
- onError: {
5284
- target: "#idCapture.error",
5285
- actions: assign({ error: ({ event }) => String(event.error) })
5286
- }
5287
- },
5288
- on: {
5289
- QUIT: { target: "closed" },
5290
- MANUAL_UPLOAD_TAB_CHANGED: { actions: sendTo("manualIdUpload", ({ event }) => event) },
5291
- MANUAL_UPLOAD_FILE_SELECTED: { actions: sendTo("manualIdUpload", ({ event }) => event) },
5292
- MANUAL_UPLOAD_CONTINUE: { actions: sendTo("manualIdUpload", ({ event }) => event) },
5293
- MANUAL_UPLOAD_RESET: { actions: sendTo("manualIdUpload", ({ event }) => event) }
5294
- }
5295
- },
5296
5406
  digitalIdUpload: {
5297
5407
  invoke: {
5298
5408
  id: "digitalIdUpload",
@@ -5351,28 +5461,38 @@ const idCaptureMachine = setup({
5351
5461
  },
5352
5462
  {
5353
5463
  target: "chooser",
5354
- guard: "hasShowDocumentChooser"
5464
+ guard: "hasShowDocumentChooser",
5465
+ actions: ["clearDigitalIdAttempt"]
5355
5466
  },
5356
- { target: "loading" }
5467
+ {
5468
+ target: "loading",
5469
+ actions: ["clearDigitalIdAttempt"]
5470
+ }
5357
5471
  ],
5358
5472
  onError: [{
5359
5473
  target: "chooser",
5360
- guard: "hasShowDocumentChooser"
5361
- }, { target: "loading" }]
5474
+ guard: "hasShowDocumentChooser",
5475
+ actions: ["clearDigitalIdAttempt"]
5476
+ }, {
5477
+ target: "loading",
5478
+ actions: ["clearDigitalIdAttempt"]
5479
+ }]
5362
5480
  },
5363
5481
  on: { QUIT: { target: "closed" } }
5364
5482
  },
5365
5483
  digitalIdRedirect: {
5484
+ exit: "clearDigitalIdAttempt",
5366
5485
  invoke: {
5367
5486
  id: "digitalIdRedirect",
5368
5487
  src: "digitalIdRedirectMachine",
5369
5488
  input: ({ context }) => {
5370
- const method = getDigitalIdRedirectMethod(context.config);
5371
5489
  const resuming = context.digitalIdReturnParams !== void 0 && context.digitalIdResumeVerificationId !== void 0;
5372
5490
  const routedProvider = context.digitalIdRoute?.kind === "trinsicRedirect" ? context.digitalIdRoute.providerId : void 0;
5491
+ const presetProvider = (resuming ? context.digitalIdResumeProviderId : void 0) ?? routedProvider ?? context.selectedDigitalIdProviderId;
5492
+ const method = presetProvider !== void 0 ? getDigitalIdRedirectMethod(context.config, presetProvider) : getDigitalIdRedirectMethod(context.config);
5373
5493
  return {
5374
5494
  config: {
5375
- provider: context.digitalIdResumeProviderId ?? routedProvider ?? method?.provider,
5495
+ provider: presetProvider ?? method?.provider,
5376
5496
  returnUrl: context.config.digitalIdReturnUrl,
5377
5497
  country: method?.country
5378
5498
  },
@@ -5479,4 +5599,4 @@ function provideIdProtectedCaptureController(machine, controller) {
5479
5599
  });
5480
5600
  }
5481
5601
  //#endregion
5482
- 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 };
5602
+ export { mapDeviceWalletScreenName as _, isRedirectFlowType as a, createIdCaptureOnlyDeliveryActor as c, captureOnlyNeedsBack as d, hasOnCaptureCallback as f, BrowserDigitalCredentialsProvider as g, createDeviceWalletManager as h, DIGITAL_ID_REGISTRY as i, createIdProtectedCaptureController as l, renderWallet as m, provideIdProtectedCaptureController as n, resolveDigitalIdImplementation as o, pickIdCaptureOnlyConfig as p, DIGITAL_ID_DOCUMENT_TYPES as r, attachIdProtectedCaptureController as s, idCaptureMachine as t, findIdProtectedCaptureController as u, resolveWalletPlatform as v };