@incodetech/core 2.2.0 → 2.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD_PARTY_NOTICES.txt +281 -0
- package/dist/BaseWasmProvider-Dyi6dKp1.d.ts +65 -0
- package/dist/{BrowserDiagnosticsCryptoProvider-B8vorxN5.esm.js → BrowserDiagnosticsCryptoProvider-C_9o3L2L.esm.js} +1 -1
- package/dist/{BrowserVisibilityObserverProvider-BX1BiLDK.esm.js → BrowserVisibilityObserverProvider-BrJN1B8A.esm.js} +2 -0
- package/dist/FaceDetectionProvider-DTetAE8H.esm.js +476 -0
- package/dist/FaceDetectionProvider-LdZ47bWy.d.ts +36 -0
- package/dist/{IIdCaptureCapability-DHTT3FC2.d.ts → IIdCaptureCapability-RNiFhvXR.d.ts} +7 -33
- package/dist/IWasmUtilCapability-CXXDA5l1.d.ts +159 -0
- package/dist/{MotionSensorProvider-DGiV77J_.esm.js → MotionSensorProvider-BgTYwg9L.esm.js} +1 -1
- package/dist/{deepsightService-DLVQS_RH.d.ts → StreamCanvasCapture-B3YT2Bt6.d.ts} +54 -41
- package/dist/{StreamCanvasCapture-D2XcXCZ4.esm.js → StreamCanvasCapture-C9jtgBdT.esm.js} +2 -2
- package/dist/{addressSearch-D1XpBdUS.esm.js → addressSearch-BRSu_aGA.esm.js} +1 -1
- package/dist/advanceableErrors-CpF2cBy6.esm.js +21 -0
- package/dist/ae-signature.d.ts +4 -2
- package/dist/ae-signature.esm.js +1 -1
- package/dist/antifraud.esm.js +2 -2
- package/dist/{antifraudManager-CQFNgSPt.esm.js → antifraudManager-Bg9ynGum.esm.js} +2 -2
- package/dist/{antifraudStateMachine-Bf_JVNwn.esm.js → antifraudStateMachine-BHxehiJZ.esm.js} +1 -1
- package/dist/{apiError-BA1d_au0.esm.js → apiError-D0v5hFyN.esm.js} +14 -2
- package/dist/authentication.d.ts +3 -3
- package/dist/authentication.esm.js +2 -2
- package/dist/{authenticationManager-zh9TC62P.esm.js → authenticationManager-C4B4-29g.esm.js} +5 -5
- package/dist/{authenticationManager-BBMGTcwW.d.ts → authenticationManager-DXTIUcma.d.ts} +7 -1
- package/dist/{authenticationStateMachine-xzi-vnvE.esm.js → authenticationStateMachine-DJ0zUm-A.esm.js} +6 -62
- package/dist/avatar.d.ts +1 -2
- package/dist/avatar.esm.js +1 -1
- package/dist/{backCameraStream-CUeYm5eu.esm.js → backCameraStream-BOtyQowf.esm.js} +83 -22
- package/dist/{camera-_2sXMXiF.esm.js → camera-sIa9ceW2.esm.js} +19 -1
- package/dist/camera.esm.js +1 -1
- package/dist/{canvas-E1zGkel4.esm.js → canvas-BxFzWTt-.esm.js} +19 -0
- package/dist/certificate-issuance.d.ts +2 -302
- package/dist/certificate-issuance.esm.js +3 -66
- package/dist/certificateIssuanceManager-Bfvkq7Cy.esm.js +69 -0
- package/dist/certificateIssuanceManager-DCgEidHz.d.ts +312 -0
- package/dist/{certificateIssuanceStateMachine-D_R92TYu.esm.js → certificateIssuanceStateMachine-BBD5h8c4.esm.js} +1 -1
- package/dist/consent.d.ts +1 -1
- package/dist/consent.esm.js +2 -2
- package/dist/{consentManager-BxjpLZAG.esm.js → consentManager-B5Vqdez-.esm.js} +2 -2
- package/dist/{consentManager-3Pbtj-RV.d.ts → consentManager-uhDPdU6i.d.ts} +4 -4
- package/dist/{consentStateMachine-D9D4ON8Z.esm.js → consentStateMachine-DbUmqWgV.esm.js} +1 -1
- package/dist/createCameraCycleOwnership-DQ5-F2oW.esm.js +826 -0
- package/dist/{createFaceAvatar-DQKaRise.esm.js → createFaceAvatar-BNHdeFj8.esm.js} +2 -2
- package/dist/createFaceAvatar-BlcTYgJv.d.ts +497 -0
- package/dist/cross-document-data-match.esm.js +1 -1
- package/dist/{crossDocumentDataMatchManager-CGT0V6RV.esm.js → crossDocumentDataMatchManager-QMOGwHXR.esm.js} +2 -2
- package/dist/curp-validation.d.ts +1 -1
- package/dist/curp-validation.esm.js +2 -2
- package/dist/{curpValidationManager-6Nmkn5u1.esm.js → curpValidationManager-D1ppROcF.esm.js} +2 -2
- package/dist/{curpValidationManager-DVGr7Fq7.d.ts → curpValidationManager-SSKNrm5Z.d.ts} +1 -1
- package/dist/{curpValidationStateMachine-Ds7KMacK.esm.js → curpValidationStateMachine-DvUPC_8S.esm.js} +1 -1
- package/dist/custom-fields.d.ts +2 -114
- package/dist/custom-fields.esm.js +2 -232
- package/dist/custom-watchlist.d.ts +2 -51
- package/dist/custom-watchlist.esm.js +3 -70
- package/dist/customFieldsManager-2j_PBq4U.esm.js +264 -0
- package/dist/customFieldsManager-8-W2KY4n.d.ts +141 -0
- package/dist/customWatchlistManager-DUVrEdw9.esm.js +73 -0
- package/dist/customWatchlistManager-PBWHr_oM.d.ts +64 -0
- package/dist/{customWatchlistStateMachine-YWSgKLbF.esm.js → customWatchlistStateMachine-qZVgkKgP.esm.js} +1 -1
- package/dist/{deepsightLoader--wMKWECS.esm.js → deepsightLoader-Dm7ejW4v.esm.js} +27 -71
- package/dist/device.d.ts +25 -2
- package/dist/device.esm.js +69 -7
- package/dist/document-capture.d.ts +2 -2
- package/dist/document-capture.esm.js +2 -2
- package/dist/{documentCaptureManager-DsQQhWry.esm.js → documentCaptureManager-DR_Wv-3H.esm.js} +2 -2
- package/dist/{documentCaptureManager-B3t0ydtg.d.ts → documentCaptureManager-DensIj5F.d.ts} +83 -83
- package/dist/{documentCaptureStateMachine-BM4Ex_5x.esm.js → documentCaptureStateMachine-CWOATHfE.esm.js} +5 -5
- package/dist/dynamic-forms.d.ts +1 -1
- package/dist/dynamic-forms.esm.js +2 -2
- package/dist/{dynamicFormsManager-fhK0yINk.d.ts → dynamicFormsManager-D5R_uxLQ.d.ts} +25 -3
- package/dist/{dynamicFormsManager-fFVIRBNv.esm.js → dynamicFormsManager-Dt98SDfV.esm.js} +16 -5
- package/dist/{dynamicFormsStateMachine-D6D7oEYk.esm.js → dynamicFormsStateMachine-obKr4IIp.esm.js} +35 -3
- package/dist/ekyb.d.ts +1 -1
- package/dist/ekyb.esm.js +3 -3
- package/dist/{ekybManager-Y3MkoXpC.esm.js → ekybManager-DNZKrKF3.esm.js} +2 -2
- package/dist/{ekybStateMachine-3nlnqdlr.esm.js → ekybStateMachine-DjhHQXZ8.esm.js} +3 -3
- package/dist/ekyc.esm.js +2 -2
- package/dist/{ekycManager-DgT48ky6.esm.js → ekycManager-CmQBpeiI.esm.js} +2 -2
- package/dist/{ekycStateMachine-CQwqiR-R.esm.js → ekycStateMachine-CmdW2gVm.esm.js} +2 -2
- package/dist/electronic-signature.d.ts +3 -3
- package/dist/electronic-signature.esm.js +2 -2
- package/dist/electronicSignatureManager-Ds-KEVTq.d.ts +2786 -0
- package/dist/{electronicSignatureManager-DGbAJ00W.esm.js → electronicSignatureManager-DzulxM4_.esm.js} +357 -51
- package/dist/email.esm.js +2 -2
- package/dist/{emailManager-BY0jeYwR.esm.js → emailManager-B5ek_UfM.esm.js} +2 -2
- package/dist/{emailStateMachine-Crw8QVmc.esm.js → emailStateMachine-CyPFZ7dW.esm.js} +1 -1
- package/dist/{endpoints-CCzL3XD_.esm.js → endpoints-D3B9mUHh.esm.js} +24 -7
- package/dist/{events-DYiyoSlx.esm.js → events-ijlbX_Wx.esm.js} +2 -2
- package/dist/events.d.ts +1 -1
- package/dist/events.esm.js +1 -1
- package/dist/extensibility.d.ts +24 -64
- package/dist/extensibility.esm.js +35 -29
- package/dist/face-match.esm.js +2 -2
- package/dist/{faceCaptureManagerFactory-BxWPpxyd.d.ts → faceCaptureManagerFactory-CvtqcLxs.d.ts} +87 -54
- package/dist/{faceCaptureManagerFactory-CVnZ0fa5.esm.js → faceCaptureManagerFactory-pCmG4MeF.esm.js} +68 -7
- package/dist/faceCaptureSetup-BSextack.esm.js +2106 -0
- package/dist/{faceMatchManager-Ri4UbNz0.esm.js → faceMatchManager-Caal2zyV.esm.js} +2 -2
- package/dist/{faceMatchStateMachine-20DdZ3jg.esm.js → faceMatchStateMachine-BUc5FXYy.esm.js} +1 -1
- package/dist/{feature-management-CPQ4qv3F.esm.js → feature-management-B0sGlozZ.esm.js} +32 -7
- package/dist/feature-management.d.ts +1 -1
- package/dist/feature-management.esm.js +1 -1
- package/dist/field-comparison.esm.js +1 -1
- package/dist/{fieldComparisonManager-Cg6kPQQx.esm.js → fieldComparisonManager-BIs6HPJG.esm.js} +2 -2
- package/dist/fiscal-qr.d.ts +2 -56
- package/dist/fiscal-qr.esm.js +2 -308
- package/dist/fiscalQrManager-Bp_-3LJr.esm.js +34131 -0
- package/dist/fiscalQrManager-CHcBJwUM.d.ts +68 -0
- package/dist/flow-events.d.ts +1 -1
- package/dist/flow.d.ts +212 -185
- package/dist/flow.esm.js +119 -30
- package/dist/{flowCompletionService-tfeoGre9.esm.js → flowCompletionService-CLtbWhSK.esm.js} +2 -2
- package/dist/{flowServices-f5Ruz7R9.esm.js → flowServices-B5FwukTQ.esm.js} +91 -26
- package/dist/geolocation.esm.js +2 -2
- package/dist/{geolocationManager-CH6RfqFH.esm.js → geolocationManager-DIw-xq9a.esm.js} +2 -2
- package/dist/{geolocationStateMachine-DmbWFely.esm.js → geolocationStateMachine-DpG0TFOU.esm.js} +3 -3
- package/dist/getBrowser-CBiibsL-.esm.js +19 -0
- package/dist/{getBrowser-DNrQeL4T.esm.js → getBrowser-Cbe1C4Tn.esm.js} +2 -1
- package/dist/{getDeviceClass-9y-kbVDw.esm.js → getDeviceClass-5NYZg9s6.esm.js} +2 -2
- package/dist/getParsedUserAgent-B2iFDql1.esm.js +1278 -0
- package/dist/government-validation.d.ts +2 -85
- package/dist/government-validation.esm.js +3 -120
- package/dist/governmentValidationManager-D02vT4yY.d.ts +63 -0
- package/dist/governmentValidationManager-TjC9iVjH.esm.js +79 -0
- package/dist/governmentValidationStateMachine-D1ZnEqdd.esm.js +54 -0
- package/dist/home.d.ts +1 -1
- package/dist/home.esm.js +1 -1
- package/dist/id-ocr.d.ts +2 -2
- package/dist/id-ocr.esm.js +2 -2
- package/dist/id-selfie-flow.d.ts +3 -2
- package/dist/id-selfie-flow.esm.js +11 -10
- package/dist/id-verification.esm.js +2 -2
- package/dist/id.d.ts +29 -5
- package/dist/id.esm.js +48 -46
- package/dist/idCaptureDefaults-BcxjQDI3.esm.js +484 -0
- package/dist/{idCaptureManager-CqrqSjJV.d.ts → idCaptureManager-CvmHOWWd.d.ts} +227 -39
- package/dist/{idCaptureManager-BYwTZ2H8.esm.js → idCaptureManager-Vby8oYqh.esm.js} +259 -46
- package/dist/{idCaptureServices-44kwQItl.esm.js → idCaptureServices-CJIxD9ep.esm.js} +342 -31
- package/dist/idCaptureStateMachine-CmqihkUy.esm.js +5343 -0
- package/dist/{idOcrManager-DXc_xRN7.d.ts → idOcrManager-BF_cXZq8.d.ts} +1 -1
- package/dist/{idOcrManager-BIQYQXXj.esm.js → idOcrManager-DHU22hVn.esm.js} +2 -2
- package/dist/{idOcrStateMachine-cSxzHpHt.esm.js → idOcrStateMachine-33UvRAHE.esm.js} +1 -1
- package/dist/{idVerificationManager-JpV_SAsl.esm.js → idVerificationManager-CyaixD0D.esm.js} +2 -2
- package/dist/{idVerificationStateMachine-nPeUNyFq.esm.js → idVerificationStateMachine-BPGrQZEm.esm.js} +1 -1
- package/dist/identity-reuse.esm.js +2 -2
- package/dist/{identityReuseManager-kNCg9AbD.esm.js → identityReuseManager-CWaNHBO3.esm.js} +2 -2
- package/dist/{identityReuseStateMachine-DyulxDjP.esm.js → identityReuseStateMachine-CQgnATo5.esm.js} +1 -1
- package/dist/{index-CLMF0QLQ.d.ts → index-DLcJT5YP.d.ts} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/{invokeOnCaptureCallback-GpvMuz9o.esm.js → invokeOnCaptureCallback-BjzPjJ_p.esm.js} +1 -1
- package/dist/mandatory-consent.d.ts +2 -2
- package/dist/mandatory-consent.esm.js +2 -2
- package/dist/{mandatoryConsentManager-DyPzUmsZ.d.ts → mandatoryConsentManager-CJSyhTpi.d.ts} +5 -5
- package/dist/{mandatoryConsentManager-B4Niyg_u.esm.js → mandatoryConsentManager-Du-m-tZ_.esm.js} +2 -2
- package/dist/{mandatoryConsentStateMachine-B4-1u-L8.esm.js → mandatoryConsentStateMachine-DSDAk7WZ.esm.js} +1 -1
- package/dist/multipartRecordingService-B3C_zlev.esm.js +594 -0
- package/dist/{permissionGuards-KINpmKtI.esm.js → permissionGuards-uYZYFu4J.esm.js} +1 -1
- package/dist/{permissionServices-UvUcOSRb.esm.js → permissionServices-Bj_fyWtv.esm.js} +5 -5
- package/dist/personhood.d.ts +6 -5
- package/dist/personhood.esm.js +10 -10
- package/dist/phone.esm.js +2 -2
- package/dist/{phoneManager-FsJ0W2Ei.esm.js → phoneManager-N1-sbrY4.esm.js} +2 -2
- package/dist/{phoneStateMachine-Ba0wKHyX.esm.js → phoneStateMachine-jl8LJKCo.esm.js} +1 -1
- package/dist/{platform-BJrm5nzZ.esm.js → platform-DkcRc9JV.esm.js} +29 -1
- package/dist/qe-signature.d.ts +5 -3
- package/dist/qe-signature.esm.js +2 -2
- package/dist/recordingsRepository-CCWy13M-.esm.js +72 -0
- package/dist/redirect-to-mobile.esm.js +2 -2
- package/dist/{redirectToMobileManager-y4VQB11M.esm.js → redirectToMobileManager-DsFzdTwA.esm.js} +2 -2
- package/dist/{redirectToMobileStateMachine-Cyv2x0PP.esm.js → redirectToMobileStateMachine-v3otpT-L.esm.js} +1 -1
- package/dist/{rolldown-runtime-FZ4Itg2g.esm.js → rolldown-runtime-FDOR9p9I.esm.js} +1 -19
- package/dist/{runChildModule-Dukuw23X.esm.js → runChildModule-TuyGE00X.esm.js} +3 -21
- package/dist/safeStorage-B-trhxg6.esm.js +423 -0
- package/dist/{sdkVersion-D1Bm59g6.esm.js → sdkVersion-CLxKvdyz.esm.js} +1 -1
- package/dist/selfie.d.ts +5 -5
- package/dist/selfie.esm.js +14 -76
- package/dist/{selfieErrorUtils-C2PYBVsp.esm.js → selfieErrorUtils-C77GXJH6.esm.js} +30 -4
- package/dist/{selfieManager-Bj6B2ZVy.esm.js → selfieManager-CihVnODO.esm.js} +5 -5
- package/dist/{selfieManager-DwlCppg0.d.ts → selfieManager-D1KRJ39M.d.ts} +8 -11
- package/dist/selfieStateMachine-fwMmNtOi.esm.js +69 -0
- package/dist/{session-BFdLgN4i.esm.js → session-BHIeAamR.esm.js} +4 -5
- package/dist/session.d.ts +21 -11
- package/dist/session.esm.js +5 -5
- package/dist/{sessionInitializer-dlfDehHk.esm.js → sessionInitializer-BLoK7SP9.esm.js} +187 -68
- package/dist/{setup-BgoWtvIS.d.ts → setup-Cv-JM9QC.d.ts} +63 -69
- package/dist/{setup-BIAtLa8k.esm.js → setup-msLybMbC.esm.js} +226 -100
- package/dist/signature.esm.js +2 -2
- package/dist/{signatureManager-Ca43yhwk.esm.js → signatureManager-ChsB-nMD.esm.js} +2 -2
- package/dist/{signatureStateMachine-K1LMRrxG.esm.js → signatureStateMachine-BHPwkJZS.esm.js} +1 -1
- package/dist/{src-0Jurt29N.esm.js → src-35ekGRzK.esm.js} +1 -1
- package/dist/{stats-B0nFKawG.esm.js → stats-DkK-f8JT.esm.js} +1 -1
- package/dist/stats.esm.js +1 -1
- package/dist/{trueSightService---iW2KKJ.esm.js → trueSightService-CKrrfV0Q.esm.js} +7 -7
- package/dist/trust-graph.esm.js +1 -1
- package/dist/{types-BX_XkMsQ2.d.ts → types-9_esGTPj.d.ts} +1 -1
- package/dist/types-BT_Oalip.d.ts +1 -0
- package/dist/{types-DelMqZLe.d.ts → types-BjSly8cp.d.ts} +1 -1
- package/dist/{types-Hmd7U0JA.d.ts → types-CM-jtjJ2.d.ts} +6 -81
- package/dist/{types-Bdf8_LYB2.d.ts → types-CjKBS37a2.d.ts} +303 -13
- package/dist/{types-DVmXIdzW.d.ts → types-D0gekt4U.d.ts} +38 -5
- package/dist/types-DgDvvzg0.d.ts +10 -0
- package/dist/{types-KPvmag_B.d.ts → types-Do5rEXij.d.ts} +7 -4
- package/dist/types-ZT1XFIJ8.d.ts +6 -0
- package/dist/video-selfie.d.ts +59 -35
- package/dist/video-selfie.esm.js +510 -601
- package/dist/{videoSelfieCameraWarmer-NEhFZvWx.esm.js → videoSelfieCameraWarmer-CB1IzZkt.esm.js} +14 -153
- package/dist/videoSelfieFlowModuleMachine-BUe2uACW.esm.js +32 -0
- package/dist/{videoSelfieStepDetector-D3oo2ofo.d.ts → videoSelfieStepDetector-5YHFe0Mq.d.ts} +21 -5
- package/dist/virtualCameraCheck-WOloJ-do.esm.js +48 -0
- package/dist/warmup-CUUnwYxe.esm.js +714 -0
- package/dist/warmup-Cis2p6O0.d.ts +128 -0
- package/dist/wasm.d.ts +30 -6
- package/dist/wasm.esm.js +36 -4
- package/dist/{wasmDsFlagSync-BqUCwLY8.esm.js → wasmDsFlagSync-B2e8p268.esm.js} +2 -2
- package/dist/{wasmWebClient-DM8lkb0z.esm.js → wasmWebClient-B7baE-Wf.esm.js} +358 -28
- package/dist/watchlist-for-business.d.ts +2 -78
- package/dist/watchlist-for-business.esm.js +3 -102
- package/dist/watchlist.esm.js +2 -2
- package/dist/watchlistForBusinessManager-CpuuB1J2.esm.js +107 -0
- package/dist/watchlistForBusinessManager-CsnJnG-C.d.ts +99 -0
- package/dist/{watchlistForBusinessStateMachine-CxFnlshm.esm.js → watchlistForBusinessStateMachine-DEzgjz7C.esm.js} +13 -6
- package/dist/{watchlistManager-LoNZH9qm.esm.js → watchlistManager-CI8BHFob.esm.js} +2 -2
- package/dist/{watchlistStateMachine-DIKQDoUT.esm.js → watchlistStateMachine-DvOby3a-.esm.js} +1 -1
- package/dist/workflow.d.ts +12 -8
- package/dist/workflow.esm.js +48 -39
- package/package.json +4 -10
- package/dist/BrowserStorageProvider-k-FDkC4W.esm.js +0 -67
- package/dist/FaceDetectionProvider-Mh6q3EbA.d.ts +0 -349
- package/dist/IFaceAvatarCapability-BrL19G0s.d.ts +0 -23
- package/dist/IStorageCapability-ORkQfK3F.d.ts +0 -242
- package/dist/OpenViduRecordingProvider-Du98qevC.esm.js +0 -85
- package/dist/createFaceAvatar-BEj18KXi.d.ts +0 -50
- package/dist/document-upload.d.ts +0 -325
- package/dist/document-upload.esm.js +0 -197
- package/dist/electronicSignatureManager-CcUXQZS7.d.ts +0 -1300
- package/dist/faceCaptureSetup-Cd-04W_X.esm.js +0 -863
- package/dist/getBrowser-Cb8Zgrgp.esm.js +0 -7
- package/dist/getParsedUserAgent-pJjATANs.esm.js +0 -1622
- package/dist/governmentValidationStateMachine-CK9DrZXX.esm.js +0 -226
- package/dist/idCaptureDefaults-WeHbQM7W.esm.js +0 -225
- package/dist/idCaptureStateMachine-kZ5OpdfM.esm.js +0 -3342
- package/dist/lib-DAyUtOTj.esm.js +0 -12420
- package/dist/manualReviewService-D4IkipnN.esm.js +0 -15
- package/dist/mediaRecorderOptions-dALSV8af.esm.js +0 -40
- package/dist/onDeviceFaceResults-CUHwyZT4.esm.js +0 -646
- package/dist/openviduLazy-CCDpFWg1.esm.js +0 -10
- package/dist/recordingService-3NSENVwp.esm.js +0 -329
- package/dist/safeStorage-Ep56I-f_.esm.js +0 -982
- package/dist/selfieStateMachine-CHV_WNst.esm.js +0 -55
- package/dist/trueSight-B_icM8r2.esm.js +0 -1
- package/dist/types-EELWyWpd.d.ts +0 -6
- package/dist/types-Vb4P9ovf.d.ts +0 -22
- package/dist/warmup-Dv2alr2-.d.ts +0 -82
- package/dist/wasmDefaults-DTUrftxR.esm.js +0 -125
- package/dist/{Face2DProvider-UUQt_vRC.esm.js → Face2DProvider-BF48sz5c.esm.js} +1 -1
- package/dist/{PrivacyLensProvider-D8PS7AJa.esm.js → PrivacyLensProvider-EwnC_C5n.esm.js} +1 -1
- /package/dist/{types-BfgmV7s72.d.ts → types-BfgmV7s7.d.ts} +0 -0
- /package/dist/{types-CJ7Yl9K-2.d.ts → types-CJ7Yl9K-.d.ts} +0 -0
|
@@ -0,0 +1,2106 @@
|
|
|
1
|
+
import { t as getTimestamp } from "./IBrowserEnvironmentCapability-DEaRRk-R.esm.js";
|
|
2
|
+
import { r as getToken } from "./api-Bx3gKW_y.esm.js";
|
|
3
|
+
import { l as trueSightLog, m as isTrueSightTesting } from "./trueSightService-CKrrfV0Q.esm.js";
|
|
4
|
+
import "./sessionInitializer-BLoK7SP9.esm.js";
|
|
5
|
+
import { t as addDeviceStats } from "./stats-DkK-f8JT.esm.js";
|
|
6
|
+
import { t as isAndroid } from "./platform-DkcRc9JV.esm.js";
|
|
7
|
+
import { i as assign, n as enqueueActions, r as sendTo, t as setup } from "./xstate.esm-BuFLFuzz.esm.js";
|
|
8
|
+
import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-BXeQ-Ucz.esm.js";
|
|
9
|
+
import { n as invokeRequestPermissionActor, t as checkPermission } from "./permissionServices-Bj_fyWtv.esm.js";
|
|
10
|
+
import { a as sendLabelInspectionEvent, c as stopRecording, i as initializeDeepsightSession, l as stopStream, r as initializeCamera, s as startRecordingSession, t as FaceDetectionProvider } from "./FaceDetectionProvider-DTetAE8H.esm.js";
|
|
11
|
+
import { t as FACE_ERROR_CODES } from "./types-BN5RPPIZ.esm.js";
|
|
12
|
+
import { a as flagFaceManualReview, i as imageFileToDataUrl, n as runProtectedAttempt, r as cameraCycleProvider, s as PROTECTED_CAPTURE_TARGET, t as createCameraCycleOwnership } from "./createCameraCycleOwnership-DQ5-F2oW.esm.js";
|
|
13
|
+
import { n as releaseFaceAvatarWarmup, r as warmFaceAvatar, t as createFaceAvatar } from "./createFaceAvatar-BNHdeFj8.esm.js";
|
|
14
|
+
import { t as IncodeCanvas } from "./canvas-BxFzWTt-.esm.js";
|
|
15
|
+
import { n as needsMotionPrimeOnlyFromPermission, r as resolveStoredPermissionResult, t as isReadyForCaptureFromPermission } from "./permissionGuards-uYZYFu4J.esm.js";
|
|
16
|
+
import { i as uploadDeepsightVideo } from "./recordingsRepository-CCWy13M-.esm.js";
|
|
17
|
+
import { t as getDeviceClass } from "./getDeviceClass-5NYZg9s6.esm.js";
|
|
18
|
+
import { n as getAdaptiveMediaRecorderOptions, t as MultipartRecordingService } from "./multipartRecordingService-B3C_zlev.esm.js";
|
|
19
|
+
//#region src/internal/avatar/concealment.ts
|
|
20
|
+
/**
|
|
21
|
+
* Maps a backend concealment option to the internal avatar flags. `OPTION_NONE`
|
|
22
|
+
* — and any absent or unknown value, for forward-compat safety — yields the live
|
|
23
|
+
* video (no avatar). The other three map 1:1 onto the shipped avatar catalog:
|
|
24
|
+
* - `OPTION_3D` → `avatar-3d` (3D VRM)
|
|
25
|
+
* - `OPTION_2D` → `avatar-2d` (stylized 2D face)
|
|
26
|
+
* - `OPTION_SILHOUETTE` → `privacy-lens` (WebGL frosted-glass conceal)
|
|
27
|
+
*/
|
|
28
|
+
function concealmentToAvatar(option) {
|
|
29
|
+
switch (option) {
|
|
30
|
+
case "OPTION_3D": return {
|
|
31
|
+
obfuscateWithAvatar: true,
|
|
32
|
+
avatarVariant: "avatar-3d"
|
|
33
|
+
};
|
|
34
|
+
case "OPTION_2D": return {
|
|
35
|
+
obfuscateWithAvatar: true,
|
|
36
|
+
avatarVariant: "avatar-2d"
|
|
37
|
+
};
|
|
38
|
+
case "OPTION_SILHOUETTE": return {
|
|
39
|
+
obfuscateWithAvatar: true,
|
|
40
|
+
avatarVariant: "privacy-lens"
|
|
41
|
+
};
|
|
42
|
+
default: return {
|
|
43
|
+
obfuscateWithAvatar: false,
|
|
44
|
+
avatarVariant: void 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/internal/faceCapture/manualUpload/errors.ts
|
|
50
|
+
/**
|
|
51
|
+
* Reuses the `notifications.*` copy that `errorModal.tsx` shows for the same
|
|
52
|
+
* codes on the camera path, plus the ID manual-upload keys. Only the `title`
|
|
53
|
+
* half of each notification pair is used — the inline error is one line.
|
|
54
|
+
*/
|
|
55
|
+
const MANUAL_FACE_UPLOAD_ERROR_KEYS = {
|
|
56
|
+
/** The one new string; ID accepts any image type so there was none to reuse. */
|
|
57
|
+
typeError: "selfiev2.manualUpload.typeError",
|
|
58
|
+
fileTooBig: "manualIdUpload.fileTooBig",
|
|
59
|
+
generic: "manualIdUpload.generic",
|
|
60
|
+
faceNotFound: "notifications.faceNotFound",
|
|
61
|
+
multipleFaces: "notifications.multiple",
|
|
62
|
+
lowQualityImage: "notifications.lowQualityImage",
|
|
63
|
+
brightness: "notifications.brightness",
|
|
64
|
+
lenses: "notifications.lenses",
|
|
65
|
+
mask: "notifications.mask",
|
|
66
|
+
removeHeadwear: "notifications.removeHeadwear",
|
|
67
|
+
closedEyes: "notifications.closedEyes",
|
|
68
|
+
spoof: "notifications.spoof",
|
|
69
|
+
accessDenied: "notifications.accessDenied",
|
|
70
|
+
nonexistentCustomer: "notifications.nonexistentCustomer",
|
|
71
|
+
hintNotProvided: "notifications.hintNotProvided",
|
|
72
|
+
selfieImageLowQuality: "notifications.selfieImageLowQuality"
|
|
73
|
+
};
|
|
74
|
+
/** Mirrors `errorContent` in `errorModal.tsx`; omissions resolve to `generic`. */
|
|
75
|
+
const ERROR_KEY_BY_FACE_ERROR_CODE = {
|
|
76
|
+
[FACE_ERROR_CODES.FACE_NOT_FOUND]: MANUAL_FACE_UPLOAD_ERROR_KEYS.faceNotFound,
|
|
77
|
+
[FACE_ERROR_CODES.MULTIPLE_FACES]: MANUAL_FACE_UPLOAD_ERROR_KEYS.multipleFaces,
|
|
78
|
+
[FACE_ERROR_CODES.BAD_PHOTO_QUALITY]: MANUAL_FACE_UPLOAD_ERROR_KEYS.lowQualityImage,
|
|
79
|
+
[FACE_ERROR_CODES.SELFIE_IMAGE_LOW_QUALITY]: MANUAL_FACE_UPLOAD_ERROR_KEYS.selfieImageLowQuality,
|
|
80
|
+
[FACE_ERROR_CODES.BRIGHTNESS]: MANUAL_FACE_UPLOAD_ERROR_KEYS.brightness,
|
|
81
|
+
[FACE_ERROR_CODES.FACE_TOO_DARK]: MANUAL_FACE_UPLOAD_ERROR_KEYS.brightness,
|
|
82
|
+
[FACE_ERROR_CODES.LENSES]: MANUAL_FACE_UPLOAD_ERROR_KEYS.lenses,
|
|
83
|
+
[FACE_ERROR_CODES.LENSES_DETECTED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.lenses,
|
|
84
|
+
[FACE_ERROR_CODES.MASK]: MANUAL_FACE_UPLOAD_ERROR_KEYS.mask,
|
|
85
|
+
[FACE_ERROR_CODES.FACE_MASK_DETECTED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.mask,
|
|
86
|
+
[FACE_ERROR_CODES.HEAD_COVER]: MANUAL_FACE_UPLOAD_ERROR_KEYS.removeHeadwear,
|
|
87
|
+
[FACE_ERROR_CODES.HEAD_COVER_DETECTED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.removeHeadwear,
|
|
88
|
+
[FACE_ERROR_CODES.CLOSED_EYES]: MANUAL_FACE_UPLOAD_ERROR_KEYS.closedEyes,
|
|
89
|
+
[FACE_ERROR_CODES.CLOSED_EYES_DETECTED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.closedEyes,
|
|
90
|
+
[FACE_ERROR_CODES.LIVENESS]: MANUAL_FACE_UPLOAD_ERROR_KEYS.spoof,
|
|
91
|
+
[FACE_ERROR_CODES.SPOOF_ATTEMPT_DETECTED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.spoof,
|
|
92
|
+
[FACE_ERROR_CODES.FACE_TOO_BLURRY]: MANUAL_FACE_UPLOAD_ERROR_KEYS.spoof,
|
|
93
|
+
[FACE_ERROR_CODES.USER_IS_NOT_RECOGNIZED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.accessDenied,
|
|
94
|
+
[FACE_ERROR_CODES.NONEXISTENT_CUSTOMER]: MANUAL_FACE_UPLOAD_ERROR_KEYS.nonexistentCustomer,
|
|
95
|
+
[FACE_ERROR_CODES.HINT_NOT_PROVIDED]: MANUAL_FACE_UPLOAD_ERROR_KEYS.hintNotProvided
|
|
96
|
+
};
|
|
97
|
+
function mapFaceErrorCodeToErrorKey(code) {
|
|
98
|
+
if (!code) return MANUAL_FACE_UPLOAD_ERROR_KEYS.generic;
|
|
99
|
+
return ERROR_KEY_BY_FACE_ERROR_CODE[code] ?? MANUAL_FACE_UPLOAD_ERROR_KEYS.generic;
|
|
100
|
+
}
|
|
101
|
+
/** Enforced in core because the picker's `accept` filter is bypassable. */
|
|
102
|
+
const MANUAL_FACE_UPLOAD_ACCEPTED_MIME_TYPES = ["image/png", "image/jpeg"];
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/internal/faceCapture/manualUpload/prepareFile.ts
|
|
105
|
+
const isAcceptedMimeType = (type) => MANUAL_FACE_UPLOAD_ACCEPTED_MIME_TYPES.includes(type);
|
|
106
|
+
const stripDataUrlPrefix = (dataUrl) => {
|
|
107
|
+
const commaIndex = dataUrl.indexOf(",");
|
|
108
|
+
return commaIndex === -1 ? dataUrl : dataUrl.slice(commaIndex + 1);
|
|
109
|
+
};
|
|
110
|
+
/** Duplicated from the ID manual upload; consolidate if a third caller appears. */
|
|
111
|
+
const getBase64PayloadByteLength = (payload) => {
|
|
112
|
+
const normalized = payload.replace(/\s/g, "");
|
|
113
|
+
let padding = 0;
|
|
114
|
+
if (normalized.endsWith("==")) padding = 2;
|
|
115
|
+
else if (normalized.endsWith("=")) padding = 1;
|
|
116
|
+
return Math.max(0, Math.floor(normalized.length * 3 / 4) - padding);
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Every client-side check, run before any network call so a rejection here
|
|
120
|
+
* never costs the user a retry.
|
|
121
|
+
*/
|
|
122
|
+
async function prepareManualFaceUpload(file) {
|
|
123
|
+
if (!isAcceptedMimeType(file.type)) return {
|
|
124
|
+
ok: false,
|
|
125
|
+
errorKey: MANUAL_FACE_UPLOAD_ERROR_KEYS.typeError
|
|
126
|
+
};
|
|
127
|
+
if (file.size > 5242880) return {
|
|
128
|
+
ok: false,
|
|
129
|
+
errorKey: MANUAL_FACE_UPLOAD_ERROR_KEYS.fileTooBig
|
|
130
|
+
};
|
|
131
|
+
const base64Image = stripDataUrlPrefix(await imageFileToDataUrl(file));
|
|
132
|
+
if (getBase64PayloadByteLength(base64Image) > 5242880) return {
|
|
133
|
+
ok: false,
|
|
134
|
+
errorKey: MANUAL_FACE_UPLOAD_ERROR_KEYS.fileTooBig
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
ok: true,
|
|
138
|
+
base64Image
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/internal/faceCapture/manualUpload/manualFaceUploadStateMachine.ts
|
|
143
|
+
const prepareFileActor = fromPromise(async ({ input }) => prepareManualFaceUpload(input.file));
|
|
144
|
+
const getPreparedFromEvent = (event) => "output" in event ? event.output : void 0;
|
|
145
|
+
const getErrorMessageFromEvent = (event) => {
|
|
146
|
+
const { error } = event;
|
|
147
|
+
if (error instanceof Error) return error.message;
|
|
148
|
+
return typeof error === "string" ? error : "";
|
|
149
|
+
};
|
|
150
|
+
const manualFaceUploadMachine = setup({
|
|
151
|
+
types: {
|
|
152
|
+
context: {},
|
|
153
|
+
events: {},
|
|
154
|
+
input: {},
|
|
155
|
+
output: {}
|
|
156
|
+
},
|
|
157
|
+
actors: {
|
|
158
|
+
prepareFile: prepareFileActor,
|
|
159
|
+
uploadFile: fromPromise(async () => {
|
|
160
|
+
throw new Error("uploadFile must be provided by variant");
|
|
161
|
+
})
|
|
162
|
+
},
|
|
163
|
+
actions: {
|
|
164
|
+
storeSelectedFile: assign(({ event }) => {
|
|
165
|
+
if (event.type !== "MANUAL_UPLOAD_FILE_SELECTED") return {};
|
|
166
|
+
return {
|
|
167
|
+
file: event.file,
|
|
168
|
+
errorKey: null,
|
|
169
|
+
uploaded: false
|
|
170
|
+
};
|
|
171
|
+
}),
|
|
172
|
+
storePreparedImage: assign(({ event }) => {
|
|
173
|
+
const prepared = getPreparedFromEvent(event);
|
|
174
|
+
return prepared?.ok ? { base64Image: prepared.base64Image } : {};
|
|
175
|
+
}),
|
|
176
|
+
setErrorFromPrepare: assign(({ event }) => {
|
|
177
|
+
const prepared = getPreparedFromEvent(event);
|
|
178
|
+
return { errorKey: prepared && !prepared.ok ? prepared.errorKey : MANUAL_FACE_UPLOAD_ERROR_KEYS.generic };
|
|
179
|
+
}),
|
|
180
|
+
setGenericError: assign(() => ({ errorKey: MANUAL_FACE_UPLOAD_ERROR_KEYS.generic })),
|
|
181
|
+
setErrorFromUploadFailure: assign(({ event }) => ({ errorKey: mapFaceErrorCodeToErrorKey(getErrorMessageFromEvent(event)) })),
|
|
182
|
+
markUploaded: assign(() => ({
|
|
183
|
+
uploaded: true,
|
|
184
|
+
errorKey: null
|
|
185
|
+
})),
|
|
186
|
+
decrementRetries: assign(({ context }) => ({ retriesLeft: Math.max(0, context.retriesLeft - 1) })),
|
|
187
|
+
reset: assign(({ context }) => ({
|
|
188
|
+
file: null,
|
|
189
|
+
base64Image: null,
|
|
190
|
+
uploaded: false,
|
|
191
|
+
errorKey: null,
|
|
192
|
+
retriesLeft: context.captureAttempts
|
|
193
|
+
})),
|
|
194
|
+
markSucceeded: assign(() => ({ outcome: "success" })),
|
|
195
|
+
markExhausted: assign(() => ({ outcome: "exhausted" }))
|
|
196
|
+
},
|
|
197
|
+
guards: {
|
|
198
|
+
isPrepareRejected: ({ event }) => getPreparedFromEvent(event)?.ok === false,
|
|
199
|
+
retriesExhausted: ({ context }) => context.retriesLeft <= 0,
|
|
200
|
+
canContinue: ({ context }) => context.uploaded
|
|
201
|
+
}
|
|
202
|
+
}).createMachine({
|
|
203
|
+
id: "manualFaceUpload",
|
|
204
|
+
initial: "selecting",
|
|
205
|
+
context: ({ input }) => ({
|
|
206
|
+
config: input.config,
|
|
207
|
+
dependencies: input.dependencies,
|
|
208
|
+
captureAttempts: input.captureAttempts,
|
|
209
|
+
retriesLeft: input.captureAttempts,
|
|
210
|
+
file: null,
|
|
211
|
+
base64Image: null,
|
|
212
|
+
uploaded: false,
|
|
213
|
+
errorKey: null,
|
|
214
|
+
outcome: null
|
|
215
|
+
}),
|
|
216
|
+
output: ({ context }) => ({ result: context.outcome === "success" ? "success" : "exhausted" }),
|
|
217
|
+
on: { MANUAL_UPLOAD_RESET: {
|
|
218
|
+
target: ".selecting",
|
|
219
|
+
actions: ["reset"]
|
|
220
|
+
} },
|
|
221
|
+
states: {
|
|
222
|
+
selecting: { on: {
|
|
223
|
+
MANUAL_UPLOAD_FILE_SELECTED: {
|
|
224
|
+
target: "preparing",
|
|
225
|
+
actions: ["storeSelectedFile"]
|
|
226
|
+
},
|
|
227
|
+
MANUAL_UPLOAD_CONTINUE: {
|
|
228
|
+
guard: "canContinue",
|
|
229
|
+
target: "finished",
|
|
230
|
+
actions: ["markSucceeded"]
|
|
231
|
+
}
|
|
232
|
+
} },
|
|
233
|
+
preparing: { invoke: {
|
|
234
|
+
id: "prepareFile",
|
|
235
|
+
src: "prepareFile",
|
|
236
|
+
input: ({ context }) => ({ file: context.file }),
|
|
237
|
+
onDone: [{
|
|
238
|
+
guard: "isPrepareRejected",
|
|
239
|
+
target: "selecting",
|
|
240
|
+
actions: ["setErrorFromPrepare"]
|
|
241
|
+
}, {
|
|
242
|
+
target: "uploading",
|
|
243
|
+
actions: ["storePreparedImage"]
|
|
244
|
+
}],
|
|
245
|
+
onError: {
|
|
246
|
+
target: "selecting",
|
|
247
|
+
actions: ["setGenericError"]
|
|
248
|
+
}
|
|
249
|
+
} },
|
|
250
|
+
uploading: { invoke: {
|
|
251
|
+
id: "uploadFile",
|
|
252
|
+
src: "uploadFile",
|
|
253
|
+
input: ({ context }) => ({
|
|
254
|
+
base64Image: context.base64Image,
|
|
255
|
+
config: context.config,
|
|
256
|
+
dependencies: context.dependencies
|
|
257
|
+
}),
|
|
258
|
+
onDone: {
|
|
259
|
+
target: "selecting",
|
|
260
|
+
actions: ["markUploaded"]
|
|
261
|
+
},
|
|
262
|
+
onError: {
|
|
263
|
+
target: "checkRetries",
|
|
264
|
+
actions: ["decrementRetries", "setErrorFromUploadFailure"]
|
|
265
|
+
}
|
|
266
|
+
} },
|
|
267
|
+
checkRetries: { always: [{
|
|
268
|
+
guard: "retriesExhausted",
|
|
269
|
+
target: "exhausted",
|
|
270
|
+
actions: ["markExhausted"]
|
|
271
|
+
}, { target: "selecting" }] },
|
|
272
|
+
exhausted: { type: "final" },
|
|
273
|
+
finished: { type: "final" }
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region ../infra/src/providers/wasm/createProtectedFaceCaptureCapabilities.ts
|
|
278
|
+
/**
|
|
279
|
+
* Infrastructure composition boundary for one protected still-face attempt.
|
|
280
|
+
* Core receives capability interfaces and never constructs or reaches for the
|
|
281
|
+
* process-wide WASM providers directly.
|
|
282
|
+
*/
|
|
283
|
+
function createProtectedFaceCaptureCapabilities() {
|
|
284
|
+
return {
|
|
285
|
+
cycle: cameraCycleProvider,
|
|
286
|
+
faceDetection: new FaceDetectionProvider()
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/internal/faceCapture/faceCaptureDiagnostics.ts
|
|
291
|
+
const FACE_CAMERA_DIAGNOSTIC_PREFIX = "[Incode face camera]";
|
|
292
|
+
/**
|
|
293
|
+
* Emits one bounded lifecycle line only for SDK testing sessions. The payload
|
|
294
|
+
* deliberately excludes media, native results, tokens, request data, and
|
|
295
|
+
* errors so a browser console recording cannot retain capture data.
|
|
296
|
+
*/
|
|
297
|
+
function logFaceCameraDiagnostic(event, fields) {
|
|
298
|
+
if (!isTrueSightTesting()) return;
|
|
299
|
+
console.debug(`${FACE_CAMERA_DIAGNOSTIC_PREFIX} ${event} ${JSON.stringify(fields)}`);
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region ../infra/src/media/VideoTrimmer.ts
|
|
303
|
+
async function getDurationBySeek(videoBlob) {
|
|
304
|
+
const video = document.createElement("video");
|
|
305
|
+
video.preload = "metadata";
|
|
306
|
+
video.src = URL.createObjectURL(videoBlob);
|
|
307
|
+
try {
|
|
308
|
+
if (!Number.isFinite(video.duration)) {
|
|
309
|
+
video.currentTime = Number.MAX_SAFE_INTEGER;
|
|
310
|
+
await new Promise((resolve) => {
|
|
311
|
+
const onDurationChange = () => {
|
|
312
|
+
if (Number.isFinite(video.duration)) {
|
|
313
|
+
video.removeEventListener("durationchange", onDurationChange);
|
|
314
|
+
video.removeEventListener("timeupdate", onTimeUpdate);
|
|
315
|
+
resolve(video.duration);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const onTimeUpdate = () => {
|
|
319
|
+
if (Number.isFinite(video.duration)) {
|
|
320
|
+
video.removeEventListener("timeupdate", onTimeUpdate);
|
|
321
|
+
video.removeEventListener("durationchange", onDurationChange);
|
|
322
|
+
resolve(video.duration);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
video.addEventListener("durationchange", onDurationChange);
|
|
326
|
+
video.addEventListener("timeupdate", onTimeUpdate);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const duration = video.duration;
|
|
330
|
+
return Number.isFinite(duration) ? duration : null;
|
|
331
|
+
} finally {
|
|
332
|
+
URL.revokeObjectURL(video.src);
|
|
333
|
+
video.src = "";
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async function trimLastNSecondsUsingPlayback(videoBlob, seconds) {
|
|
337
|
+
const video = document.createElement("video");
|
|
338
|
+
video.preload = "metadata";
|
|
339
|
+
video.playsInline = true;
|
|
340
|
+
video.muted = true;
|
|
341
|
+
const videoURL = URL.createObjectURL(videoBlob);
|
|
342
|
+
video.src = videoURL;
|
|
343
|
+
const duration = await getDurationBySeek(videoBlob);
|
|
344
|
+
if (!duration || duration < seconds) {
|
|
345
|
+
URL.revokeObjectURL(videoURL);
|
|
346
|
+
return videoBlob;
|
|
347
|
+
}
|
|
348
|
+
const startTime = Math.max(0, Math.floor(duration) - seconds);
|
|
349
|
+
await new Promise((resolve) => {
|
|
350
|
+
if (video.readyState >= 2) resolve();
|
|
351
|
+
else video.addEventListener("loadedmetadata", () => resolve(), { once: true });
|
|
352
|
+
});
|
|
353
|
+
const canvas = document.createElement("canvas");
|
|
354
|
+
canvas.width = 230;
|
|
355
|
+
canvas.height = 320;
|
|
356
|
+
const captureFps = isAndroid() ? 15 : 24;
|
|
357
|
+
const stream = canvas.captureStream(captureFps);
|
|
358
|
+
const ctx = canvas.getContext("2d");
|
|
359
|
+
video.currentTime = startTime;
|
|
360
|
+
await new Promise((resolve) => {
|
|
361
|
+
video.addEventListener("seeked", () => resolve(), { once: true });
|
|
362
|
+
});
|
|
363
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
364
|
+
const mimeType = videoBlob.type || (MediaRecorder.isTypeSupported("video/webm") ? "video/webm" : "video/mp4");
|
|
365
|
+
const mediaRecorder = new MediaRecorder(stream.clone(), {
|
|
366
|
+
mimeType,
|
|
367
|
+
videoBitsPerSecond: 5e5,
|
|
368
|
+
bitsPerSecond: 5e5
|
|
369
|
+
});
|
|
370
|
+
const chunks = [];
|
|
371
|
+
mediaRecorder.ondataavailable = (event) => {
|
|
372
|
+
if (event.data.size > 0) chunks.push(event.data);
|
|
373
|
+
};
|
|
374
|
+
const recordingPromise = new Promise((resolve) => {
|
|
375
|
+
mediaRecorder.onstop = () => {
|
|
376
|
+
const trimmedBlob = new Blob(chunks, { type: mimeType });
|
|
377
|
+
URL.revokeObjectURL(videoURL);
|
|
378
|
+
mediaRecorder.stream?.getTracks().forEach((track) => track.stop());
|
|
379
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
380
|
+
video.src = "";
|
|
381
|
+
resolve(trimmedBlob);
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
video.addEventListener("play", () => {
|
|
385
|
+
function drawVideo() {
|
|
386
|
+
if (video.currentTime >= duration) {
|
|
387
|
+
mediaRecorder.stop();
|
|
388
|
+
video.pause();
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
392
|
+
requestAnimationFrame(drawVideo);
|
|
393
|
+
}
|
|
394
|
+
drawVideo();
|
|
395
|
+
setTimeout(() => {
|
|
396
|
+
mediaRecorder.start(100);
|
|
397
|
+
}, 500);
|
|
398
|
+
});
|
|
399
|
+
video.play().catch(() => {
|
|
400
|
+
URL.revokeObjectURL(videoURL);
|
|
401
|
+
});
|
|
402
|
+
const trimmedBlob = await recordingPromise;
|
|
403
|
+
console.timeEnd("trimLastNSecondsUsingPlayback");
|
|
404
|
+
return trimmedBlob;
|
|
405
|
+
}
|
|
406
|
+
async function trimLastNSeconds(videoBlob, seconds) {
|
|
407
|
+
return await trimLastNSecondsUsingPlayback(videoBlob, seconds);
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region ../infra/src/providers/browser/LocalRecordingProvider.ts
|
|
411
|
+
function toBase64(videoBlob) {
|
|
412
|
+
return new Promise((resolve, reject) => {
|
|
413
|
+
const reader = new FileReader();
|
|
414
|
+
reader.onloadend = () => {
|
|
415
|
+
const base64 = reader.result.split(",")[1];
|
|
416
|
+
resolve(base64);
|
|
417
|
+
};
|
|
418
|
+
reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("FileReader error"));
|
|
419
|
+
reader.readAsDataURL(videoBlob);
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
var LocalRecordingProvider = class {
|
|
423
|
+
constructor() {
|
|
424
|
+
this.mediaRecorder = null;
|
|
425
|
+
this._isRecording = false;
|
|
426
|
+
this._hasError = false;
|
|
427
|
+
this._error = null;
|
|
428
|
+
this.mimeType = "";
|
|
429
|
+
this.stream = null;
|
|
430
|
+
this.pauseRecordingBound = this.pauseRecording.bind(this);
|
|
431
|
+
}
|
|
432
|
+
get isRecording() {
|
|
433
|
+
return this._isRecording;
|
|
434
|
+
}
|
|
435
|
+
get hasError() {
|
|
436
|
+
return this._hasError;
|
|
437
|
+
}
|
|
438
|
+
get error() {
|
|
439
|
+
return this._error;
|
|
440
|
+
}
|
|
441
|
+
startRecording(stream) {
|
|
442
|
+
this.reset();
|
|
443
|
+
this.registerEventListeners();
|
|
444
|
+
this.stream = stream;
|
|
445
|
+
try {
|
|
446
|
+
const options = getAdaptiveMediaRecorderOptions();
|
|
447
|
+
this.mimeType = options.mimeType;
|
|
448
|
+
const recorder = new MediaRecorder(stream.clone(), options);
|
|
449
|
+
recorder.onerror = (event) => {
|
|
450
|
+
this._error = `Recording error: ${event}`;
|
|
451
|
+
this._isRecording = false;
|
|
452
|
+
this._hasError = true;
|
|
453
|
+
};
|
|
454
|
+
recorder.start();
|
|
455
|
+
this.mediaRecorder = recorder;
|
|
456
|
+
this._isRecording = true;
|
|
457
|
+
this._error = null;
|
|
458
|
+
this._hasError = false;
|
|
459
|
+
} catch (err) {
|
|
460
|
+
this._error = `Failed to start recording: ${err}`;
|
|
461
|
+
this._hasError = true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async stopRecording(trimSeconds, encrypt, generateChecksum) {
|
|
465
|
+
const recorder = this.mediaRecorder;
|
|
466
|
+
return new Promise((resolve, reject) => {
|
|
467
|
+
this.removeEventListeners();
|
|
468
|
+
if (recorder && this._isRecording) {
|
|
469
|
+
const chunks = [];
|
|
470
|
+
recorder.ondataavailable = (event) => {
|
|
471
|
+
if (event.data.size > 0) chunks.push(event.data);
|
|
472
|
+
};
|
|
473
|
+
recorder.onstop = async () => {
|
|
474
|
+
try {
|
|
475
|
+
const trimmedVideo = await trimLastNSeconds(new Blob(chunks, { type: this.mimeType }), trimSeconds);
|
|
476
|
+
const encryptedVideoBase64 = encrypt(await toBase64(trimmedVideo));
|
|
477
|
+
generateChecksum(await trimmedVideo.arrayBuffer());
|
|
478
|
+
this._isRecording = false;
|
|
479
|
+
resolve({
|
|
480
|
+
trimmedBlob: trimmedVideo,
|
|
481
|
+
encryptedVideo: encryptedVideoBase64
|
|
482
|
+
});
|
|
483
|
+
} catch (error) {
|
|
484
|
+
this._isRecording = false;
|
|
485
|
+
this._error = `Recording stop failed: ${error}`;
|
|
486
|
+
this._hasError = true;
|
|
487
|
+
reject(error);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
recorder.stop();
|
|
491
|
+
this._isRecording = false;
|
|
492
|
+
} else resolve({
|
|
493
|
+
trimmedBlob: new Blob([], { type: this.mimeType }),
|
|
494
|
+
encryptedVideo: ""
|
|
495
|
+
});
|
|
496
|
+
recorder?.stream?.getTracks().forEach((track) => track.stop());
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
reset() {
|
|
500
|
+
this._isRecording = false;
|
|
501
|
+
this._error = null;
|
|
502
|
+
this._hasError = false;
|
|
503
|
+
}
|
|
504
|
+
pauseRecording() {
|
|
505
|
+
if (!this._isRecording) return;
|
|
506
|
+
if (this.mediaRecorder?.state === "recording") try {
|
|
507
|
+
this.mediaRecorder.pause();
|
|
508
|
+
} catch {}
|
|
509
|
+
}
|
|
510
|
+
registerEventListeners() {
|
|
511
|
+
document.addEventListener("visibilitychange", this.pauseRecordingBound);
|
|
512
|
+
}
|
|
513
|
+
removeEventListeners() {
|
|
514
|
+
document.removeEventListener("visibilitychange", this.pauseRecordingBound);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region src/internal/faceCapture/recordingService.ts
|
|
519
|
+
/**
|
|
520
|
+
* Records on-device and uploads encrypted video for liveness.
|
|
521
|
+
*/
|
|
522
|
+
var LocalRecordingService = class {
|
|
523
|
+
constructor(wasmUtil, sessionToken) {
|
|
524
|
+
this.recorder = new LocalRecordingProvider();
|
|
525
|
+
this.sessionToken = sessionToken;
|
|
526
|
+
this.wasmUtil = wasmUtil;
|
|
527
|
+
}
|
|
528
|
+
async start(stream) {
|
|
529
|
+
this.recorder.startRecording(stream);
|
|
530
|
+
this.wasmUtil.osv();
|
|
531
|
+
}
|
|
532
|
+
async stop() {
|
|
533
|
+
if (!this.recorder.isRecording) return { recordingId: null };
|
|
534
|
+
this.wasmUtil.oev();
|
|
535
|
+
const result = await this.recorder.stopRecording(10, (base64) => this.wasmUtil.encryptImage(base64), (buffer) => this.wasmUtil.ckvcks(buffer));
|
|
536
|
+
if (!this.sessionToken) return { recordingId: null };
|
|
537
|
+
return { recordingId: await uploadDeepsightVideo(result.encryptedVideo, this.sessionToken) };
|
|
538
|
+
}
|
|
539
|
+
prepareUpload() {
|
|
540
|
+
return this.stop();
|
|
541
|
+
}
|
|
542
|
+
async stopAndGetVideo() {
|
|
543
|
+
if (!this.recorder.isRecording) return { videoBase64: void 0 };
|
|
544
|
+
try {
|
|
545
|
+
this.wasmUtil.oev();
|
|
546
|
+
const videoBase64 = (await this.recorder.stopRecording(10, (base64) => base64, () => {})).encryptedVideo;
|
|
547
|
+
return { videoBase64: videoBase64 ? videoBase64 : void 0 };
|
|
548
|
+
} catch {
|
|
549
|
+
return { videoBase64: void 0 };
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
cleanup() {
|
|
553
|
+
this.recorder.reset();
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* Records a regular Selfie capture locally and streams it through the scoped
|
|
558
|
+
* video-selfie-service multipart protocol. The recording is associated with
|
|
559
|
+
* the interview asynchronously by the backend's `selfie` scope consumer, so
|
|
560
|
+
* its upload id must not be forwarded to `/omni/add/face` as a legacy
|
|
561
|
+
* `recordingId`.
|
|
562
|
+
*/
|
|
563
|
+
var MultipartFaceRecordingService = class {
|
|
564
|
+
constructor() {
|
|
565
|
+
this.service = new MultipartRecordingService("selfie");
|
|
566
|
+
this.pausedForAttempt = false;
|
|
567
|
+
}
|
|
568
|
+
async start(stream) {
|
|
569
|
+
if (this.pausedForAttempt) {
|
|
570
|
+
if (!this.service.resume()) throw new Error("Face recording did not resume");
|
|
571
|
+
this.pausedForAttempt = false;
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
await this.service.start(stream);
|
|
575
|
+
}
|
|
576
|
+
async prepareUpload() {
|
|
577
|
+
this.pausedForAttempt = this.service.pause();
|
|
578
|
+
return { recordingId: null };
|
|
579
|
+
}
|
|
580
|
+
async stop() {
|
|
581
|
+
try {
|
|
582
|
+
await this.service.stop();
|
|
583
|
+
} finally {
|
|
584
|
+
this.pausedForAttempt = false;
|
|
585
|
+
}
|
|
586
|
+
return { recordingId: null };
|
|
587
|
+
}
|
|
588
|
+
pauseForBackground() {
|
|
589
|
+
this.service.pause();
|
|
590
|
+
}
|
|
591
|
+
async resumeFromBackground() {
|
|
592
|
+
this.service.resume();
|
|
593
|
+
}
|
|
594
|
+
async stopAndGetVideo() {
|
|
595
|
+
try {
|
|
596
|
+
await this.service.stop();
|
|
597
|
+
} finally {
|
|
598
|
+
this.pausedForAttempt = false;
|
|
599
|
+
}
|
|
600
|
+
return { videoBase64: void 0 };
|
|
601
|
+
}
|
|
602
|
+
cleanup() {
|
|
603
|
+
this.pausedForAttempt = false;
|
|
604
|
+
this.service.cleanup();
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
/**
|
|
608
|
+
* Records through a consumer-injected remote provider and manages its session.
|
|
609
|
+
*/
|
|
610
|
+
var InjectedRecordingService = class {
|
|
611
|
+
constructor(config) {
|
|
612
|
+
this.config = config;
|
|
613
|
+
}
|
|
614
|
+
async start(stream) {
|
|
615
|
+
if (this.session) return;
|
|
616
|
+
this.session = await startRecordingSession({
|
|
617
|
+
config: this.config,
|
|
618
|
+
clonedStream: stream.clone()
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
async prepareUpload() {
|
|
622
|
+
return { recordingId: null };
|
|
623
|
+
}
|
|
624
|
+
async stop() {
|
|
625
|
+
if (this.session) {
|
|
626
|
+
stopRecording(this.session);
|
|
627
|
+
this.session = void 0;
|
|
628
|
+
}
|
|
629
|
+
return { recordingId: null };
|
|
630
|
+
}
|
|
631
|
+
async stopAndGetVideo() {
|
|
632
|
+
if (this.session) {
|
|
633
|
+
stopRecording(this.session);
|
|
634
|
+
this.session = void 0;
|
|
635
|
+
}
|
|
636
|
+
return { videoBase64: void 0 };
|
|
637
|
+
}
|
|
638
|
+
cleanup() {
|
|
639
|
+
if (this.session) stopRecording(this.session);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
/**
|
|
643
|
+
* Creates the recording service for the current configuration.
|
|
644
|
+
*
|
|
645
|
+
* Order of precedence:
|
|
646
|
+
* 1. `onDeviceFaceResultsSubmissionEnabled === true` → no recording. The
|
|
647
|
+
* on-device workflow never uploads media, only the staged face-results
|
|
648
|
+
* JSON via `WebApi.postFaceResults`. Mirrors V1's
|
|
649
|
+
* `FaceFlowManager.setRecorders()` early return.
|
|
650
|
+
* 2. `deepsightLiveness === 'VIDEOLIVENESS'` → local Deepsight recorder.
|
|
651
|
+
* 3. `enableFaceRecording === true` → scoped multipart recorder. A custom
|
|
652
|
+
* injected recording capability remains supported for backwards-compatible
|
|
653
|
+
* dependency injection.
|
|
654
|
+
* 4. otherwise → no recording.
|
|
655
|
+
*/
|
|
656
|
+
function createRecordingService(params) {
|
|
657
|
+
if (params.config.onDeviceFaceResultsSubmissionEnabled === true) return;
|
|
658
|
+
if (params.localDelivery === true) return params.config.enableFaceRecording === true || params.config.deepsightLiveness === "VIDEOLIVENESS" ? new LocalRecordingService(params.wasmUtil, null) : void 0;
|
|
659
|
+
if (params.config.deepsightLiveness === "VIDEOLIVENESS") return new LocalRecordingService(params.wasmUtil, params.sessionToken);
|
|
660
|
+
if (params.config.enableFaceRecording === true) return params.config.recording?.capability ? new InjectedRecordingService(params.config) : new MultipartFaceRecordingService();
|
|
661
|
+
}
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/internal/faceCapture/protectedFaceAttempt.ts
|
|
664
|
+
const ON_DEVICE_MANUAL_CAPTURE_TIMEOUT_MS = 2147483647;
|
|
665
|
+
var ProtectedFaceRecorderHooks = class {
|
|
666
|
+
constructor(config, dependencies, delivery) {
|
|
667
|
+
this.config = config;
|
|
668
|
+
this.dependencies = dependencies;
|
|
669
|
+
this.delivery = delivery;
|
|
670
|
+
this.mode = config.onDeviceFaceResultsSubmissionEnabled === true || config.enableFaceRecording !== true && config.deepsightLiveness !== "VIDEOLIVENESS" ? "none" : "finalizeBeforePoc";
|
|
671
|
+
}
|
|
672
|
+
async start(unprotectedStream) {
|
|
673
|
+
if (this.mode === "none") return;
|
|
674
|
+
const wasmUtil = await this.dependencies.getWasmUtil();
|
|
675
|
+
this.service = createRecordingService({
|
|
676
|
+
config: this.config,
|
|
677
|
+
wasmUtil,
|
|
678
|
+
sessionToken: getToken(),
|
|
679
|
+
localDelivery: this.delivery === "local"
|
|
680
|
+
});
|
|
681
|
+
await this.service?.start(unprotectedStream);
|
|
682
|
+
}
|
|
683
|
+
async prepareForPoc() {
|
|
684
|
+
if (!this.service) return {};
|
|
685
|
+
if (this.delivery === "local") {
|
|
686
|
+
try {
|
|
687
|
+
const result = await this.service.stopAndGetVideo();
|
|
688
|
+
this.captureOnlyVideoBase64 = result.videoBase64;
|
|
689
|
+
} catch {
|
|
690
|
+
this.captureOnlyVideoBase64 = void 0;
|
|
691
|
+
}
|
|
692
|
+
return {};
|
|
693
|
+
}
|
|
694
|
+
return this.service.stop();
|
|
695
|
+
}
|
|
696
|
+
async resumeAfterPoc() {}
|
|
697
|
+
pauseForBackground() {
|
|
698
|
+
this.service?.pauseForBackground?.();
|
|
699
|
+
}
|
|
700
|
+
async resumeFromBackground(unprotectedStream) {
|
|
701
|
+
await this.service?.resumeFromBackground?.(unprotectedStream);
|
|
702
|
+
}
|
|
703
|
+
dispose() {
|
|
704
|
+
try {
|
|
705
|
+
this.service?.cleanup();
|
|
706
|
+
} finally {
|
|
707
|
+
this.service = void 0;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
takeCaptureOnlyVideo() {
|
|
711
|
+
const video = this.captureOnlyVideoBase64;
|
|
712
|
+
this.captureOnlyVideoBase64 = void 0;
|
|
713
|
+
return video;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
let nextAttemptNumber = 1;
|
|
717
|
+
/**
|
|
718
|
+
* Private composition object for one still-face attempt. It owns provider
|
|
719
|
+
* callbacks and raw-stream handoff outside XState; the machine sees only the
|
|
720
|
+
* presentation stream/canvas and allowlisted semantic results.
|
|
721
|
+
*/
|
|
722
|
+
var ProtectedFaceAttemptRuntime = class ProtectedFaceAttemptRuntime {
|
|
723
|
+
constructor(options) {
|
|
724
|
+
this.options = options;
|
|
725
|
+
this.attemptId = `face-${nextAttemptNumber++}`;
|
|
726
|
+
this.acquiredUsingBackCamera = false;
|
|
727
|
+
this.rawHandedOff = false;
|
|
728
|
+
this.rawStreamReleased = false;
|
|
729
|
+
this.released = false;
|
|
730
|
+
this.recorderTransferred = false;
|
|
731
|
+
this.pendingSignals = [];
|
|
732
|
+
this.provider = options.faceDetection;
|
|
733
|
+
this.recorder = new ProtectedFaceRecorderHooks(options.config, options.dependencies, options.delivery);
|
|
734
|
+
this.cycleOwnership = createCameraCycleOwnership(options.cycle);
|
|
735
|
+
}
|
|
736
|
+
get delivery() {
|
|
737
|
+
return this.options.delivery;
|
|
738
|
+
}
|
|
739
|
+
get usingBackCamera() {
|
|
740
|
+
return this.acquiredUsingBackCamera;
|
|
741
|
+
}
|
|
742
|
+
setDeepsightService(service) {
|
|
743
|
+
if (this.released || this.deepsightService) return;
|
|
744
|
+
this.deepsightService = service;
|
|
745
|
+
}
|
|
746
|
+
async preAcquire() {
|
|
747
|
+
await this.acquireRawStream();
|
|
748
|
+
}
|
|
749
|
+
subscribeSignals(listener) {
|
|
750
|
+
if (this.released) return () => void 0;
|
|
751
|
+
this.signalListener = listener;
|
|
752
|
+
const queued = this.pendingSignals;
|
|
753
|
+
this.pendingSignals = [];
|
|
754
|
+
queued.forEach(listener);
|
|
755
|
+
return () => {
|
|
756
|
+
if (this.signalListener === listener) this.signalListener = void 0;
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
toActorInput(onStartupTiming) {
|
|
760
|
+
this.recorderTransferred = true;
|
|
761
|
+
return {
|
|
762
|
+
attemptId: this.attemptId,
|
|
763
|
+
target: this.options.target,
|
|
764
|
+
workflow: this.options.config.onDeviceFaceResultsSubmissionEnabled === true ? 5 : 4,
|
|
765
|
+
streamSource: {
|
|
766
|
+
kind: "acquire",
|
|
767
|
+
acquire: async () => {
|
|
768
|
+
const result = await this.acquireRawStream();
|
|
769
|
+
this.rawHandedOff = true;
|
|
770
|
+
return {
|
|
771
|
+
stream: result.stream,
|
|
772
|
+
constraints: result.constraints
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
cycle: this.options.cycle,
|
|
777
|
+
cycleOwnership: this.cycleOwnership,
|
|
778
|
+
recorder: this.recorder,
|
|
779
|
+
releaseUnadoptedStream: (stream) => stopStream(stream),
|
|
780
|
+
configureWorkflow: () => this.configureWorkflow(),
|
|
781
|
+
runPrc: () => this.runPrc(),
|
|
782
|
+
runPoc: () => this.runPoc(),
|
|
783
|
+
onStartupTiming,
|
|
784
|
+
faceResults: this.provider
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
createUploadCommand(recordingId) {
|
|
788
|
+
const token = getToken();
|
|
789
|
+
const config = {
|
|
790
|
+
headers: token ? { "X-Incode-Hardware-Id": token } : void 0,
|
|
791
|
+
timeout: 3e4
|
|
792
|
+
};
|
|
793
|
+
if (this.options.config.onDeviceFaceResultsSubmissionEnabled === true) return {
|
|
794
|
+
type: "UPLOAD_FACE_RESULTS",
|
|
795
|
+
config
|
|
796
|
+
};
|
|
797
|
+
const validRecordingId = recordingId && recordingId.trim().length > 0 ? recordingId : void 0;
|
|
798
|
+
if (this.options.target === PROTECTED_CAPTURE_TARGET.authentication) return {
|
|
799
|
+
type: "UPLOAD_PROTECTED",
|
|
800
|
+
request: {
|
|
801
|
+
target: PROTECTED_CAPTURE_TARGET.authentication,
|
|
802
|
+
extras: {
|
|
803
|
+
deviceClass: getDeviceClass(),
|
|
804
|
+
hint: this.options.authHint,
|
|
805
|
+
...validRecordingId ? { recordingId: validRecordingId } : {}
|
|
806
|
+
},
|
|
807
|
+
config
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
return {
|
|
811
|
+
type: "UPLOAD_PROTECTED",
|
|
812
|
+
request: {
|
|
813
|
+
target: PROTECTED_CAPTURE_TARGET.selfie,
|
|
814
|
+
extras: { deviceClass: getDeviceClass() },
|
|
815
|
+
config: {
|
|
816
|
+
...config,
|
|
817
|
+
params: {
|
|
818
|
+
imageType: "selfie",
|
|
819
|
+
...validRecordingId ? { recordingId: validRecordingId } : {}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
mapProtectedResult(result) {
|
|
826
|
+
if (result.kind === "onDeviceFace") return {};
|
|
827
|
+
if (result.kind === "authentication") return {
|
|
828
|
+
overallStatus: result.overallStatus,
|
|
829
|
+
captureAttemptsLimit: {
|
|
830
|
+
max: this.options.config.captureAttempts ?? this.options.config.numberOfAttempts ?? 3,
|
|
831
|
+
remaining: result.attemptsRemaining ?? 0
|
|
832
|
+
},
|
|
833
|
+
candidate: "",
|
|
834
|
+
error: {
|
|
835
|
+
name: result.errorName ?? "",
|
|
836
|
+
message: result.errorName ?? ""
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
takeCaptureOnlyVideo() {
|
|
842
|
+
return this.recorder.takeCaptureOnlyVideo();
|
|
843
|
+
}
|
|
844
|
+
next() {
|
|
845
|
+
return new ProtectedFaceAttemptRuntime(this.options);
|
|
846
|
+
}
|
|
847
|
+
release() {
|
|
848
|
+
if (this.released) return;
|
|
849
|
+
this.released = true;
|
|
850
|
+
this.signalListener = void 0;
|
|
851
|
+
this.pendingSignals = [];
|
|
852
|
+
if (!this.recorderTransferred) try {
|
|
853
|
+
this.recorder.dispose();
|
|
854
|
+
} catch {}
|
|
855
|
+
try {
|
|
856
|
+
Promise.resolve(this.provider.dispose()).catch(() => void 0);
|
|
857
|
+
} catch {}
|
|
858
|
+
this.cycleOwnership.releaseByController();
|
|
859
|
+
if (!this.rawHandedOff) this.acquired?.then(({ stream }) => this.releaseRawStream(stream), () => void 0);
|
|
860
|
+
}
|
|
861
|
+
async acquireRawStream() {
|
|
862
|
+
if (!this.acquired) this.acquired = this.options.acquireCamera(this.provider);
|
|
863
|
+
const result = await this.acquired;
|
|
864
|
+
this.acquiredUsingBackCamera = result.usingBackCamera;
|
|
865
|
+
if (this.released) {
|
|
866
|
+
this.releaseRawStream(result.stream);
|
|
867
|
+
throw new Error("Protected face attempt was released");
|
|
868
|
+
}
|
|
869
|
+
return result;
|
|
870
|
+
}
|
|
871
|
+
async configureWorkflow() {
|
|
872
|
+
await this.provider.initialize({
|
|
873
|
+
autocaptureInterval: this.options.config.onDeviceFaceResultsSubmissionEnabled === true ? ON_DEVICE_MANUAL_CAPTURE_TIMEOUT_MS : this.options.config.autoCaptureTimeout * 1e3,
|
|
874
|
+
useOnDeviceWorkflow: this.options.config.onDeviceFaceResultsSubmissionEnabled === true
|
|
875
|
+
});
|
|
876
|
+
if (this.released) return;
|
|
877
|
+
this.provider.setChecksEnabled({
|
|
878
|
+
lenses: this.options.config.validateLenses ?? false,
|
|
879
|
+
mask: this.options.config.validateFaceMask ?? false,
|
|
880
|
+
closedEyes: this.options.config.validateClosedEyes ?? false,
|
|
881
|
+
headWear: this.options.config.validateHeadCover ?? false,
|
|
882
|
+
occlusion: false
|
|
883
|
+
});
|
|
884
|
+
this.provider.setCallbacks({
|
|
885
|
+
onFarAway: () => this.emitDetection("tooFar"),
|
|
886
|
+
onTooClose: () => this.emitDetection("tooClose"),
|
|
887
|
+
onTooManyFaces: () => this.emitDetection("tooManyFaces"),
|
|
888
|
+
onNoFace: () => this.emitDetection("idle"),
|
|
889
|
+
onCenterFace: () => this.emitDetection("centerFace"),
|
|
890
|
+
onGetReady: () => this.emitDetection("getReady"),
|
|
891
|
+
onGetReadyFinished: () => this.emitDetection("getReadyFinished"),
|
|
892
|
+
onDark: () => {
|
|
893
|
+
if (this.options.config.validateBrightness) this.emitDetection("dark");
|
|
894
|
+
},
|
|
895
|
+
onBlur: () => this.emitDetection("blur"),
|
|
896
|
+
onFaceAngle: () => this.emitDetection("faceAngle"),
|
|
897
|
+
onLenses: () => {
|
|
898
|
+
if (this.options.config.validateLenses) this.emitDetection("lenses");
|
|
899
|
+
},
|
|
900
|
+
onMask: () => {
|
|
901
|
+
if (this.options.config.validateFaceMask) this.emitDetection("faceMask");
|
|
902
|
+
},
|
|
903
|
+
onEyesClosed: () => {
|
|
904
|
+
if (this.options.config.validateClosedEyes) this.emitDetection("eyesClosed");
|
|
905
|
+
},
|
|
906
|
+
onHeadWear: () => {
|
|
907
|
+
if (this.options.config.validateHeadCover) this.emitDetection("headWear");
|
|
908
|
+
},
|
|
909
|
+
onFaceOccluded: () => this.emitDetection("faceOcclusion"),
|
|
910
|
+
onSwitchToManualCapture: () => {
|
|
911
|
+
if (this.options.config.onDeviceFaceResultsSubmissionEnabled !== true) this.emitDetection("manualCapture");
|
|
912
|
+
},
|
|
913
|
+
onCapture: (canvas) => {
|
|
914
|
+
this.emit({
|
|
915
|
+
type: "autoCapture",
|
|
916
|
+
preview: canvas.canvas
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
this.emitDetection("detecting");
|
|
921
|
+
}
|
|
922
|
+
async runPrc() {
|
|
923
|
+
if (this.options.delivery === "protected") await this.attachProtectedCameraMetadata();
|
|
924
|
+
if (this.released) return;
|
|
925
|
+
const wasmUtil = await this.options.dependencies.getWasmUtil();
|
|
926
|
+
if (this.released) return;
|
|
927
|
+
await wasmUtil.prc();
|
|
928
|
+
}
|
|
929
|
+
async runPoc() {
|
|
930
|
+
if (this.options.delivery === "local") return;
|
|
931
|
+
const deepsight = this.deepsightService;
|
|
932
|
+
if (!deepsight) throw new Error("Protected face Deepsight is not ready");
|
|
933
|
+
await deepsight.performVirtualCameraCheck(getToken(), "SELFIE");
|
|
934
|
+
if (this.released) return;
|
|
935
|
+
try {
|
|
936
|
+
await deepsight.finalizeProtectedCaptureMetadata();
|
|
937
|
+
} catch {
|
|
938
|
+
trueSightLog("warning", "deepsight", "faceScan", "protected capture metadata finalization failed");
|
|
939
|
+
}
|
|
940
|
+
if (this.released) return;
|
|
941
|
+
try {
|
|
942
|
+
addDeviceStats({
|
|
943
|
+
frontIdStatsAnalysisStatus: "",
|
|
944
|
+
backIdStatsAnalysisStatus: "",
|
|
945
|
+
selfieStatsAnalysisStatus: deepsight.getAnalysisStatus(),
|
|
946
|
+
motionStatus: deepsight.getMotionStatus()
|
|
947
|
+
});
|
|
948
|
+
} catch {}
|
|
949
|
+
const logs = deepsight.getPipelineState();
|
|
950
|
+
this.options.dependencies.trackCaptureAttemptFinished?.({ logs });
|
|
951
|
+
}
|
|
952
|
+
async attachProtectedCameraMetadata() {
|
|
953
|
+
const deepsight = this.deepsightService;
|
|
954
|
+
if (!deepsight) return;
|
|
955
|
+
try {
|
|
956
|
+
const { stream } = await this.acquireRawStream();
|
|
957
|
+
const [videoTrack] = stream.getVideoTracks();
|
|
958
|
+
if (videoTrack) await deepsight.checkVirtualCamera(videoTrack);
|
|
959
|
+
} catch {
|
|
960
|
+
trueSightLog("warning", "deepsight", "faceScan", "protected camera metadata attachment failed");
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
emitDetection(status) {
|
|
964
|
+
this.emit({
|
|
965
|
+
type: "detection",
|
|
966
|
+
status
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
emit(signal) {
|
|
970
|
+
if (this.released) return;
|
|
971
|
+
if (this.signalListener) this.signalListener(signal);
|
|
972
|
+
else this.pendingSignals.push(signal);
|
|
973
|
+
}
|
|
974
|
+
releaseRawStream(stream) {
|
|
975
|
+
if (this.rawHandedOff || this.rawStreamReleased) return;
|
|
976
|
+
this.rawStreamReleased = true;
|
|
977
|
+
try {
|
|
978
|
+
stopStream(stream);
|
|
979
|
+
} catch {}
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
//#endregion
|
|
983
|
+
//#region src/internal/faceCapture/protectedFaceController.ts
|
|
984
|
+
/**
|
|
985
|
+
* Binds one face-machine variant to a fixed native target and delivery mode.
|
|
986
|
+
* The capability-bearing runtime and lifecycle input never leave this callback
|
|
987
|
+
* actor's closure, so parent snapshots contain only presentation/output data.
|
|
988
|
+
*/
|
|
989
|
+
function createProtectedFaceControllerActor(options) {
|
|
990
|
+
return fromCallback(({ sendBack, receive }) => {
|
|
991
|
+
let controllerInput;
|
|
992
|
+
let runtime;
|
|
993
|
+
let deepsightService;
|
|
994
|
+
let deepsightState = "idle";
|
|
995
|
+
let signalCleanup;
|
|
996
|
+
let lifecycleCleanup;
|
|
997
|
+
let sendLifecycle;
|
|
998
|
+
let committedPreview;
|
|
999
|
+
let pendingAutoPreview;
|
|
1000
|
+
let recording = {};
|
|
1001
|
+
let generation = 0;
|
|
1002
|
+
let active = true;
|
|
1003
|
+
let diagnosticAttempt = 0;
|
|
1004
|
+
let diagnosticStartedAt;
|
|
1005
|
+
const logDiagnostic = (event, fields = {}) => {
|
|
1006
|
+
if (diagnosticStartedAt === void 0) return;
|
|
1007
|
+
logFaceCameraDiagnostic(event, {
|
|
1008
|
+
attempt: diagnosticAttempt,
|
|
1009
|
+
target: options.target === PROTECTED_CAPTURE_TARGET.authentication ? "authentication" : "selfie",
|
|
1010
|
+
delivery: options.delivery,
|
|
1011
|
+
elapsedMs: Math.round(getTimestamp() - diagnosticStartedAt),
|
|
1012
|
+
...fields
|
|
1013
|
+
});
|
|
1014
|
+
};
|
|
1015
|
+
const getControllerInput = () => {
|
|
1016
|
+
if (!controllerInput) throw new Error("Protected face controller is not bound");
|
|
1017
|
+
return controllerInput;
|
|
1018
|
+
};
|
|
1019
|
+
const ensureRuntime = () => {
|
|
1020
|
+
if (!runtime) {
|
|
1021
|
+
const input = getControllerInput();
|
|
1022
|
+
const capabilities = options.createCapabilities();
|
|
1023
|
+
runtime = new ProtectedFaceAttemptRuntime({
|
|
1024
|
+
config: input.config,
|
|
1025
|
+
dependencies: input.dependencies,
|
|
1026
|
+
target: options.target,
|
|
1027
|
+
delivery: options.delivery,
|
|
1028
|
+
authHint: input.authHint,
|
|
1029
|
+
faceDetection: capabilities.faceDetection,
|
|
1030
|
+
cycle: capabilities.cycle,
|
|
1031
|
+
acquireCamera: (provider) => initializeCamera({
|
|
1032
|
+
config: input.config,
|
|
1033
|
+
provider
|
|
1034
|
+
})
|
|
1035
|
+
});
|
|
1036
|
+
if (deepsightService) runtime.setDeepsightService(deepsightService);
|
|
1037
|
+
}
|
|
1038
|
+
return runtime;
|
|
1039
|
+
};
|
|
1040
|
+
const releaseCurrent = (reason) => {
|
|
1041
|
+
logDiagnostic("attempt-release", { releaseReason: reason });
|
|
1042
|
+
diagnosticStartedAt = void 0;
|
|
1043
|
+
generation += 1;
|
|
1044
|
+
signalCleanup?.();
|
|
1045
|
+
signalCleanup = void 0;
|
|
1046
|
+
sendLifecycle?.({
|
|
1047
|
+
type: "RELEASE",
|
|
1048
|
+
reason
|
|
1049
|
+
});
|
|
1050
|
+
sendLifecycle = void 0;
|
|
1051
|
+
lifecycleCleanup?.();
|
|
1052
|
+
lifecycleCleanup = void 0;
|
|
1053
|
+
runtime?.release();
|
|
1054
|
+
runtime = void 0;
|
|
1055
|
+
try {
|
|
1056
|
+
deepsightService?.cleanup();
|
|
1057
|
+
} catch {}
|
|
1058
|
+
deepsightService = void 0;
|
|
1059
|
+
deepsightState = "idle";
|
|
1060
|
+
committedPreview = void 0;
|
|
1061
|
+
pendingAutoPreview = void 0;
|
|
1062
|
+
recording = {};
|
|
1063
|
+
};
|
|
1064
|
+
const initializeDeepsight = () => {
|
|
1065
|
+
if (deepsightState === "ready") {
|
|
1066
|
+
sendBack({ type: "PROTECTED_DEEPSIGHT_READY" });
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
if (deepsightState === "initializing") return;
|
|
1070
|
+
deepsightState = "initializing";
|
|
1071
|
+
const input = getControllerInput();
|
|
1072
|
+
const ownerGeneration = generation;
|
|
1073
|
+
options.initializeDeepsight({
|
|
1074
|
+
ds: input.config.ds,
|
|
1075
|
+
storage: input.dependencies.storage
|
|
1076
|
+
}).then((service) => {
|
|
1077
|
+
if (!active || generation !== ownerGeneration) {
|
|
1078
|
+
try {
|
|
1079
|
+
service.cleanup();
|
|
1080
|
+
} catch {}
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
deepsightService = service;
|
|
1084
|
+
deepsightState = "ready";
|
|
1085
|
+
runtime?.setDeepsightService(service);
|
|
1086
|
+
sendBack({ type: "PROTECTED_DEEPSIGHT_READY" });
|
|
1087
|
+
const preview = pendingAutoPreview;
|
|
1088
|
+
pendingAutoPreview = void 0;
|
|
1089
|
+
if (preview) queueMicrotask(() => {
|
|
1090
|
+
if (active && generation === ownerGeneration) sendBack({
|
|
1091
|
+
type: "PROTECTED_NATIVE_AUTO_CAPTURE",
|
|
1092
|
+
preview
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
}, () => {
|
|
1096
|
+
if (!active || generation !== ownerGeneration) return;
|
|
1097
|
+
deepsightState = "failed";
|
|
1098
|
+
sendBack({ type: "PROTECTED_DEEPSIGHT_FAILED" });
|
|
1099
|
+
}).finally(() => {
|
|
1100
|
+
if (generation === ownerGeneration) {}
|
|
1101
|
+
});
|
|
1102
|
+
};
|
|
1103
|
+
const finalizeLocalCapture = async (owner, ownerGeneration) => {
|
|
1104
|
+
const preview = committedPreview;
|
|
1105
|
+
if (!preview) throw new Error("selfieCaptureOnly: no captured image");
|
|
1106
|
+
let metadata = "";
|
|
1107
|
+
try {
|
|
1108
|
+
metadata = deepsightService?.getMetadata() ?? "";
|
|
1109
|
+
} catch {
|
|
1110
|
+
metadata = "";
|
|
1111
|
+
}
|
|
1112
|
+
const canvas = new IncodeCanvas(preview);
|
|
1113
|
+
const blobData = canvas.getBlobData();
|
|
1114
|
+
if (!blobData) throw new Error("selfieCaptureOnly: failed to build image blob");
|
|
1115
|
+
const result = {
|
|
1116
|
+
imageBase64: canvas.getBase64Image() ?? "",
|
|
1117
|
+
blob: blobData.blob,
|
|
1118
|
+
url: blobData.url,
|
|
1119
|
+
metadata,
|
|
1120
|
+
videoBase64: owner.takeCaptureOnlyVideo()
|
|
1121
|
+
};
|
|
1122
|
+
if (active && runtime === owner && generation === ownerGeneration) {
|
|
1123
|
+
sendBack({
|
|
1124
|
+
type: "PROTECTED_LOCAL_DONE",
|
|
1125
|
+
attemptId: owner.attemptId,
|
|
1126
|
+
result
|
|
1127
|
+
});
|
|
1128
|
+
logDiagnostic("upload-complete");
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
const forwardLifecycleEvent = (event, owner, ownerGeneration) => {
|
|
1132
|
+
if (!active || runtime !== owner || generation !== ownerGeneration) return;
|
|
1133
|
+
switch (event.type) {
|
|
1134
|
+
case "PROTECTED_ATTEMPT_READY":
|
|
1135
|
+
logDiagnostic("camera-ready");
|
|
1136
|
+
sendBack({
|
|
1137
|
+
...event,
|
|
1138
|
+
usingBackCamera: owner.usingBackCamera
|
|
1139
|
+
});
|
|
1140
|
+
return;
|
|
1141
|
+
case "PROTECTED_AUTO_COMMITTED":
|
|
1142
|
+
case "PROTECTED_MANUAL_COMMITTED":
|
|
1143
|
+
logDiagnostic("capture-selected", { captureKind: event.type === "PROTECTED_AUTO_COMMITTED" ? "auto" : "manual" });
|
|
1144
|
+
committedPreview = event.preview;
|
|
1145
|
+
sendBack(event);
|
|
1146
|
+
return;
|
|
1147
|
+
case "PROTECTED_ATTEMPT_FINALIZED":
|
|
1148
|
+
logDiagnostic("attempt-finalized");
|
|
1149
|
+
recording = event.recording;
|
|
1150
|
+
sendBack(event);
|
|
1151
|
+
return;
|
|
1152
|
+
case "PROTECTED_UPLOAD_DONE":
|
|
1153
|
+
if (event.result.kind === "id") {
|
|
1154
|
+
sendBack({
|
|
1155
|
+
type: "PROTECTED_ATTEMPT_FAILED",
|
|
1156
|
+
attemptId: event.attemptId,
|
|
1157
|
+
failure: {
|
|
1158
|
+
stage: "upload",
|
|
1159
|
+
code: "protected-attempt-unexpected-result"
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
sendBack({
|
|
1165
|
+
type: "PROTECTED_UPLOAD_MAPPED",
|
|
1166
|
+
attemptId: event.attemptId,
|
|
1167
|
+
response: owner.mapProtectedResult(event.result)
|
|
1168
|
+
});
|
|
1169
|
+
logDiagnostic("upload-complete");
|
|
1170
|
+
return;
|
|
1171
|
+
case "PROTECTED_ATTEMPT_FAILED":
|
|
1172
|
+
logDiagnostic("attempt-failed", {
|
|
1173
|
+
failureStage: event.failure.stage,
|
|
1174
|
+
failureCode: event.failure.code
|
|
1175
|
+
});
|
|
1176
|
+
sendBack(event);
|
|
1177
|
+
return;
|
|
1178
|
+
default: sendBack(event);
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
const start = (visible) => {
|
|
1182
|
+
if (lifecycleCleanup) {
|
|
1183
|
+
sendLifecycle?.({ type: visible ? "FOREGROUND" : "BACKGROUND" });
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
const owner = ensureRuntime();
|
|
1187
|
+
diagnosticAttempt += 1;
|
|
1188
|
+
diagnosticStartedAt = getTimestamp();
|
|
1189
|
+
logDiagnostic("attempt-start");
|
|
1190
|
+
const ownerGeneration = generation;
|
|
1191
|
+
signalCleanup = owner.subscribeSignals((signal) => {
|
|
1192
|
+
if (!active || runtime !== owner || generation !== ownerGeneration) return;
|
|
1193
|
+
if (signal.type === "detection") {
|
|
1194
|
+
sendBack({
|
|
1195
|
+
type: "DETECTION_UPDATE",
|
|
1196
|
+
status: signal.status
|
|
1197
|
+
});
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (deepsightState === "ready") sendBack({
|
|
1201
|
+
type: "PROTECTED_NATIVE_AUTO_CAPTURE",
|
|
1202
|
+
preview: signal.preview
|
|
1203
|
+
});
|
|
1204
|
+
else pendingAutoPreview ??= signal.preview;
|
|
1205
|
+
});
|
|
1206
|
+
lifecycleCleanup = runProtectedAttempt({
|
|
1207
|
+
input: owner.toActorInput((timing) => {
|
|
1208
|
+
logDiagnostic("camera-startup", timing);
|
|
1209
|
+
}),
|
|
1210
|
+
sendBack: (event) => forwardLifecycleEvent(event, owner, ownerGeneration),
|
|
1211
|
+
receive: (listener) => {
|
|
1212
|
+
sendLifecycle = listener;
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
sendLifecycle?.({ type: visible ? "FOREGROUND" : "BACKGROUND" });
|
|
1216
|
+
};
|
|
1217
|
+
receive((command) => {
|
|
1218
|
+
if (command.type === "BIND_PROTECTED_FACE_CONTROLLER") {
|
|
1219
|
+
controllerInput ??= command.input;
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
switch (command.type) {
|
|
1223
|
+
case "INITIALIZE_DEEPSIGHT":
|
|
1224
|
+
initializeDeepsight();
|
|
1225
|
+
return;
|
|
1226
|
+
case "PRE_ACQUIRE": {
|
|
1227
|
+
const owner = ensureRuntime();
|
|
1228
|
+
const ownerGeneration = generation;
|
|
1229
|
+
owner.preAcquire().then(() => {
|
|
1230
|
+
if (active && runtime === owner && generation === ownerGeneration) sendBack({
|
|
1231
|
+
type: "PROTECTED_PRE_ACQUIRE_DONE",
|
|
1232
|
+
usingBackCamera: owner.usingBackCamera
|
|
1233
|
+
});
|
|
1234
|
+
}, (error) => {
|
|
1235
|
+
if (active && runtime === owner && generation === ownerGeneration) sendBack({
|
|
1236
|
+
type: "PROTECTED_PRE_ACQUIRE_FAILED",
|
|
1237
|
+
error
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
case "START":
|
|
1243
|
+
start(command.visible);
|
|
1244
|
+
return;
|
|
1245
|
+
case "AUTO_CAPTURED":
|
|
1246
|
+
case "MANUAL_CAPTURE":
|
|
1247
|
+
case "FINALIZE":
|
|
1248
|
+
if (deepsightState !== "ready") return;
|
|
1249
|
+
sendLifecycle?.(command);
|
|
1250
|
+
return;
|
|
1251
|
+
case "BACKGROUND":
|
|
1252
|
+
case "FOREGROUND":
|
|
1253
|
+
sendLifecycle?.(command);
|
|
1254
|
+
return;
|
|
1255
|
+
case "UPLOAD": {
|
|
1256
|
+
const owner = runtime;
|
|
1257
|
+
if (!owner) return;
|
|
1258
|
+
logDiagnostic("upload-start");
|
|
1259
|
+
if (options.delivery === "protected") {
|
|
1260
|
+
sendLifecycle?.(owner.createUploadCommand(recording.recordingId));
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
finalizeLocalCapture(owner, generation).catch(() => {
|
|
1264
|
+
if (active && runtime === owner) sendBack({
|
|
1265
|
+
type: "PROTECTED_ATTEMPT_FAILED",
|
|
1266
|
+
attemptId: owner.attemptId,
|
|
1267
|
+
failure: {
|
|
1268
|
+
stage: "upload",
|
|
1269
|
+
code: "protected-attempt-local-finalize-failed"
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
case "RESET":
|
|
1276
|
+
releaseCurrent("retry");
|
|
1277
|
+
return;
|
|
1278
|
+
case "RELEASE": releaseCurrent(command.reason);
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
return () => {
|
|
1282
|
+
active = false;
|
|
1283
|
+
releaseCurrent("externalStop");
|
|
1284
|
+
};
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
//#endregion
|
|
1288
|
+
//#region src/internal/faceCapture/protectedFaceComposition.ts
|
|
1289
|
+
/**
|
|
1290
|
+
* Private composition root for protected still-face variants. Provider
|
|
1291
|
+
* construction remains in Infra; the controller receives only capability
|
|
1292
|
+
* interfaces/factories and owns them in its callback closure.
|
|
1293
|
+
*/
|
|
1294
|
+
function createDefaultProtectedFaceControllerActor(options) {
|
|
1295
|
+
return createProtectedFaceControllerActor({
|
|
1296
|
+
...options,
|
|
1297
|
+
createCapabilities: createProtectedFaceCaptureCapabilities,
|
|
1298
|
+
initializeDeepsight: initializeDeepsightSession
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
//#endregion
|
|
1302
|
+
//#region src/internal/faceCapture/faceCaptureSetup.ts
|
|
1303
|
+
const getAttemptsFromConfig = (config) => {
|
|
1304
|
+
return config.captureAttempts ?? config.numberOfAttempts ?? 3;
|
|
1305
|
+
};
|
|
1306
|
+
/**
|
|
1307
|
+
* The protected face controller is invoked at the machine root, so a started
|
|
1308
|
+
* machine has it for its whole lifetime and every guarded send below behaves
|
|
1309
|
+
* like a plain `sendTo`. Harnesses that start the machine from a resolved
|
|
1310
|
+
* snapshot carry no children (the Storybook state stories seed managers that
|
|
1311
|
+
* way), and XState throws on a send to a missing actor, so each send checks
|
|
1312
|
+
* for the controller first.
|
|
1313
|
+
*
|
|
1314
|
+
* Only sends that run after the machine has started may use this guard. A
|
|
1315
|
+
* root `entry` action runs inside the initial transition, before the actor
|
|
1316
|
+
* has a committed snapshot; XState delivers a plain `sendTo` from there
|
|
1317
|
+
* through its deferred-actor path, whereas this check cannot see the child
|
|
1318
|
+
* yet and would skip the send.
|
|
1319
|
+
*/
|
|
1320
|
+
function isProtectedFaceControllerRunning(self) {
|
|
1321
|
+
return self.getSnapshot()?.children?.["protectedFaceController"] !== void 0;
|
|
1322
|
+
}
|
|
1323
|
+
const faceCaptureMachine = setup({
|
|
1324
|
+
types: {
|
|
1325
|
+
context: {},
|
|
1326
|
+
events: {},
|
|
1327
|
+
input: {}
|
|
1328
|
+
},
|
|
1329
|
+
actors: {
|
|
1330
|
+
checkPermission: fromPromise(async () => {
|
|
1331
|
+
return checkPermission();
|
|
1332
|
+
}),
|
|
1333
|
+
requestPermission: fromPromise(async ({ input }) => {
|
|
1334
|
+
return invokeRequestPermissionActor(input);
|
|
1335
|
+
}),
|
|
1336
|
+
protectedFaceController: createDefaultProtectedFaceControllerActor({
|
|
1337
|
+
target: PROTECTED_CAPTURE_TARGET.selfie,
|
|
1338
|
+
delivery: "protected"
|
|
1339
|
+
}),
|
|
1340
|
+
runAvatar: fromCallback(({ input, sendBack }) => {
|
|
1341
|
+
const { obfuscateWithAvatar, avatarVariant } = concealmentToAvatar(input.config.selfieConcealmentOption);
|
|
1342
|
+
if (!obfuscateWithAvatar || !input.stream) return () => {};
|
|
1343
|
+
const stream = input.stream;
|
|
1344
|
+
const variant = avatarVariant ?? "avatar-3d";
|
|
1345
|
+
let handle = null;
|
|
1346
|
+
let cancelled = false;
|
|
1347
|
+
(async () => {
|
|
1348
|
+
try {
|
|
1349
|
+
const created = await createFaceAvatar({
|
|
1350
|
+
variant,
|
|
1351
|
+
stream,
|
|
1352
|
+
assets: input.config.avatarAssets
|
|
1353
|
+
});
|
|
1354
|
+
if (cancelled) {
|
|
1355
|
+
created.stop();
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
handle = created;
|
|
1359
|
+
sendBack({
|
|
1360
|
+
type: "AVATAR_READY",
|
|
1361
|
+
canvas: created.canvas
|
|
1362
|
+
});
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
console.warn("[face-avatar] failed to start avatar overlay", err);
|
|
1365
|
+
}
|
|
1366
|
+
})();
|
|
1367
|
+
return () => {
|
|
1368
|
+
cancelled = true;
|
|
1369
|
+
handle?.stop();
|
|
1370
|
+
};
|
|
1371
|
+
}),
|
|
1372
|
+
manualUploadMachine: manualFaceUploadMachine,
|
|
1373
|
+
prepareFaceUpload: fromPromise(async () => {
|
|
1374
|
+
throw new Error("prepareFaceUpload must be provided by variant");
|
|
1375
|
+
}),
|
|
1376
|
+
uploadFace: fromPromise(async () => new Promise(() => {})),
|
|
1377
|
+
processFace: fromPromise(async () => {
|
|
1378
|
+
throw new Error("processFace must be provided by variant");
|
|
1379
|
+
})
|
|
1380
|
+
},
|
|
1381
|
+
actions: {
|
|
1382
|
+
bindProtectedFaceController: sendTo("protectedFaceController", ({ context }) => ({
|
|
1383
|
+
type: "BIND_PROTECTED_FACE_CONTROLLER",
|
|
1384
|
+
input: {
|
|
1385
|
+
config: context.config,
|
|
1386
|
+
dependencies: context.dependencies,
|
|
1387
|
+
authHint: context.authHint
|
|
1388
|
+
}
|
|
1389
|
+
})),
|
|
1390
|
+
stopMediaStream: assign(() => {
|
|
1391
|
+
return {
|
|
1392
|
+
stream: void 0,
|
|
1393
|
+
avatarCanvas: void 0,
|
|
1394
|
+
usingBackCamera: false
|
|
1395
|
+
};
|
|
1396
|
+
}),
|
|
1397
|
+
setCameraPreAcquired: assign({ usingBackCamera: ({ event }) => event.usingBackCamera }),
|
|
1398
|
+
trackTutorial: () => void 0,
|
|
1399
|
+
trackContinue: () => {},
|
|
1400
|
+
resetContext: assign(({ context }) => {
|
|
1401
|
+
return {
|
|
1402
|
+
stream: void 0,
|
|
1403
|
+
usingBackCamera: false,
|
|
1404
|
+
error: void 0,
|
|
1405
|
+
detectionStatus: "idle",
|
|
1406
|
+
capturedImage: void 0,
|
|
1407
|
+
faceCoordinates: void 0,
|
|
1408
|
+
uploadResponse: void 0,
|
|
1409
|
+
processResponse: void 0,
|
|
1410
|
+
attemptsRemaining: getAttemptsFromConfig(context.config),
|
|
1411
|
+
uploadError: void 0,
|
|
1412
|
+
permissionResult: void 0,
|
|
1413
|
+
deepsightReady: false,
|
|
1414
|
+
manualCaptureTriggered: false,
|
|
1415
|
+
captureOnlyResult: void 0,
|
|
1416
|
+
avatarCanvas: void 0
|
|
1417
|
+
};
|
|
1418
|
+
}),
|
|
1419
|
+
clearUploadFailure: assign({
|
|
1420
|
+
uploadError: () => void 0,
|
|
1421
|
+
detectionStatus: () => "idle",
|
|
1422
|
+
capturedImage: () => void 0
|
|
1423
|
+
}),
|
|
1424
|
+
clearProtectedAttempt: assign(() => {
|
|
1425
|
+
return {
|
|
1426
|
+
stream: void 0,
|
|
1427
|
+
avatarCanvas: void 0,
|
|
1428
|
+
usingBackCamera: false
|
|
1429
|
+
};
|
|
1430
|
+
}),
|
|
1431
|
+
decrementAttemptsRemaining: assign(({ context }) => ({ attemptsRemaining: context.attemptsRemaining - 1 })),
|
|
1432
|
+
setUploadErrorFromUploadValidation: assign({ uploadError: () => FACE_ERROR_CODES.SERVER }),
|
|
1433
|
+
setTerminalError: assign({ error: () => "Authentication failed" }),
|
|
1434
|
+
cleanup: () => void 0,
|
|
1435
|
+
setPermissionResultFromEvent: assign({ permissionResult: ({ event }) => event.output }),
|
|
1436
|
+
setPermissionDenied: assign({ permissionResult: () => "denied" }),
|
|
1437
|
+
setPermissionPrompt: assign({ permissionResult: () => "prompt" }),
|
|
1438
|
+
setPermissionRefresh: assign({ permissionResult: () => "refresh" }),
|
|
1439
|
+
setDeepsightReady: assign({ deepsightReady: () => true }),
|
|
1440
|
+
setDeepsightNotReady: assign({ deepsightReady: () => false }),
|
|
1441
|
+
setErrorFromEvent: assign({ error: ({ event }) => String(event.error) }),
|
|
1442
|
+
setDetectionStatusDetecting: assign({ detectionStatus: ({ context }) => context.manualCaptureTriggered ? "manualCapture" : "detecting" }),
|
|
1443
|
+
setDetectionStatusFromEvent: assign({ detectionStatus: ({ event }) => event.status }),
|
|
1444
|
+
setManualCaptureTriggered: assign({ manualCaptureTriggered: ({ context, event }) => {
|
|
1445
|
+
const status = event.status;
|
|
1446
|
+
return context.manualCaptureTriggered || status === "manualCapture";
|
|
1447
|
+
} }),
|
|
1448
|
+
setAvatarCanvas: assign({ avatarCanvas: ({ event }) => event.canvas }),
|
|
1449
|
+
clearAvatarCanvas: assign({ avatarCanvas: () => void 0 }),
|
|
1450
|
+
setCapturedImageFromEvent: assign({
|
|
1451
|
+
capturedImage: ({ event }) => event.canvas,
|
|
1452
|
+
faceCoordinates: ({ event }) => event.faceCoordinates
|
|
1453
|
+
}),
|
|
1454
|
+
setProtectedAttemptReady: assign({
|
|
1455
|
+
stream: ({ event }) => event.displayStream,
|
|
1456
|
+
usingBackCamera: ({ event }) => event.usingBackCamera
|
|
1457
|
+
}),
|
|
1458
|
+
setProtectedCommittedPreview: assign({
|
|
1459
|
+
capturedImage: ({ event }) => new IncodeCanvas(event.preview),
|
|
1460
|
+
faceCoordinates: () => void 0,
|
|
1461
|
+
detectionStatus: () => "success"
|
|
1462
|
+
}),
|
|
1463
|
+
setProtectedFinalization: assign({
|
|
1464
|
+
uploadRecordingId: ({ event }) => event.recording.recordingId ?? null,
|
|
1465
|
+
stream: ({ context, event }) => event.displayStream ?? context.stream
|
|
1466
|
+
}),
|
|
1467
|
+
setProtectedUploadResponse: assign({ uploadResponse: ({ event }) => event.response }),
|
|
1468
|
+
setProtectedLocalResult: assign({ captureOnlyResult: ({ event }) => event.result }),
|
|
1469
|
+
setProtectedFailure: assign({ error: ({ event }) => event.failure.code }),
|
|
1470
|
+
setProtectedVisibility: assign({ protectedVisible: ({ event }) => event.visible }),
|
|
1471
|
+
setUploadPreparationFromEvent: assign({
|
|
1472
|
+
encryptedBase64Image: ({ event }) => event.output.encryptedBase64Image,
|
|
1473
|
+
uploadRecordingId: ({ event }) => event.output.recordingId
|
|
1474
|
+
}),
|
|
1475
|
+
setUploadResponseFromEvent: assign({ uploadResponse: ({ event }) => event.output }),
|
|
1476
|
+
setProcessResponseFromEvent: assign({ processResponse: ({ event }) => event.output }),
|
|
1477
|
+
setServerErrorAndDecrement: assign(({ context }) => ({
|
|
1478
|
+
uploadError: FACE_ERROR_CODES.SERVER,
|
|
1479
|
+
attemptsRemaining: context.attemptsRemaining - 1
|
|
1480
|
+
})),
|
|
1481
|
+
sendLabelInspection: () => {
|
|
1482
|
+
sendLabelInspectionEvent();
|
|
1483
|
+
},
|
|
1484
|
+
flagFaceManualReview: () => {
|
|
1485
|
+
flagFaceManualReview();
|
|
1486
|
+
},
|
|
1487
|
+
preloadRecordingProvider: () => void 0,
|
|
1488
|
+
warmAvatarProvider: ({ context }) => {
|
|
1489
|
+
const { obfuscateWithAvatar, avatarVariant } = concealmentToAvatar(context.config.selfieConcealmentOption);
|
|
1490
|
+
if (!obfuscateWithAvatar || !avatarVariant) return;
|
|
1491
|
+
warmFaceAvatar({
|
|
1492
|
+
variant: avatarVariant,
|
|
1493
|
+
assets: context.config.avatarAssets
|
|
1494
|
+
});
|
|
1495
|
+
},
|
|
1496
|
+
releaseAvatarWarmup: () => {
|
|
1497
|
+
releaseFaceAvatarWarmup();
|
|
1498
|
+
},
|
|
1499
|
+
noOp: () => void 0,
|
|
1500
|
+
forwardToManualUpload: sendTo("manualUpload", ({ event }) => event),
|
|
1501
|
+
sendProtectedPreAcquire: enqueueActions(({ enqueue, self }) => {
|
|
1502
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1503
|
+
enqueue.sendTo("protectedFaceController", { type: "PRE_ACQUIRE" });
|
|
1504
|
+
}),
|
|
1505
|
+
startProtectedAttempt: enqueueActions(({ enqueue, self, context }) => {
|
|
1506
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1507
|
+
enqueue.sendTo("protectedFaceController", {
|
|
1508
|
+
type: "START",
|
|
1509
|
+
visible: context.protectedVisible
|
|
1510
|
+
});
|
|
1511
|
+
}),
|
|
1512
|
+
initializeProtectedDeepsight: enqueueActions(({ enqueue, self }) => {
|
|
1513
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1514
|
+
enqueue.sendTo("protectedFaceController", { type: "INITIALIZE_DEEPSIGHT" });
|
|
1515
|
+
}),
|
|
1516
|
+
resetProtectedController: enqueueActions(({ enqueue, self }) => {
|
|
1517
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1518
|
+
enqueue.sendTo("protectedFaceController", { type: "RESET" });
|
|
1519
|
+
}),
|
|
1520
|
+
sendProtectedAutoCapture: enqueueActions(({ enqueue, self, event }) => {
|
|
1521
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1522
|
+
enqueue.sendTo("protectedFaceController", {
|
|
1523
|
+
type: "AUTO_CAPTURED",
|
|
1524
|
+
preview: event.preview
|
|
1525
|
+
});
|
|
1526
|
+
}),
|
|
1527
|
+
sendProtectedManualCapture: enqueueActions(({ enqueue, self }) => {
|
|
1528
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1529
|
+
enqueue.sendTo("protectedFaceController", { type: "MANUAL_CAPTURE" });
|
|
1530
|
+
}),
|
|
1531
|
+
sendProtectedFinalize: enqueueActions(({ enqueue, self }) => {
|
|
1532
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1533
|
+
enqueue.sendTo("protectedFaceController", { type: "FINALIZE" });
|
|
1534
|
+
}),
|
|
1535
|
+
sendProtectedUploadIfNeeded: enqueueActions(({ enqueue, self }) => {
|
|
1536
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1537
|
+
enqueue.sendTo("protectedFaceController", { type: "UPLOAD" });
|
|
1538
|
+
}),
|
|
1539
|
+
sendProtectedVisibility: enqueueActions(({ enqueue, self, event }) => {
|
|
1540
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1541
|
+
enqueue.sendTo("protectedFaceController", { type: event.visible ? "FOREGROUND" : "BACKGROUND" });
|
|
1542
|
+
}),
|
|
1543
|
+
releaseProtectedAttempt: enqueueActions(({ enqueue, self, event }) => {
|
|
1544
|
+
if (!isProtectedFaceControllerRunning(self)) return;
|
|
1545
|
+
enqueue.sendTo("protectedFaceController", {
|
|
1546
|
+
type: "RELEASE",
|
|
1547
|
+
reason: event.reason ?? "externalStop"
|
|
1548
|
+
});
|
|
1549
|
+
})
|
|
1550
|
+
},
|
|
1551
|
+
guards: {
|
|
1552
|
+
hasShowTutorial: ({ context }) => context.config.showTutorial,
|
|
1553
|
+
hasAgeAssurance: ({ context }) => context.config.ageAssurance === true,
|
|
1554
|
+
hasManualUploadEnabled: () => false,
|
|
1555
|
+
isManualUploadExhausted: ({ event }) => event.output?.result === "exhausted",
|
|
1556
|
+
isPermissionGranted: ({ event }) => {
|
|
1557
|
+
if ("output" in event) return event.output === "granted";
|
|
1558
|
+
return false;
|
|
1559
|
+
},
|
|
1560
|
+
isReadyForCapture: ({ context, event }) => {
|
|
1561
|
+
return isReadyForCaptureFromPermission(resolveStoredPermissionResult("output" in event ? event.output : void 0, context.permissionResult), context.config.ds);
|
|
1562
|
+
},
|
|
1563
|
+
needsMotionPrimeOnly: ({ context, event }) => {
|
|
1564
|
+
return needsMotionPrimeOnlyFromPermission(resolveStoredPermissionResult("output" in event ? event.output : void 0, context.permissionResult), context.config.ds);
|
|
1565
|
+
},
|
|
1566
|
+
isPermissionDeniedError: ({ event }) => {
|
|
1567
|
+
if ("error" in event) {
|
|
1568
|
+
const error = event.error;
|
|
1569
|
+
return error?.name === "NotAllowedError" || error?.name === "PermissionDeniedError";
|
|
1570
|
+
}
|
|
1571
|
+
return false;
|
|
1572
|
+
},
|
|
1573
|
+
hasStream: ({ context }) => context.stream !== void 0,
|
|
1574
|
+
isCameraAndDeepsightReady: ({ context }) => context.stream !== void 0 && context.deepsightReady,
|
|
1575
|
+
hasAttemptsRemaining: ({ context }) => context.attemptsRemaining > 0,
|
|
1576
|
+
hasCapturedImage: ({ context }) => context.capturedImage !== void 0,
|
|
1577
|
+
canCommitProtectedManual: ({ context }) => context.manualCaptureTriggered && context.config.onDeviceFaceResultsSubmissionEnabled !== true,
|
|
1578
|
+
isProtectedManualRefusal: ({ event }) => event.failure.stage === "manualCommit",
|
|
1579
|
+
isProtectedUploadFailure: ({ event }) => event.failure.stage === "upload",
|
|
1580
|
+
hasUploadValidationError: () => false,
|
|
1581
|
+
isTerminalUploadError: () => false,
|
|
1582
|
+
isNoAttemptsTerminal: () => false
|
|
1583
|
+
}
|
|
1584
|
+
}).createMachine({
|
|
1585
|
+
id: "faceCapture",
|
|
1586
|
+
initial: "idle",
|
|
1587
|
+
entry: "bindProtectedFaceController",
|
|
1588
|
+
context: ({ input }) => ({
|
|
1589
|
+
config: input.config,
|
|
1590
|
+
dependencies: input.dependencies,
|
|
1591
|
+
authHint: input.authHint,
|
|
1592
|
+
stream: void 0,
|
|
1593
|
+
usingBackCamera: false,
|
|
1594
|
+
error: void 0,
|
|
1595
|
+
detectionStatus: "idle",
|
|
1596
|
+
capturedImage: void 0,
|
|
1597
|
+
faceCoordinates: void 0,
|
|
1598
|
+
uploadResponse: void 0,
|
|
1599
|
+
processResponse: void 0,
|
|
1600
|
+
attemptsRemaining: getAttemptsFromConfig(input.config),
|
|
1601
|
+
uploadError: void 0,
|
|
1602
|
+
permissionResult: void 0,
|
|
1603
|
+
deepsightReady: false,
|
|
1604
|
+
encryptedBase64Image: void 0,
|
|
1605
|
+
uploadRecordingId: void 0,
|
|
1606
|
+
manualCaptureTriggered: false,
|
|
1607
|
+
captureOnlyResult: void 0,
|
|
1608
|
+
avatarCanvas: void 0,
|
|
1609
|
+
faceFeed: void 0,
|
|
1610
|
+
protectedVisible: true
|
|
1611
|
+
}),
|
|
1612
|
+
invoke: {
|
|
1613
|
+
id: "protectedFaceController",
|
|
1614
|
+
src: "protectedFaceController"
|
|
1615
|
+
},
|
|
1616
|
+
on: {
|
|
1617
|
+
QUIT: { target: "#faceCapture.closed" },
|
|
1618
|
+
PROTECTED_VISIBILITY: { actions: "setProtectedVisibility" },
|
|
1619
|
+
PROTECTED_DEEPSIGHT_READY: { actions: "setDeepsightReady" },
|
|
1620
|
+
PROTECTED_DEEPSIGHT_FAILED: { actions: "setDeepsightNotReady" }
|
|
1621
|
+
},
|
|
1622
|
+
states: {
|
|
1623
|
+
idle: { on: { LOAD: [
|
|
1624
|
+
{
|
|
1625
|
+
target: "tutorial",
|
|
1626
|
+
guard: "hasShowTutorial"
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
target: "tutorial",
|
|
1630
|
+
guard: "hasAgeAssurance"
|
|
1631
|
+
},
|
|
1632
|
+
{ target: "loading" }
|
|
1633
|
+
] } },
|
|
1634
|
+
loading: {
|
|
1635
|
+
entry: [
|
|
1636
|
+
"preloadRecordingProvider",
|
|
1637
|
+
"warmAvatarProvider",
|
|
1638
|
+
"initializeProtectedDeepsight"
|
|
1639
|
+
],
|
|
1640
|
+
invoke: {
|
|
1641
|
+
id: "checkPermissionLoading",
|
|
1642
|
+
src: "checkPermission",
|
|
1643
|
+
onDone: [{
|
|
1644
|
+
target: "capture",
|
|
1645
|
+
guard: "isReadyForCapture",
|
|
1646
|
+
actions: "setPermissionResultFromEvent"
|
|
1647
|
+
}, {
|
|
1648
|
+
target: "permissions",
|
|
1649
|
+
actions: "setPermissionResultFromEvent"
|
|
1650
|
+
}]
|
|
1651
|
+
}
|
|
1652
|
+
},
|
|
1653
|
+
tutorial: {
|
|
1654
|
+
initial: "checkingPermission",
|
|
1655
|
+
entry: [
|
|
1656
|
+
"trackTutorial",
|
|
1657
|
+
"preloadRecordingProvider",
|
|
1658
|
+
"warmAvatarProvider"
|
|
1659
|
+
],
|
|
1660
|
+
on: { GO_TO_MANUAL_UPLOAD: {
|
|
1661
|
+
guard: "hasManualUploadEnabled",
|
|
1662
|
+
target: "#faceCapture.manualUpload"
|
|
1663
|
+
} },
|
|
1664
|
+
states: {
|
|
1665
|
+
checkingPermission: { invoke: {
|
|
1666
|
+
id: "checkPermissionTutorial",
|
|
1667
|
+
src: "checkPermission",
|
|
1668
|
+
onDone: [{
|
|
1669
|
+
target: "initializingCamera",
|
|
1670
|
+
guard: "isReadyForCapture",
|
|
1671
|
+
actions: "setPermissionResultFromEvent"
|
|
1672
|
+
}, {
|
|
1673
|
+
target: "ready",
|
|
1674
|
+
actions: "setPermissionResultFromEvent"
|
|
1675
|
+
}]
|
|
1676
|
+
} },
|
|
1677
|
+
initializingCamera: {
|
|
1678
|
+
type: "parallel",
|
|
1679
|
+
states: {
|
|
1680
|
+
cameraInit: {
|
|
1681
|
+
initial: "initializingDeepsight",
|
|
1682
|
+
states: {
|
|
1683
|
+
initializingDeepsight: {
|
|
1684
|
+
entry: "initializeProtectedDeepsight",
|
|
1685
|
+
on: {
|
|
1686
|
+
PROTECTED_DEEPSIGHT_READY: {
|
|
1687
|
+
target: "initializingStream",
|
|
1688
|
+
actions: "setDeepsightReady"
|
|
1689
|
+
},
|
|
1690
|
+
PROTECTED_DEEPSIGHT_FAILED: {
|
|
1691
|
+
target: "#faceCapture.tutorial.ready",
|
|
1692
|
+
actions: "setDeepsightNotReady"
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
initializingStream: {
|
|
1697
|
+
entry: "sendProtectedPreAcquire",
|
|
1698
|
+
on: {
|
|
1699
|
+
PROTECTED_PRE_ACQUIRE_DONE: {
|
|
1700
|
+
target: "ready",
|
|
1701
|
+
actions: "setCameraPreAcquired"
|
|
1702
|
+
},
|
|
1703
|
+
PROTECTED_PRE_ACQUIRE_FAILED: [{
|
|
1704
|
+
target: "#faceCapture.tutorial.ready",
|
|
1705
|
+
guard: "isPermissionDeniedError",
|
|
1706
|
+
actions: "setPermissionDenied"
|
|
1707
|
+
}, {
|
|
1708
|
+
target: "#faceCapture.tutorial.ready",
|
|
1709
|
+
actions: "setErrorFromEvent"
|
|
1710
|
+
}]
|
|
1711
|
+
}
|
|
1712
|
+
},
|
|
1713
|
+
ready: { type: "final" }
|
|
1714
|
+
}
|
|
1715
|
+
},
|
|
1716
|
+
userIntent: {
|
|
1717
|
+
initial: "waiting",
|
|
1718
|
+
states: {
|
|
1719
|
+
waiting: { on: { NEXT_STEP: {
|
|
1720
|
+
target: "clicked",
|
|
1721
|
+
actions: "trackContinue"
|
|
1722
|
+
} } },
|
|
1723
|
+
clicked: { type: "final" }
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
},
|
|
1727
|
+
onDone: { target: "#faceCapture.capture" }
|
|
1728
|
+
},
|
|
1729
|
+
ready: {
|
|
1730
|
+
initial: "idle",
|
|
1731
|
+
states: {
|
|
1732
|
+
idle: { always: [{
|
|
1733
|
+
target: "initializingDeepsight",
|
|
1734
|
+
guard: ({ context }) => !context.deepsightReady
|
|
1735
|
+
}, { target: "readyForNext" }] },
|
|
1736
|
+
initializingDeepsight: {
|
|
1737
|
+
entry: "initializeProtectedDeepsight",
|
|
1738
|
+
on: {
|
|
1739
|
+
PROTECTED_DEEPSIGHT_READY: {
|
|
1740
|
+
target: "readyForNext",
|
|
1741
|
+
actions: "setDeepsightReady"
|
|
1742
|
+
},
|
|
1743
|
+
PROTECTED_DEEPSIGHT_FAILED: {
|
|
1744
|
+
target: "readyForNext",
|
|
1745
|
+
actions: "setDeepsightNotReady"
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
},
|
|
1749
|
+
readyForNext: { on: { NEXT_STEP: {
|
|
1750
|
+
target: "#faceCapture.tutorial.waitingForPermission",
|
|
1751
|
+
actions: "trackContinue"
|
|
1752
|
+
} } }
|
|
1753
|
+
}
|
|
1754
|
+
},
|
|
1755
|
+
waitingForPermission: { invoke: {
|
|
1756
|
+
id: "checkPermissionWaiting",
|
|
1757
|
+
src: "checkPermission",
|
|
1758
|
+
onDone: [
|
|
1759
|
+
{
|
|
1760
|
+
target: "#faceCapture.capture",
|
|
1761
|
+
guard: "isReadyForCapture",
|
|
1762
|
+
actions: "setPermissionResultFromEvent"
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
target: "#faceCapture.permissions.requesting",
|
|
1766
|
+
guard: "needsMotionPrimeOnly",
|
|
1767
|
+
actions: "setPermissionResultFromEvent"
|
|
1768
|
+
},
|
|
1769
|
+
{
|
|
1770
|
+
target: "#faceCapture.permissions",
|
|
1771
|
+
actions: "setPermissionResultFromEvent"
|
|
1772
|
+
}
|
|
1773
|
+
]
|
|
1774
|
+
} }
|
|
1775
|
+
}
|
|
1776
|
+
},
|
|
1777
|
+
permissions: {
|
|
1778
|
+
entry: [
|
|
1779
|
+
"preloadRecordingProvider",
|
|
1780
|
+
"warmAvatarProvider",
|
|
1781
|
+
"initializeProtectedDeepsight"
|
|
1782
|
+
],
|
|
1783
|
+
initial: "checkingDeepsight",
|
|
1784
|
+
states: {
|
|
1785
|
+
checkingDeepsight: { always: [{
|
|
1786
|
+
target: "initializingDeepsight",
|
|
1787
|
+
guard: ({ context }) => !context.deepsightReady
|
|
1788
|
+
}, { target: "idle" }] },
|
|
1789
|
+
initializingDeepsight: {
|
|
1790
|
+
entry: "initializeProtectedDeepsight",
|
|
1791
|
+
on: {
|
|
1792
|
+
PROTECTED_DEEPSIGHT_READY: {
|
|
1793
|
+
target: "idle",
|
|
1794
|
+
actions: "setDeepsightReady"
|
|
1795
|
+
},
|
|
1796
|
+
PROTECTED_DEEPSIGHT_FAILED: {
|
|
1797
|
+
target: "idle",
|
|
1798
|
+
actions: "setDeepsightNotReady"
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
},
|
|
1802
|
+
idle: {
|
|
1803
|
+
invoke: {
|
|
1804
|
+
id: "checkPermissionIdle",
|
|
1805
|
+
src: "checkPermission",
|
|
1806
|
+
onDone: [
|
|
1807
|
+
{
|
|
1808
|
+
target: "#faceCapture.capture",
|
|
1809
|
+
guard: "isReadyForCapture",
|
|
1810
|
+
actions: "setPermissionResultFromEvent"
|
|
1811
|
+
},
|
|
1812
|
+
{
|
|
1813
|
+
target: "denied",
|
|
1814
|
+
guard: ({ event }) => event.output === "denied",
|
|
1815
|
+
actions: "setPermissionResultFromEvent"
|
|
1816
|
+
},
|
|
1817
|
+
{
|
|
1818
|
+
target: "motionOnly",
|
|
1819
|
+
guard: "needsMotionPrimeOnly",
|
|
1820
|
+
actions: "setPermissionResultFromEvent"
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
target: "waitingForUser",
|
|
1824
|
+
actions: "setPermissionResultFromEvent"
|
|
1825
|
+
}
|
|
1826
|
+
],
|
|
1827
|
+
onError: {
|
|
1828
|
+
target: "waitingForUser",
|
|
1829
|
+
actions: "setPermissionPrompt"
|
|
1830
|
+
}
|
|
1831
|
+
},
|
|
1832
|
+
on: {
|
|
1833
|
+
REQUEST_PERMISSION: "requesting",
|
|
1834
|
+
GO_TO_LEARN_MORE: "learnMore"
|
|
1835
|
+
}
|
|
1836
|
+
},
|
|
1837
|
+
waitingForUser: { on: {
|
|
1838
|
+
REQUEST_PERMISSION: "requesting",
|
|
1839
|
+
GO_TO_LEARN_MORE: "learnMore"
|
|
1840
|
+
} },
|
|
1841
|
+
motionOnly: { on: { REQUEST_PERMISSION: "requesting" } },
|
|
1842
|
+
learnMore: { on: {
|
|
1843
|
+
BACK: "idle",
|
|
1844
|
+
REQUEST_PERMISSION: "requesting"
|
|
1845
|
+
} },
|
|
1846
|
+
requesting: { invoke: {
|
|
1847
|
+
id: "requestPermission",
|
|
1848
|
+
src: "requestPermission",
|
|
1849
|
+
input: ({ context }) => ({
|
|
1850
|
+
permissionResult: context.permissionResult === "refresh" ? void 0 : context.permissionResult,
|
|
1851
|
+
requestMotionPermission: context.config.ds === true
|
|
1852
|
+
}),
|
|
1853
|
+
onDone: [
|
|
1854
|
+
{
|
|
1855
|
+
target: "#faceCapture.capture",
|
|
1856
|
+
guard: "isReadyForCapture",
|
|
1857
|
+
actions: "setPermissionResultFromEvent"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
target: "denied",
|
|
1861
|
+
guard: ({ event }) => event.output === "denied",
|
|
1862
|
+
actions: "setPermissionResultFromEvent"
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
target: "waitingForUser",
|
|
1866
|
+
actions: "setPermissionResultFromEvent"
|
|
1867
|
+
}
|
|
1868
|
+
],
|
|
1869
|
+
onError: { target: "denied" }
|
|
1870
|
+
} },
|
|
1871
|
+
denied: { entry: "setPermissionRefresh" }
|
|
1872
|
+
}
|
|
1873
|
+
},
|
|
1874
|
+
capture: {
|
|
1875
|
+
entry: [
|
|
1876
|
+
"preloadRecordingProvider",
|
|
1877
|
+
"warmAvatarProvider",
|
|
1878
|
+
"initializeProtectedDeepsight",
|
|
1879
|
+
"startProtectedAttempt"
|
|
1880
|
+
],
|
|
1881
|
+
initial: "checkingDeepsight",
|
|
1882
|
+
exit: [
|
|
1883
|
+
"releaseProtectedAttempt",
|
|
1884
|
+
"stopMediaStream",
|
|
1885
|
+
"cleanup"
|
|
1886
|
+
],
|
|
1887
|
+
on: {
|
|
1888
|
+
PROTECTED_ATTEMPT_READY: { actions: "setProtectedAttemptReady" },
|
|
1889
|
+
PROTECTED_VISIBILITY: { actions: ["setProtectedVisibility", "sendProtectedVisibility"] },
|
|
1890
|
+
PROTECTED_APPLY_VISIBILITY: { actions: "sendProtectedVisibility" },
|
|
1891
|
+
PROTECTED_ATTEMPT_FAILED: [
|
|
1892
|
+
{
|
|
1893
|
+
target: ".detecting",
|
|
1894
|
+
guard: "isProtectedManualRefusal"
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
target: ".uploadError",
|
|
1898
|
+
guard: "isProtectedUploadFailure",
|
|
1899
|
+
actions: "setServerErrorAndDecrement"
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
target: "#faceCapture.error",
|
|
1903
|
+
actions: "setProtectedFailure"
|
|
1904
|
+
}
|
|
1905
|
+
],
|
|
1906
|
+
PROTECTED_ATTEMPT_RELEASED: { actions: "noOp" }
|
|
1907
|
+
},
|
|
1908
|
+
states: {
|
|
1909
|
+
checkingDeepsight: { always: [{
|
|
1910
|
+
target: "initializingDeepsight",
|
|
1911
|
+
guard: ({ context }) => !context.deepsightReady
|
|
1912
|
+
}, { target: "initializing" }] },
|
|
1913
|
+
initializingDeepsight: {
|
|
1914
|
+
entry: "initializeProtectedDeepsight",
|
|
1915
|
+
on: {
|
|
1916
|
+
PROTECTED_DEEPSIGHT_READY: {
|
|
1917
|
+
target: "initializing",
|
|
1918
|
+
actions: "setDeepsightReady"
|
|
1919
|
+
},
|
|
1920
|
+
PROTECTED_DEEPSIGHT_FAILED: {
|
|
1921
|
+
target: "#faceCapture.permissions",
|
|
1922
|
+
actions: "setDeepsightNotReady"
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
},
|
|
1926
|
+
checkingStream: { always: "initializing" },
|
|
1927
|
+
initializing: { always: {
|
|
1928
|
+
target: "detecting",
|
|
1929
|
+
guard: "isCameraAndDeepsightReady"
|
|
1930
|
+
} },
|
|
1931
|
+
detecting: {
|
|
1932
|
+
entry: ["setDetectionStatusDetecting"],
|
|
1933
|
+
invoke: {
|
|
1934
|
+
id: "runAvatar",
|
|
1935
|
+
src: "runAvatar",
|
|
1936
|
+
input: ({ context }) => ({
|
|
1937
|
+
stream: context.stream,
|
|
1938
|
+
config: context.config,
|
|
1939
|
+
faceFeed: context.faceFeed
|
|
1940
|
+
})
|
|
1941
|
+
},
|
|
1942
|
+
exit: ["clearAvatarCanvas"],
|
|
1943
|
+
on: {
|
|
1944
|
+
DETECTION_UPDATE: { actions: ["setDetectionStatusFromEvent", "setManualCaptureTriggered"] },
|
|
1945
|
+
AVATAR_READY: { actions: "setAvatarCanvas" },
|
|
1946
|
+
PROTECTED_NATIVE_AUTO_CAPTURE: { actions: "sendProtectedAutoCapture" },
|
|
1947
|
+
PROTECTED_AUTO_COMMITTED: {
|
|
1948
|
+
target: "capturing",
|
|
1949
|
+
actions: "setProtectedCommittedPreview"
|
|
1950
|
+
},
|
|
1951
|
+
MANUAL_CAPTURE: {
|
|
1952
|
+
target: "capturingManual",
|
|
1953
|
+
guard: "canCommitProtectedManual",
|
|
1954
|
+
actions: "sendProtectedManualCapture"
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
},
|
|
1958
|
+
capturing: {
|
|
1959
|
+
entry: ["sendProtectedFinalize"],
|
|
1960
|
+
on: { PROTECTED_ATTEMPT_FINALIZED: {
|
|
1961
|
+
target: "preparingUpload",
|
|
1962
|
+
actions: "setProtectedFinalization"
|
|
1963
|
+
} }
|
|
1964
|
+
},
|
|
1965
|
+
capturingManual: { on: {
|
|
1966
|
+
PROTECTED_MANUAL_COMMITTED: { actions: ["setProtectedCommittedPreview", "sendProtectedFinalize"] },
|
|
1967
|
+
PROTECTED_ATTEMPT_FINALIZED: {
|
|
1968
|
+
target: "preparingUpload",
|
|
1969
|
+
actions: "setProtectedFinalization"
|
|
1970
|
+
}
|
|
1971
|
+
} },
|
|
1972
|
+
preparingUpload: { always: "uploading" },
|
|
1973
|
+
uploading: {
|
|
1974
|
+
entry: "sendProtectedUploadIfNeeded",
|
|
1975
|
+
on: {
|
|
1976
|
+
PROTECTED_UPLOAD_MAPPED: {
|
|
1977
|
+
target: "validatingUpload",
|
|
1978
|
+
actions: "setProtectedUploadResponse"
|
|
1979
|
+
},
|
|
1980
|
+
PROTECTED_LOCAL_DONE: {
|
|
1981
|
+
target: "validatingUpload",
|
|
1982
|
+
actions: "setProtectedLocalResult"
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
},
|
|
1986
|
+
validatingUpload: { always: [
|
|
1987
|
+
{
|
|
1988
|
+
target: "#faceCapture.error",
|
|
1989
|
+
guard: "isTerminalUploadError",
|
|
1990
|
+
actions: "setTerminalError"
|
|
1991
|
+
},
|
|
1992
|
+
{
|
|
1993
|
+
target: "uploadError",
|
|
1994
|
+
guard: "hasUploadValidationError",
|
|
1995
|
+
actions: ["setUploadErrorFromUploadValidation", "decrementAttemptsRemaining"]
|
|
1996
|
+
},
|
|
1997
|
+
{ target: "success" }
|
|
1998
|
+
] },
|
|
1999
|
+
uploadError: { on: { RETRY_CAPTURE: [
|
|
2000
|
+
{
|
|
2001
|
+
target: "#faceCapture.capture",
|
|
2002
|
+
reenter: true,
|
|
2003
|
+
guard: "hasAttemptsRemaining",
|
|
2004
|
+
actions: [
|
|
2005
|
+
"clearUploadFailure",
|
|
2006
|
+
"setDeepsightNotReady",
|
|
2007
|
+
"resetProtectedController",
|
|
2008
|
+
"clearProtectedAttempt"
|
|
2009
|
+
]
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
target: "#faceCapture.error",
|
|
2013
|
+
guard: "isNoAttemptsTerminal",
|
|
2014
|
+
actions: ["setTerminalError"]
|
|
2015
|
+
},
|
|
2016
|
+
{
|
|
2017
|
+
target: "#faceCapture.terminalProcessing",
|
|
2018
|
+
actions: ["flagFaceManualReview"]
|
|
2019
|
+
}
|
|
2020
|
+
] } },
|
|
2021
|
+
success: {
|
|
2022
|
+
entry: "cleanup",
|
|
2023
|
+
after: { 3e3: { target: "#faceCapture.processing" } }
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
},
|
|
2027
|
+
manualUpload: {
|
|
2028
|
+
entry: [
|
|
2029
|
+
"releaseProtectedAttempt",
|
|
2030
|
+
"stopMediaStream",
|
|
2031
|
+
"releaseAvatarWarmup"
|
|
2032
|
+
],
|
|
2033
|
+
invoke: {
|
|
2034
|
+
id: "manualUpload",
|
|
2035
|
+
src: "manualUploadMachine",
|
|
2036
|
+
input: ({ context }) => ({
|
|
2037
|
+
config: context.config,
|
|
2038
|
+
dependencies: context.dependencies,
|
|
2039
|
+
captureAttempts: getAttemptsFromConfig(context.config)
|
|
2040
|
+
}),
|
|
2041
|
+
onSnapshot: { actions: "noOp" },
|
|
2042
|
+
onDone: [{
|
|
2043
|
+
guard: "isManualUploadExhausted",
|
|
2044
|
+
target: "terminalProcessing"
|
|
2045
|
+
}, { target: "processing" }],
|
|
2046
|
+
onError: {
|
|
2047
|
+
target: "error",
|
|
2048
|
+
actions: "setErrorFromEvent"
|
|
2049
|
+
}
|
|
2050
|
+
},
|
|
2051
|
+
on: {
|
|
2052
|
+
MANUAL_UPLOAD_FILE_SELECTED: { actions: "forwardToManualUpload" },
|
|
2053
|
+
MANUAL_UPLOAD_CONTINUE: { actions: "forwardToManualUpload" },
|
|
2054
|
+
MANUAL_UPLOAD_RESET: { actions: "forwardToManualUpload" }
|
|
2055
|
+
}
|
|
2056
|
+
},
|
|
2057
|
+
processing: { invoke: {
|
|
2058
|
+
id: "processFace",
|
|
2059
|
+
src: "processFace",
|
|
2060
|
+
input: ({ context }) => context,
|
|
2061
|
+
onDone: {
|
|
2062
|
+
target: "finished",
|
|
2063
|
+
actions: "setProcessResponseFromEvent"
|
|
2064
|
+
},
|
|
2065
|
+
onError: { target: "finished" }
|
|
2066
|
+
} },
|
|
2067
|
+
terminalProcessing: {
|
|
2068
|
+
entry: "cleanup",
|
|
2069
|
+
invoke: {
|
|
2070
|
+
id: "processFace",
|
|
2071
|
+
src: "processFace",
|
|
2072
|
+
input: ({ context }) => context,
|
|
2073
|
+
onDone: {
|
|
2074
|
+
target: "finished",
|
|
2075
|
+
actions: "setProcessResponseFromEvent"
|
|
2076
|
+
},
|
|
2077
|
+
onError: { target: "finished" }
|
|
2078
|
+
}
|
|
2079
|
+
},
|
|
2080
|
+
finished: {
|
|
2081
|
+
entry: [
|
|
2082
|
+
"releaseProtectedAttempt",
|
|
2083
|
+
"stopMediaStream",
|
|
2084
|
+
"releaseAvatarWarmup"
|
|
2085
|
+
],
|
|
2086
|
+
type: "final"
|
|
2087
|
+
},
|
|
2088
|
+
closed: {
|
|
2089
|
+
entry: [
|
|
2090
|
+
"releaseProtectedAttempt",
|
|
2091
|
+
"stopMediaStream",
|
|
2092
|
+
"releaseAvatarWarmup"
|
|
2093
|
+
],
|
|
2094
|
+
type: "final"
|
|
2095
|
+
},
|
|
2096
|
+
error: {
|
|
2097
|
+
entry: ["stopMediaStream", "releaseAvatarWarmup"],
|
|
2098
|
+
on: { RESET: {
|
|
2099
|
+
target: "idle",
|
|
2100
|
+
actions: ["resetProtectedController", "resetContext"]
|
|
2101
|
+
} }
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
});
|
|
2105
|
+
//#endregion
|
|
2106
|
+
export { concealmentToAvatar as i, createDefaultProtectedFaceControllerActor as n, manualFaceUploadMachine as r, faceCaptureMachine as t };
|