@incodetech/core 2.2.0-rc.6 → 2.2.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/dist/{BrowserDiagnosticsCryptoProvider-Ce3rctBe.esm.js → BrowserDiagnosticsCryptoProvider-B8vorxN5.esm.js} +1 -1
- package/dist/{addressSearch-DQyrvDQj.esm.js → addressSearch-D1XpBdUS.esm.js} +1 -1
- package/dist/ae-signature.esm.js +1 -1
- package/dist/antifraud.esm.js +2 -2
- package/dist/{antifraudManager-BMZvuxv0.esm.js → antifraudManager-CQFNgSPt.esm.js} +2 -2
- package/dist/{antifraudStateMachine-r4VxSBS4.esm.js → antifraudStateMachine-Bf_JVNwn.esm.js} +2 -2
- package/dist/{apiError-BSXFYi4z.esm.js → apiError-BA1d_au0.esm.js} +1 -1
- package/dist/authentication.esm.js +2 -2
- package/dist/{authenticationManager-DYqYkfs6.esm.js → authenticationManager-zh9TC62P.esm.js} +3 -3
- package/dist/{authenticationStateMachine-9tP0fEe0.esm.js → authenticationStateMachine-xzi-vnvE.esm.js} +4 -4
- package/dist/certificate-issuance.esm.js +2 -2
- package/dist/{certificateIssuanceStateMachine-COd7_l2T.esm.js → certificateIssuanceStateMachine-D_R92TYu.esm.js} +2 -2
- package/dist/consent.esm.js +2 -2
- package/dist/{consentManager-DyBXdkFn.esm.js → consentManager-BxjpLZAG.esm.js} +2 -2
- package/dist/{consentStateMachine-WD-S-Yhs.esm.js → consentStateMachine-D9D4ON8Z.esm.js} +2 -2
- package/dist/cross-document-data-match.esm.js +1 -1
- package/dist/{crossDocumentDataMatchManager-pNkutndd.esm.js → crossDocumentDataMatchManager-CGT0V6RV.esm.js} +2 -2
- package/dist/curp-validation.esm.js +2 -2
- package/dist/{curpValidationManager-DEesnLee.esm.js → curpValidationManager-6Nmkn5u1.esm.js} +2 -2
- package/dist/{curpValidationStateMachine-9KwnqeoB.esm.js → curpValidationStateMachine-Ds7KMacK.esm.js} +2 -2
- package/dist/custom-fields.esm.js +2 -2
- package/dist/custom-watchlist.esm.js +2 -2
- package/dist/{customWatchlistStateMachine-B_ey3TFj.esm.js → customWatchlistStateMachine-YWSgKLbF.esm.js} +2 -2
- package/dist/{deepsightLoader-CraWVfCs.esm.js → deepsightLoader--wMKWECS.esm.js} +3 -3
- package/dist/document-capture.esm.js +2 -2
- package/dist/document-upload.esm.js +2 -2
- package/dist/{documentCaptureManager-DwM7AmEJ.esm.js → documentCaptureManager-DsQQhWry.esm.js} +2 -2
- package/dist/{documentCaptureStateMachine-D2jBwwVb.esm.js → documentCaptureStateMachine-BM4Ex_5x.esm.js} +2 -2
- package/dist/dynamic-forms.esm.js +2 -2
- package/dist/{dynamicFormsManager-CXkZG8c6.esm.js → dynamicFormsManager-fFVIRBNv.esm.js} +2 -2
- package/dist/{dynamicFormsStateMachine-Cl3kW4E7.esm.js → dynamicFormsStateMachine-D6D7oEYk.esm.js} +3 -3
- package/dist/ekyb.esm.js +3 -3
- package/dist/{ekybManager-BY8ijn3M.esm.js → ekybManager-Y3MkoXpC.esm.js} +2 -2
- package/dist/{ekybStateMachine-Cyv8iPyH.esm.js → ekybStateMachine-3nlnqdlr.esm.js} +4 -4
- package/dist/ekyc.esm.js +2 -2
- package/dist/{ekycManager-CasdX1Ou.esm.js → ekycManager-DgT48ky6.esm.js} +2 -2
- package/dist/{ekycStateMachine-DHvksnrr.esm.js → ekycStateMachine-CQwqiR-R.esm.js} +3 -3
- package/dist/electronic-signature.esm.js +1 -1
- package/dist/{electronicSignatureManager-CAHjq6JR.esm.js → electronicSignatureManager-DGbAJ00W.esm.js} +3 -3
- package/dist/email.esm.js +2 -2
- package/dist/{emailManager-NLzaP12q.esm.js → emailManager-BY0jeYwR.esm.js} +2 -2
- package/dist/{emailStateMachine-Bm894MHB.esm.js → emailStateMachine-Crw8QVmc.esm.js} +2 -2
- package/dist/extensibility.d.ts +2 -2
- package/dist/extensibility.esm.js +28 -28
- package/dist/face-match.esm.js +2 -2
- package/dist/{faceCaptureManagerFactory-BdAtEQUX.esm.js → faceCaptureManagerFactory-CVnZ0fa5.esm.js} +2 -2
- package/dist/{faceCaptureSetup-BUNskJhm.esm.js → faceCaptureSetup-Cd-04W_X.esm.js} +4 -4
- package/dist/{faceMatchManager-DEVhhdf4.esm.js → faceMatchManager-Ri4UbNz0.esm.js} +2 -2
- package/dist/{faceMatchStateMachine-CJiO2yLO.esm.js → faceMatchStateMachine-20DdZ3jg.esm.js} +2 -2
- package/dist/{feature-management-Bo_xbwQa.esm.js → feature-management-CPQ4qv3F.esm.js} +1 -1
- package/dist/feature-management.esm.js +1 -1
- package/dist/field-comparison.esm.js +1 -1
- package/dist/{fieldComparisonManager-BRYsIi34.esm.js → fieldComparisonManager-Cg6kPQQx.esm.js} +2 -2
- package/dist/fiscal-qr.esm.js +2 -2
- package/dist/flow.d.ts +1 -1
- package/dist/flow.esm.js +5 -5
- package/dist/{flowCompletionService-J_2XiDZG.esm.js → flowCompletionService-tfeoGre9.esm.js} +1 -1
- package/dist/{flowServices-BydHVxc7.esm.js → flowServices-f5Ruz7R9.esm.js} +4 -4
- package/dist/geolocation.esm.js +2 -2
- package/dist/{geolocationManager-BF_JcCgS.esm.js → geolocationManager-CH6RfqFH.esm.js} +2 -2
- package/dist/{geolocationStateMachine-rYPrOUyM.esm.js → geolocationStateMachine-DmbWFely.esm.js} +3 -3
- package/dist/government-validation.esm.js +2 -2
- package/dist/{governmentValidationStateMachine-BYKYUdza.esm.js → governmentValidationStateMachine-CK9DrZXX.esm.js} +2 -2
- package/dist/home.esm.js +1 -1
- package/dist/id-ocr.esm.js +2 -2
- package/dist/id-selfie-flow.esm.js +8 -8
- package/dist/id-verification.esm.js +2 -2
- package/dist/id.d.ts +2 -2
- package/dist/id.esm.js +5 -5
- package/dist/{idCaptureManager-C5zp-Xje.esm.js → idCaptureManager-BYwTZ2H8.esm.js} +12 -16
- package/dist/{idCaptureManager-BjU-Wyow.d.ts → idCaptureManager-CqrqSjJV.d.ts} +20 -10
- package/dist/{idCaptureServices-28d5mDGM.esm.js → idCaptureServices-44kwQItl.esm.js} +141 -15
- package/dist/{idCaptureStateMachine-3jTqEo76.esm.js → idCaptureStateMachine-kZ5OpdfM.esm.js} +445 -54
- package/dist/{idOcrManager-D_2k-6Vv.esm.js → idOcrManager-BIQYQXXj.esm.js} +2 -2
- package/dist/{idOcrStateMachine-Bq_e3l4E.esm.js → idOcrStateMachine-cSxzHpHt.esm.js} +2 -2
- package/dist/{idVerificationManager-DdlrWSSB.esm.js → idVerificationManager-JpV_SAsl.esm.js} +2 -2
- package/dist/{idVerificationStateMachine-GEQWUPU2.esm.js → idVerificationStateMachine-nPeUNyFq.esm.js} +2 -2
- package/dist/identity-reuse.esm.js +2 -2
- package/dist/{identityReuseManager-CzLZNgdh.esm.js → identityReuseManager-kNCg9AbD.esm.js} +2 -2
- package/dist/{identityReuseStateMachine-AubRPhIi.esm.js → identityReuseStateMachine-DyulxDjP.esm.js} +2 -2
- package/dist/index.esm.js +1 -1
- package/dist/mandatory-consent.esm.js +2 -2
- package/dist/{mandatoryConsentManager-CEIkNukS.esm.js → mandatoryConsentManager-B4Niyg_u.esm.js} +2 -2
- package/dist/{mandatoryConsentStateMachine-zroQGzwb.esm.js → mandatoryConsentStateMachine-B4-1u-L8.esm.js} +2 -2
- package/dist/{onDeviceFaceResults-CTinxa9H.esm.js → onDeviceFaceResults-CUHwyZT4.esm.js} +3 -3
- package/dist/personhood.esm.js +5 -5
- package/dist/phone.esm.js +2 -2
- package/dist/{phoneManager-B_03mKyi.esm.js → phoneManager-FsJ0W2Ei.esm.js} +2 -2
- package/dist/{phoneStateMachine-E1fUUcNM.esm.js → phoneStateMachine-Ba0wKHyX.esm.js} +2 -2
- package/dist/qe-signature.esm.js +1 -1
- package/dist/{recordingService-DtWTn91V.esm.js → recordingService-3NSENVwp.esm.js} +1 -1
- package/dist/redirect-to-mobile.esm.js +2 -2
- package/dist/{redirectToMobileManager-Bgo6eqri.esm.js → redirectToMobileManager-y4VQB11M.esm.js} +3 -3
- package/dist/{redirectToMobileStateMachine-C9SYCJcH.esm.js → redirectToMobileStateMachine-Cyv2x0PP.esm.js} +2 -2
- package/dist/{runChildModule-Bl5F16W9.esm.js → runChildModule-Dukuw23X.esm.js} +3 -3
- package/dist/{sdkVersion-DEks9mOi.esm.js → sdkVersion-D1Bm59g6.esm.js} +1 -1
- package/dist/selfie.esm.js +6 -6
- package/dist/{selfieManager-D1jZ1_6k.esm.js → selfieManager-Bj6B2ZVy.esm.js} +3 -3
- package/dist/{selfieStateMachine-CW_c5AnO.esm.js → selfieStateMachine-CHV_WNst.esm.js} +4 -4
- package/dist/{session-ChIfu6mP.esm.js → session-BFdLgN4i.esm.js} +4 -4
- package/dist/session.esm.js +4 -4
- package/dist/{sessionInitializer-BFJAT3u0.esm.js → sessionInitializer-dlfDehHk.esm.js} +4 -4
- package/dist/{setup-CcxP8TVK.esm.js → setup-BIAtLa8k.esm.js} +5 -5
- package/dist/signature.esm.js +2 -2
- package/dist/{signatureManager-DoACIR02.esm.js → signatureManager-Ca43yhwk.esm.js} +2 -2
- package/dist/{signatureStateMachine-Cf73DGWO.esm.js → signatureStateMachine-K1LMRrxG.esm.js} +2 -2
- package/dist/trueSight-B_icM8r2.esm.js +1 -0
- package/dist/{trueSightService-B49-Eq-5.esm.js → trueSightService---iW2KKJ.esm.js} +2 -2
- package/dist/trust-graph.esm.js +1 -1
- package/dist/{types-M6eb82ra2.d.ts → types-Bdf8_LYB2.d.ts} +69 -13
- package/dist/video-selfie.esm.js +5 -5
- package/dist/{videoSelfieCameraWarmer-qld900rY.esm.js → videoSelfieCameraWarmer-NEhFZvWx.esm.js} +2 -2
- package/dist/wasm.esm.js +1 -1
- package/dist/watchlist-for-business.esm.js +2 -2
- package/dist/watchlist.esm.js +2 -2
- package/dist/{watchlistForBusinessStateMachine-BcBMkMvV.esm.js → watchlistForBusinessStateMachine-CxFnlshm.esm.js} +2 -2
- package/dist/{watchlistManager-CPBZnFYj.esm.js → watchlistManager-LoNZH9qm.esm.js} +2 -2
- package/dist/{watchlistStateMachine-Bl7Wy9Cm.esm.js → watchlistStateMachine-DIKQDoUT.esm.js} +2 -2
- package/dist/workflow.esm.js +36 -36
- package/dist/{xstate-actors.esm-CtI5S0f4.esm.js → xstate-actors.esm-BXeQ-Ucz.esm.js} +1 -1
- package/dist/{xstate.esm-Cf4e0Cfd.esm.js → xstate.esm-BuFLFuzz.esm.js} +1 -1
- package/package.json +2 -2
- package/dist/trueSight-CAaztf3O.esm.js +0 -1
package/dist/{idCaptureStateMachine-3jTqEo76.esm.js → idCaptureStateMachine-kZ5OpdfM.esm.js}
RENAMED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { t as BrowserTimerProvider } from "./BrowserTimerProvider-BXhgNxLt.esm.js";
|
|
2
|
-
import { s as exposeExperimentOnce } from "./feature-management-
|
|
2
|
+
import { s as exposeExperimentOnce } from "./feature-management-CPQ4qv3F.esm.js";
|
|
3
3
|
import { n as revokeObjectURL } from "./IBrowserEnvironmentCapability-DEaRRk-R.esm.js";
|
|
4
4
|
import { r as getToken, t as api } from "./api-Bx3gKW_y.esm.js";
|
|
5
|
-
import { c as trueSightHintTransition, l as trueSightLog, r as noteTrueSightCaptureActivity } from "./trueSightService
|
|
5
|
+
import { c as trueSightHintTransition, l as trueSightLog, r as noteTrueSightCaptureActivity } from "./trueSightService---iW2KKJ.esm.js";
|
|
6
6
|
import { n as eventModuleNames, s as recordExperimentAssignmentOnce } from "./events-DYiyoSlx.esm.js";
|
|
7
|
-
import { t as getDisableIpify } from "./sessionInitializer-
|
|
8
|
-
import "./trueSight-
|
|
7
|
+
import { t as getDisableIpify } from "./sessionInitializer-dlfDehHk.esm.js";
|
|
8
|
+
import "./trueSight-B_icM8r2.esm.js";
|
|
9
9
|
import { t as addDeviceStats } from "./stats-B0nFKawG.esm.js";
|
|
10
10
|
import { t as isAndroid } from "./platform-BJrm5nzZ.esm.js";
|
|
11
|
-
import "./session-
|
|
12
|
-
import {
|
|
13
|
-
import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-
|
|
11
|
+
import "./session-BFdLgN4i.esm.js";
|
|
12
|
+
import { c as not, i as assign, l as raise, n as enqueueActions, o as and, r as sendTo, t as setup } from "./xstate.esm-BuFLFuzz.esm.js";
|
|
13
|
+
import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-BXeQ-Ucz.esm.js";
|
|
14
14
|
import { n as invokeRequestPermissionActor, t as checkPermission } from "./permissionServices-UvUcOSRb.esm.js";
|
|
15
|
-
import { _ as StreamCanvasProcessingSession } from "./onDeviceFaceResults-
|
|
15
|
+
import { _ as StreamCanvasProcessingSession } from "./onDeviceFaceResults-CUHwyZT4.esm.js";
|
|
16
16
|
import { t as sleep } from "./ITimerCapability-DgdDzp8G.esm.js";
|
|
17
17
|
import { t as IncodeCanvas } from "./canvas-E1zGkel4.esm.js";
|
|
18
18
|
import { t as getBrowser } from "./getBrowser-DNrQeL4T.esm.js";
|
|
@@ -20,7 +20,7 @@ import { n as getWindowDimensions } from "./getDeviceClass-CeBiEayS.esm.js";
|
|
|
20
20
|
import { t as StreamCanvasCapture } from "./StreamCanvasCapture-D2XcXCZ4.esm.js";
|
|
21
21
|
import { n as needsMotionPrimeOnlyFromPermission, r as resolveStoredPermissionResult, t as isReadyForCaptureFromPermission } from "./permissionGuards-KINpmKtI.esm.js";
|
|
22
22
|
import { n as flagIdManualReview } from "./manualReviewService-D4IkipnN.esm.js";
|
|
23
|
-
import { a as processId, c as stopStream, d as validateUploadResponse, f as ID_ERROR_CODES, g as
|
|
23
|
+
import { _ as getIdCameraStream, a as processId, c as stopStream, d as validateUploadResponse, f as ID_ERROR_CODES, g as OPTIMIZING_WATCHDOG_MARGIN_MS, h as CAPTURE_OPTIMIZING, i as preloadIdRecordingProvider, l as uploadIdImage, m as resolveIdCaptureThresholds, o as startRecordingSession, r as initializeIdCapture, s as stopRecording, t as addBarcode, u as uploadManualIdFile } from "./idCaptureServices-44kwQItl.esm.js";
|
|
24
24
|
//#region src/modules/id/digitalUpload/digitalUploadErrors.ts
|
|
25
25
|
const TERMINAL_FAIL_REASONS = /* @__PURE__ */ new Set(["DIGITAL_ID_REQUESTED_BUT_OTHER_PROVIDED", "ID_TYPE_UNACCEPTABLE"]);
|
|
26
26
|
function isKnownFailReason(reason) {
|
|
@@ -791,6 +791,20 @@ const hasAttemptsRemainingGuard = ({ context }) => context.attemptsRemaining > 0
|
|
|
791
791
|
const hasCapturedImageGuard = ({ context }) => {
|
|
792
792
|
return context.provider?.getCapturedCanvas() !== null;
|
|
793
793
|
};
|
|
794
|
+
const hasCandidateFrameGuard = ({ event }) => {
|
|
795
|
+
return event.type === "DETECTION_OPTIMIZING" && Boolean(event.candidateCanvas);
|
|
796
|
+
};
|
|
797
|
+
/**
|
|
798
|
+
* IDC-05: whether WASM's commit arrived while the optimizing phase was still
|
|
799
|
+
* spending its dwell, and is therefore parked in `optimizing.holding` waiting
|
|
800
|
+
* for the release.
|
|
801
|
+
*
|
|
802
|
+
* Gates the phase's own `SWITCH_TO_MANUAL_CAPTURE` / `MANUAL_CAPTURE` handlers:
|
|
803
|
+
* while a commit is parked those escapes end the dwell early instead of leaving
|
|
804
|
+
* the phase, so WASM's committed frame is honored rather than dropped on the way
|
|
805
|
+
* out. False leaves both events to the arm parent, the documented escape hatch.
|
|
806
|
+
*/
|
|
807
|
+
const hasStashedCommitGuard = ({ context }) => context.stashedCommit !== void 0;
|
|
794
808
|
const hasUploadValidationErrorGuard = ({ context }) => {
|
|
795
809
|
if (!context.uploadResponse) return false;
|
|
796
810
|
return validateUploadResponse(context.uploadResponse, {
|
|
@@ -835,16 +849,31 @@ const shouldContinueToBackGuard = ({ context }) => {
|
|
|
835
849
|
const shouldContinueToFrontGuard = ({ context }) => context.config.usSmartCapture === true && context.currentMode === "back" && context.uploadResponse?.forceFrontIdCapture === true;
|
|
836
850
|
const hasMandatoryConsentGuard = ({ context }) => context.uploadResponse?.showMandatoryConsent === true;
|
|
837
851
|
/**
|
|
838
|
-
*
|
|
839
|
-
* `
|
|
852
|
+
* IDC-05 arm, as resolved once per session by
|
|
853
|
+
* `resolveCaptureOptimizingTreatment` at actor creation.
|
|
840
854
|
*/
|
|
841
|
-
const
|
|
855
|
+
const isCaptureOptimizingModeGuard = ({ context }) => context.captureFeedbackMode === "optimizing";
|
|
842
856
|
/**
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
857
|
+
* IDC-05 rendering-layer capability: whether the consumer opted into rendering
|
|
858
|
+
* `captureStatus === 'optimizing'`. Consumers that omit it retain the direct
|
|
859
|
+
* capture-to-upload path. Nothing is advanced by the host — core owns the phase's
|
|
860
|
+
* timing on both paths (WASM's commit on auto capture, `optimizingDwell` on
|
|
861
|
+
* manual), so there is no acknowledgement call.
|
|
862
|
+
*
|
|
863
|
+
* Paired with `isCaptureOptimizingModeGuard` at both branch sites
|
|
864
|
+
* (`choosingDetectionStrategy`, `capturingManual`). Through
|
|
865
|
+
* `createIdCaptureActor` the two can never disagree — that path derives
|
|
866
|
+
* `captureFeedbackMode` from `resolveCaptureOptimizingTreatment`, which
|
|
867
|
+
* already requires this capability — so the pairing looks redundant, and for
|
|
868
|
+
* SDK-built sessions it is.
|
|
869
|
+
*
|
|
870
|
+
* It is NOT redundant on the public surface: `idCaptureMachine` is exported from
|
|
871
|
+
* `@incodetech/core/id` and its `input.captureFeedbackMode` is settable, so a
|
|
872
|
+
* host wiring the machine up directly could otherwise reach the phase without
|
|
873
|
+
* declaring the capability. This guard is what makes the documented opt-in true
|
|
874
|
+
* for every route into the machine, not just the factory.
|
|
846
875
|
*/
|
|
847
|
-
const
|
|
876
|
+
const supportsOptimizingStatusGuard = ({ context }) => context.config.supportsOptimizingStatus === true;
|
|
848
877
|
const isDeepsightEnabledGuard = ({ context }) => context.dependencies !== void 0;
|
|
849
878
|
const isDeepsightReadyGuard = ({ context }) => context.deepsightService !== void 0;
|
|
850
879
|
const needsDeepsightInitGuard = ({ context }) => context.dependencies !== void 0 && context.deepsightService === void 0 && !context.deepsightInitAttempted;
|
|
@@ -933,6 +962,7 @@ const isBackModeInMultiSideFlowGuard = ({ context }) => context.currentMode ===
|
|
|
933
962
|
* max-attempts/OCR cap (ENG-46592 / re-respects ENG-45666).
|
|
934
963
|
*/
|
|
935
964
|
const wasExpiredRedirectedGuard = ({ context }) => context.expiredRedirected === true;
|
|
965
|
+
const hasManualCaptureTriggeredGuard = ({ context }) => context.manualCaptureTriggered;
|
|
936
966
|
//#endregion
|
|
937
967
|
//#region src/modules/id/idCaptureHelpers.ts
|
|
938
968
|
/**
|
|
@@ -1041,6 +1071,13 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1041
1071
|
let isCapturing = false;
|
|
1042
1072
|
const timer = BrowserTimerProvider.getInstance();
|
|
1043
1073
|
let notificationTimeout = null;
|
|
1074
|
+
let optimizingBoundaryTimeout = null;
|
|
1075
|
+
/**
|
|
1076
|
+
* IDC-05 latch for the boundary clock: whether a boundary is currently
|
|
1077
|
+
* pending. `onCapturing` is WASM's "aggregation window opened" callback and is
|
|
1078
|
+
* the clock's only T0; nothing in WASM enforces that it fires once per window.
|
|
1079
|
+
*/
|
|
1080
|
+
let isOptimizingBoundaryArmed = false;
|
|
1044
1081
|
let currentNotificationStatus = null;
|
|
1045
1082
|
let lastReportedSide = null;
|
|
1046
1083
|
const NOTIFICATION_DURATION_MS = 500;
|
|
@@ -1059,6 +1096,50 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1059
1096
|
currentNotificationStatus = null;
|
|
1060
1097
|
}
|
|
1061
1098
|
};
|
|
1099
|
+
const clearOptimizingBoundary = () => {
|
|
1100
|
+
if (optimizingBoundaryTimeout) {
|
|
1101
|
+
timer.clearTimeout(optimizingBoundaryTimeout);
|
|
1102
|
+
optimizingBoundaryTimeout = null;
|
|
1103
|
+
}
|
|
1104
|
+
isOptimizingBoundaryArmed = false;
|
|
1105
|
+
};
|
|
1106
|
+
/**
|
|
1107
|
+
* IDC-05 boundary clock — the actor's *only* timer, and the only piece of the
|
|
1108
|
+
* optimizing phase it owns. WASM's `CapturingManager` opens the
|
|
1109
|
+
* frames-aggregation window at `onCapturing` and closes it with `onCapture` at
|
|
1110
|
+
* `T0 + framesAggregationInterval` (already resolved to
|
|
1111
|
+
* `captureWindowMs + optimizingDwellMs`). The phase begins at
|
|
1112
|
+
* `T0 + captureWindowMs`, so it is timed from the very callback that defines
|
|
1113
|
+
* T0; how long it then lasts is the machine's business.
|
|
1114
|
+
*
|
|
1115
|
+
* No best frame yet at that point means the session simply behaves like the
|
|
1116
|
+
* countdown arm — no event, no phase — which mirrors iOS's "auto capture with
|
|
1117
|
+
* no best crop yet" fallback.
|
|
1118
|
+
*
|
|
1119
|
+
* At most one boundary pending at a time — see `isOptimizingBoundaryArmed`.
|
|
1120
|
+
*/
|
|
1121
|
+
const scheduleOptimizingBoundary = () => {
|
|
1122
|
+
if (input.optimizingAfterMs == null || isOptimizingBoundaryArmed) return;
|
|
1123
|
+
isOptimizingBoundaryArmed = true;
|
|
1124
|
+
optimizingBoundaryTimeout = timer.setTimeout(() => {
|
|
1125
|
+
optimizingBoundaryTimeout = null;
|
|
1126
|
+
isOptimizingBoundaryArmed = false;
|
|
1127
|
+
enterOptimizing();
|
|
1128
|
+
}, input.optimizingAfterMs);
|
|
1129
|
+
};
|
|
1130
|
+
/**
|
|
1131
|
+
* Reports the boundary with the candidate frame to freeze. Silent when there
|
|
1132
|
+
* is no candidate — no event means no phase, and the session behaves like the
|
|
1133
|
+
* countdown arm (see `scheduleOptimizingBoundary`).
|
|
1134
|
+
*/
|
|
1135
|
+
const enterOptimizing = () => {
|
|
1136
|
+
const candidateCanvas = bestCanvas?.clone();
|
|
1137
|
+
if (!candidateCanvas) return;
|
|
1138
|
+
sendBack({
|
|
1139
|
+
type: "DETECTION_OPTIMIZING",
|
|
1140
|
+
candidateCanvas
|
|
1141
|
+
});
|
|
1142
|
+
};
|
|
1062
1143
|
const sendNotification = (status) => {
|
|
1063
1144
|
if (isReportedSideWrong()) return;
|
|
1064
1145
|
if (notificationTimeout && currentNotificationStatus !== status) return;
|
|
@@ -1146,6 +1227,7 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1146
1227
|
},
|
|
1147
1228
|
onSwitchToManualCapture: () => {
|
|
1148
1229
|
isCapturing = false;
|
|
1230
|
+
clearOptimizingBoundary();
|
|
1149
1231
|
sendBack({ type: "SWITCH_TO_MANUAL_CAPTURE" });
|
|
1150
1232
|
},
|
|
1151
1233
|
onCapturing: () => {
|
|
@@ -1157,6 +1239,7 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1157
1239
|
type: "DETECTION_UPDATE",
|
|
1158
1240
|
status: "capturing"
|
|
1159
1241
|
});
|
|
1242
|
+
scheduleOptimizingBoundary();
|
|
1160
1243
|
},
|
|
1161
1244
|
onBestFrame: (blur, glare, orientation) => {
|
|
1162
1245
|
if (currentCanvas) {
|
|
@@ -1173,16 +1256,24 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1173
1256
|
},
|
|
1174
1257
|
onCapture: () => {
|
|
1175
1258
|
isCapturing = false;
|
|
1176
|
-
if (
|
|
1259
|
+
if (isOptimizingBoundaryArmed) {
|
|
1260
|
+
clearOptimizingBoundary();
|
|
1261
|
+
enterOptimizing();
|
|
1262
|
+
}
|
|
1263
|
+
if (!bestCanvas) {
|
|
1264
|
+
const clonedCanvas = currentCanvas?.clone();
|
|
1265
|
+
if (clonedCanvas) sendBack({
|
|
1266
|
+
type: "DETECTION_SUCCESS",
|
|
1267
|
+
canvas: clonedCanvas,
|
|
1268
|
+
qualityElements: {}
|
|
1269
|
+
});
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
sendBack({
|
|
1177
1273
|
type: "DETECTION_SUCCESS",
|
|
1178
1274
|
canvas: bestCanvas,
|
|
1179
1275
|
qualityElements: storedQualityElements
|
|
1180
1276
|
});
|
|
1181
|
-
else if (currentCanvas) sendBack({
|
|
1182
|
-
type: "DETECTION_SUCCESS",
|
|
1183
|
-
canvas: currentCanvas.clone(),
|
|
1184
|
-
qualityElements: {}
|
|
1185
|
-
});
|
|
1186
1277
|
},
|
|
1187
1278
|
onIdTypeChange: (idType) => {
|
|
1188
1279
|
sendBack({
|
|
@@ -1233,6 +1324,7 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1233
1324
|
notificationTimeout = null;
|
|
1234
1325
|
currentNotificationStatus = null;
|
|
1235
1326
|
}
|
|
1327
|
+
clearOptimizingBoundary();
|
|
1236
1328
|
currentCanvas = null;
|
|
1237
1329
|
bestCanvas = null;
|
|
1238
1330
|
storedQualityElements = {};
|
|
@@ -1251,6 +1343,7 @@ const runDetectionActor = fromCallback(({ input, sendBack }) => {
|
|
|
1251
1343
|
});
|
|
1252
1344
|
return () => {
|
|
1253
1345
|
clearNotificationTimeout();
|
|
1346
|
+
clearOptimizingBoundary();
|
|
1254
1347
|
session?.dispose();
|
|
1255
1348
|
noteTrueSightCaptureActivity(false);
|
|
1256
1349
|
};
|
|
@@ -1338,7 +1431,7 @@ const startRecordingActor = fromPromise(async ({ input }) => {
|
|
|
1338
1431
|
});
|
|
1339
1432
|
const initializeDeepsightSessionActor = fromPromise(async ({ input }) => {
|
|
1340
1433
|
if (!input.dependencies) return;
|
|
1341
|
-
const { loadDeepsightSession } = await import("./deepsightLoader
|
|
1434
|
+
const { loadDeepsightSession } = await import("./deepsightLoader--wMKWECS.esm.js");
|
|
1342
1435
|
return loadDeepsightSession({
|
|
1343
1436
|
ds: input.ds,
|
|
1344
1437
|
storage: input.dependencies.storage,
|
|
@@ -1376,11 +1469,11 @@ function performResetForFrontCapture(context) {
|
|
|
1376
1469
|
function performResetDetection(context) {
|
|
1377
1470
|
context.resetDetection?.();
|
|
1378
1471
|
}
|
|
1379
|
-
function
|
|
1380
|
-
if (context.config.
|
|
1381
|
-
const assignment = exposeExperimentOnce(
|
|
1472
|
+
function performReportCaptureOptimizingAssignment(context) {
|
|
1473
|
+
if (context.config.supportsOptimizingStatus !== true) return;
|
|
1474
|
+
const assignment = exposeExperimentOnce(CAPTURE_OPTIMIZING);
|
|
1382
1475
|
if (!assignment) return;
|
|
1383
|
-
recordExperimentAssignmentOnce(eventModuleNames.idCapture,
|
|
1476
|
+
recordExperimentAssignmentOnce(eventModuleNames.idCapture, CAPTURE_OPTIMIZING.storeParam, assignment);
|
|
1384
1477
|
}
|
|
1385
1478
|
function performStopMediaRecording(context) {
|
|
1386
1479
|
if (context.recordingSession) stopRecording(context.recordingSession);
|
|
@@ -1443,6 +1536,7 @@ function getResetContextValues(context) {
|
|
|
1443
1536
|
idType: void 0,
|
|
1444
1537
|
qualityElements: void 0,
|
|
1445
1538
|
previewImageUrl: void 0,
|
|
1539
|
+
...getClearOptimizingValues(context),
|
|
1446
1540
|
uploadProgress: void 0,
|
|
1447
1541
|
currentMode,
|
|
1448
1542
|
selectedDocumentType: void 0,
|
|
@@ -1457,6 +1551,7 @@ function getClearUploadFailureValues(context) {
|
|
|
1457
1551
|
uploadError: void 0,
|
|
1458
1552
|
detectionStatus: "idle",
|
|
1459
1553
|
previewImageUrl: void 0,
|
|
1554
|
+
...getClearOptimizingValues(context),
|
|
1460
1555
|
uploadProgress: void 0
|
|
1461
1556
|
};
|
|
1462
1557
|
}
|
|
@@ -1476,6 +1571,39 @@ function storeCapturedCanvasInProviderLogic(context, event) {
|
|
|
1476
1571
|
const canvasHeight = canvas.height();
|
|
1477
1572
|
if (!canvasWidth || !canvasHeight) return;
|
|
1478
1573
|
const originalCanvas = canvas;
|
|
1574
|
+
const frameRect = resolveCaptureFrameRect(context, canvasWidth, canvasHeight);
|
|
1575
|
+
const transformedCanvas = context.provider.transformPerspective(originalCanvas, frameRect);
|
|
1576
|
+
context.provider.setCapturedCanvases(originalCanvas, transformedCanvas);
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* JPEG quality for the IDC-05 optimizing phase's display-only still.
|
|
1580
|
+
*
|
|
1581
|
+
* `updateBlob()` is synchronous and expensive: `toDataURL` → `atob` → a per-byte
|
|
1582
|
+
* loop building the blob (`IncodeCanvas.base64ToBlob`). At the default quality of
|
|
1583
|
+
* 1.0 on a 1080p frame that is tens of milliseconds of main-thread work — and on
|
|
1584
|
+
* this path it runs inside an `assign`, in the same tick that starts the phase
|
|
1585
|
+
* animation, with the 30fps WASM detection loop still live. Both halves of the
|
|
1586
|
+
* cost scale with the encoded byte count, so dropping quality cuts the encode and
|
|
1587
|
+
* the byte loop together.
|
|
1588
|
+
*
|
|
1589
|
+
* Treatment-arm only, deliberately: the committed frame keeps quality 1.0, since
|
|
1590
|
+
* it is what uploads (`getStoreCapturedImageValues` → `getBase64Image(1.0)`) and
|
|
1591
|
+
* its encode happens on the analyzing screen with no animation to jank. This
|
|
1592
|
+
* still is on screen for one dwell at roughly card size and never leaves the
|
|
1593
|
+
* device.
|
|
1594
|
+
*/
|
|
1595
|
+
const CANDIDATE_FRAME_JPEG_QUALITY = .8;
|
|
1596
|
+
/**
|
|
1597
|
+
* The document's rectangle within a camera frame, in canvas coordinates —
|
|
1598
|
+
* the input to `transformPerspective`.
|
|
1599
|
+
*
|
|
1600
|
+
* Shared by the commit path (`storeCapturedCanvasInProviderLogic`) and the
|
|
1601
|
+
* IDC-05 optimizing phase (`getCandidateFrameValues`) so the still shown
|
|
1602
|
+
* mid-capture is cropped and warped exactly like the frame that later uploads.
|
|
1603
|
+
* Deriving it twice is what made the phase show an uncropped full frame that then
|
|
1604
|
+
* visibly jumped to a cropped one on the analyzing screen.
|
|
1605
|
+
*/
|
|
1606
|
+
function resolveCaptureFrameRect(context, canvasWidth, canvasHeight) {
|
|
1479
1607
|
let frameRect;
|
|
1480
1608
|
const { innerWidth: viewportWidth, innerHeight: viewportHeight } = getWindowDimensions();
|
|
1481
1609
|
if (context.detectionArea) {
|
|
@@ -1507,7 +1635,7 @@ function storeCapturedCanvasInProviderLogic(context, event) {
|
|
|
1507
1635
|
h: context.frameRect.h / scale
|
|
1508
1636
|
};
|
|
1509
1637
|
} else {
|
|
1510
|
-
const quadValue = (context.provider
|
|
1638
|
+
const quadValue = (context.provider?.getLastProcessResult?.())?.quad;
|
|
1511
1639
|
const hasQuad = !!quadValue;
|
|
1512
1640
|
const quadSize = quadValue?.size ? quadValue.size() : quadValue?.length ?? 0;
|
|
1513
1641
|
if (hasQuad && quadSize >= 4 && quadValue.get) {
|
|
@@ -1538,8 +1666,7 @@ function storeCapturedCanvasInProviderLogic(context, event) {
|
|
|
1538
1666
|
};
|
|
1539
1667
|
}
|
|
1540
1668
|
}
|
|
1541
|
-
|
|
1542
|
-
context.provider.setCapturedCanvases(originalCanvas, transformedCanvas);
|
|
1669
|
+
return frameRect;
|
|
1543
1670
|
}
|
|
1544
1671
|
function getStoreCapturedImageValues(context, event) {
|
|
1545
1672
|
if (!context.currentMode) return {
|
|
@@ -1573,7 +1700,79 @@ function getStoreCapturedImageValues(context, event) {
|
|
|
1573
1700
|
}
|
|
1574
1701
|
return {
|
|
1575
1702
|
capturedImages,
|
|
1576
|
-
previewImageUrl
|
|
1703
|
+
previewImageUrl,
|
|
1704
|
+
...getClearOptimizingValues(context)
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* IDC-05: freezes WASM's best-scoring frame *so far* for the optimizing phase.
|
|
1709
|
+
* Display-only — the committed frame arrives later, from `onCapture`, and
|
|
1710
|
+
* `storeCapturedImage` puts that one on `previewImageUrl` while clearing this
|
|
1711
|
+
* one in the same assign, so the rendering layer's
|
|
1712
|
+
* `candidateImageUrl ?? previewImageUrl` falls through to the frame that
|
|
1713
|
+
* actually uploads for the crossfade.
|
|
1714
|
+
*
|
|
1715
|
+
* Revokes any previous URL rather than leaking one per retry.
|
|
1716
|
+
*/
|
|
1717
|
+
function getCandidateFrameValues(context, event) {
|
|
1718
|
+
if (event.type !== "DETECTION_OPTIMIZING" || !event.candidateCanvas) return { candidateImageUrl: context.candidateImageUrl };
|
|
1719
|
+
const candidateCanvas = event.candidateCanvas;
|
|
1720
|
+
const canvas = cropCanvasForPreview(context, candidateCanvas);
|
|
1721
|
+
canvas.updateBlob(CANDIDATE_FRAME_JPEG_QUALITY);
|
|
1722
|
+
const url = canvas.getBlobData()?.url;
|
|
1723
|
+
if (!url) {
|
|
1724
|
+
releasePreviewCanvases(candidateCanvas, canvas, { shouldReleasePreview: true });
|
|
1725
|
+
return { candidateImageUrl: context.candidateImageUrl };
|
|
1726
|
+
}
|
|
1727
|
+
releasePreviewCanvases(candidateCanvas, canvas, { shouldReleasePreview: false });
|
|
1728
|
+
if (context.candidateImageUrl) revokeObjectURL(context.candidateImageUrl);
|
|
1729
|
+
return { candidateImageUrl: url };
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Reclaims the full-frame canvases the phase allocated. At 1080p each is ~8MB of
|
|
1733
|
+
* backing store, and `release()` frees it now instead of at the next GC — which
|
|
1734
|
+
* is what matters on iOS Safari, where canvas memory is its own budget.
|
|
1735
|
+
*
|
|
1736
|
+
* `source` — the clone the detection actor snapshotted — is always dead by the
|
|
1737
|
+
* time this runs: its pixels live on in `preview`'s blob.
|
|
1738
|
+
*
|
|
1739
|
+
* `preview` is the caller's decision, because `release()` also revokes
|
|
1740
|
+
* `blobData.url`. On the success path that URL is the very thing being handed to
|
|
1741
|
+
* the machine as `candidateImageUrl`, so releasing it would blank the phase's
|
|
1742
|
+
* own still; pass `shouldReleasePreview: true` only where no URL survives. Either way
|
|
1743
|
+
* the identity guard covers `transformPerspective` returning the canvas it was
|
|
1744
|
+
* given (no provider, or a WASM failure), so the aliased canvas is never
|
|
1745
|
+
* released twice or released out from under a live URL.
|
|
1746
|
+
*/
|
|
1747
|
+
function releasePreviewCanvases(source, preview, { shouldReleasePreview }) {
|
|
1748
|
+
if (preview === source) {
|
|
1749
|
+
if (shouldReleasePreview) source.release();
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
if (shouldReleasePreview) preview.release();
|
|
1753
|
+
source.release();
|
|
1754
|
+
}
|
|
1755
|
+
function cropCanvasForPreview(context, canvas) {
|
|
1756
|
+
const canvasWidth = canvas.width();
|
|
1757
|
+
const canvasHeight = canvas.height();
|
|
1758
|
+
if (!context.provider || !canvasWidth || !canvasHeight) return canvas;
|
|
1759
|
+
const frameRect = resolveCaptureFrameRect(context, canvasWidth, canvasHeight);
|
|
1760
|
+
return context.provider.transformPerspective(canvas, frameRect);
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Drops the IDC-05 best-so-far still at every attempt/side boundary. Unlike
|
|
1764
|
+
* `previewImageUrl` it is never preserved into `flipPreview` (it was never a
|
|
1765
|
+
* committed frame), so it can always be revoked outright.
|
|
1766
|
+
*
|
|
1767
|
+
* Clears `stashedCommit` alongside it: a commit parked for the phase's dwell is
|
|
1768
|
+
* only meaningful inside that phase, and holding the canvas reference past it
|
|
1769
|
+
* would keep a full-res frame alive for the rest of the attempt.
|
|
1770
|
+
*/
|
|
1771
|
+
function getClearOptimizingValues(context) {
|
|
1772
|
+
if (context.candidateImageUrl) revokeObjectURL(context.candidateImageUrl);
|
|
1773
|
+
return {
|
|
1774
|
+
candidateImageUrl: void 0,
|
|
1775
|
+
stashedCommit: void 0
|
|
1577
1776
|
};
|
|
1578
1777
|
}
|
|
1579
1778
|
function getDetectionStatusFromUpdate(context, event) {
|
|
@@ -1706,6 +1905,7 @@ const idCaptureMachine = setup({
|
|
|
1706
1905
|
qualityElements: void 0,
|
|
1707
1906
|
frameRect: void 0,
|
|
1708
1907
|
previewImageUrl: void 0,
|
|
1908
|
+
...getClearOptimizingValues(context),
|
|
1709
1909
|
flipPreview: context.previewImageUrl ? {
|
|
1710
1910
|
url: context.previewImageUrl,
|
|
1711
1911
|
orientation: context.orientation,
|
|
@@ -1735,6 +1935,7 @@ const idCaptureMachine = setup({
|
|
|
1735
1935
|
qualityElements: void 0,
|
|
1736
1936
|
frameRect: void 0,
|
|
1737
1937
|
previewImageUrl: void 0,
|
|
1938
|
+
...getClearOptimizingValues(context),
|
|
1738
1939
|
flipPreview: context.previewImageUrl ? {
|
|
1739
1940
|
url: context.previewImageUrl,
|
|
1740
1941
|
orientation: context.orientation,
|
|
@@ -1763,6 +1964,7 @@ const idCaptureMachine = setup({
|
|
|
1763
1964
|
qualityElements: void 0,
|
|
1764
1965
|
frameRect: void 0,
|
|
1765
1966
|
previewImageUrl: void 0,
|
|
1967
|
+
...getClearOptimizingValues(context),
|
|
1766
1968
|
uploadProgress: void 0,
|
|
1767
1969
|
manualCaptureTriggered: false,
|
|
1768
1970
|
attemptsRemaining: context.config.captureAttempts
|
|
@@ -1783,8 +1985,8 @@ const idCaptureMachine = setup({
|
|
|
1783
1985
|
trackTutorialId: () => {},
|
|
1784
1986
|
trackContinue: () => {},
|
|
1785
1987
|
trackCameraId: ({ context }) => {},
|
|
1786
|
-
|
|
1787
|
-
|
|
1988
|
+
reportCaptureOptimizingAssignment: ({ context }) => {
|
|
1989
|
+
performReportCaptureOptimizingAssignment(context);
|
|
1788
1990
|
},
|
|
1789
1991
|
preloadRecordingProvider: ({ context }) => {
|
|
1790
1992
|
preloadIdRecordingProvider(context.config);
|
|
@@ -1825,6 +2027,53 @@ const idCaptureMachine = setup({
|
|
|
1825
2027
|
} }),
|
|
1826
2028
|
setCurrentMode: assign({ currentMode: ({ event, context }) => getCurrentModeFromEvent(context, event) }),
|
|
1827
2029
|
storeCapturedImage: assign(({ context, event }) => getStoreCapturedImageValues(context, event)),
|
|
2030
|
+
setCandidateFrame: assign(({ context, event }) => getCandidateFrameValues(context, event)),
|
|
2031
|
+
/**
|
|
2032
|
+
* IDC-05: parks WASM's commit while the optimizing phase spends the rest of
|
|
2033
|
+
* its dwell. The frame travels in context only for that stretch —
|
|
2034
|
+
* `releaseStashedCommit` re-raises it and `getClearOptimizingValues` drops it
|
|
2035
|
+
* on every other exit from the phase.
|
|
2036
|
+
*/
|
|
2037
|
+
stashCommittedFrame: assign({ stashedCommit: ({ event }) => {
|
|
2038
|
+
const commit = event;
|
|
2039
|
+
return {
|
|
2040
|
+
canvas: commit.canvas,
|
|
2041
|
+
qualityElements: commit.qualityElements
|
|
2042
|
+
};
|
|
2043
|
+
} }),
|
|
2044
|
+
/**
|
|
2045
|
+
* IDC-05: hands a parked commit back to the machine as the very event that
|
|
2046
|
+
* would have carried it, so `capturing`'s entry actions stay event-driven.
|
|
2047
|
+
* Re-raised rather than read from context by `capturing` because
|
|
2048
|
+
* `storeCapturedCanvasInProvider` falls back to the *live* frame when the
|
|
2049
|
+
* entering event has no canvas — a timer transition into `capturing` would
|
|
2050
|
+
* stage a live frame for upload instead of the one WASM committed.
|
|
2051
|
+
*/
|
|
2052
|
+
releaseStashedCommit: enqueueActions(({ context, enqueue }) => {
|
|
2053
|
+
const stashed = context.stashedCommit;
|
|
2054
|
+
if (!stashed) return;
|
|
2055
|
+
enqueue(raise({
|
|
2056
|
+
type: "DETECTION_SUCCESS",
|
|
2057
|
+
canvas: stashed.canvas,
|
|
2058
|
+
qualityElements: stashed.qualityElements
|
|
2059
|
+
}));
|
|
2060
|
+
}),
|
|
2061
|
+
/**
|
|
2062
|
+
* IDC-05: releases everything the optimizing phase allocated — the parked
|
|
2063
|
+
* commit AND the frozen still's blob URL. Bound to `optimizing.exit`, so it
|
|
2064
|
+
* covers every route out of the phase, not just the two that already clear
|
|
2065
|
+
* up after themselves (`getStoreCapturedImageValues` on the commit,
|
|
2066
|
+
* `optimizingGrace`'s own action on the abandon path). Before this it
|
|
2067
|
+
* cleared only `stashedCommit`, and an exit through the manual-capture
|
|
2068
|
+
* escape leaked the URL until the next attempt/side boundary.
|
|
2069
|
+
*
|
|
2070
|
+
* Safe to revoke here on the commit path: exit and `capturing`'s entry run
|
|
2071
|
+
* in the same macrostep, and `capturing`'s eventless `always` means the UI
|
|
2072
|
+
* only ever observes the resulting `uploading` snapshot — by which point
|
|
2073
|
+
* `previewImageUrl` is set, so `candidateImageUrl ?? previewImageUrl` never
|
|
2074
|
+
* resolves to nothing (see `getStoreCapturedImageValues`).
|
|
2075
|
+
*/
|
|
2076
|
+
clearOptimizingPhase: assign(({ context }) => getClearOptimizingValues(context)),
|
|
1828
2077
|
storeBarcodeResponse: assign(({ context, event }) => getBarcodeResponseValues(context, event)),
|
|
1829
2078
|
storeDecodedBarcode: assign({ decodedBarcode: ({ event }) => event.decoded }),
|
|
1830
2079
|
setBarcodeFastPathAttempted: assign({ barcodeFastPathAttempted: true }),
|
|
@@ -1892,13 +2141,15 @@ const idCaptureMachine = setup({
|
|
|
1892
2141
|
hasInitializedCamera: hasInitializedCameraGuard,
|
|
1893
2142
|
hasAttemptsRemaining: hasAttemptsRemainingGuard,
|
|
1894
2143
|
hasCapturedImage: hasCapturedImageGuard,
|
|
2144
|
+
hasCandidateFrame: hasCandidateFrameGuard,
|
|
2145
|
+
hasStashedCommit: hasStashedCommitGuard,
|
|
1895
2146
|
hasUploadValidationError: hasUploadValidationErrorGuard,
|
|
1896
2147
|
isFrontMode: isFrontModeGuard,
|
|
1897
2148
|
shouldContinueToBack: shouldContinueToBackGuard,
|
|
1898
2149
|
shouldContinueToFront: shouldContinueToFrontGuard,
|
|
1899
2150
|
hasMandatoryConsent: hasMandatoryConsentGuard,
|
|
1900
|
-
|
|
1901
|
-
|
|
2151
|
+
isCaptureOptimizingMode: isCaptureOptimizingModeGuard,
|
|
2152
|
+
supportsOptimizingStatus: supportsOptimizingStatusGuard,
|
|
1902
2153
|
isDeepsightEnabled: isDeepsightEnabledGuard,
|
|
1903
2154
|
isDeepsightReady: isDeepsightReadyGuard,
|
|
1904
2155
|
needsDeepsightInit: needsDeepsightInitGuard,
|
|
@@ -1914,6 +2165,7 @@ const idCaptureMachine = setup({
|
|
|
1914
2165
|
shouldSkipProcessId: shouldSkipProcessIdGuard,
|
|
1915
2166
|
shouldSubmitBarcode: shouldSubmitBarcodeGuard,
|
|
1916
2167
|
canRunBarcodeFastPath: canRunBarcodeFastPathGuard,
|
|
2168
|
+
hasManualCaptureTriggered: hasManualCaptureTriggeredGuard,
|
|
1917
2169
|
isProcessingExpiredExhausted: isProcessingExpiredExhaustedGuard,
|
|
1918
2170
|
shouldProcessAfterExhaustion: shouldProcessAfterExhaustionGuard,
|
|
1919
2171
|
forceFinishAfterProcessing: forceFinishAfterProcessingGuard,
|
|
@@ -1922,6 +2174,36 @@ const idCaptureMachine = setup({
|
|
|
1922
2174
|
isManualUploadExhausted: ({ event }) => event.output?.result === "exhausted",
|
|
1923
2175
|
isDigitalUploadExhausted: ({ event }) => event.output?.result === "exhausted",
|
|
1924
2176
|
isDigitalUploadSuccessful: ({ event }) => event.output?.result === "success"
|
|
2177
|
+
},
|
|
2178
|
+
delays: {
|
|
2179
|
+
/**
|
|
2180
|
+
* IDC-05: how long the optimizing phase lasts, on BOTH paths — the auto
|
|
2181
|
+
* path's `optimizing.holding` and the manual path's `optimizingHold`. One
|
|
2182
|
+
* value, read from context rather than hardcoded, so the experiment payload
|
|
2183
|
+
* governs the phase wherever it is played.
|
|
2184
|
+
*
|
|
2185
|
+
* `?? 0` rather than the default dwell, deliberately: neither state is
|
|
2186
|
+
* reachable outside the treatment arm, and the treatment arm is the only
|
|
2187
|
+
* thing that populates `optimizingDwellMs`
|
|
2188
|
+
* (`resolveCaptureOptimizingTreatment` always sets it on its `optimizing`
|
|
2189
|
+
* branch). An absent value here therefore means the wiring between the two
|
|
2190
|
+
* broke, and falling back to the default would silently add a dwell to a
|
|
2191
|
+
* capture that was never supposed to have one. Zero makes that failure a
|
|
2192
|
+
* pass-through instead of a 1.5s regression.
|
|
2193
|
+
*/
|
|
2194
|
+
optimizingDwell: ({ context }) => context.optimizingDwellMs ?? 0,
|
|
2195
|
+
/**
|
|
2196
|
+
* IDC-05: how much longer the machine waits for WASM's commit once the phase
|
|
2197
|
+
* has had its full dwell (`optimizing.released`). Past this, `onCapture` —
|
|
2198
|
+
* the phase's normal exit — is not coming for the window this still belongs
|
|
2199
|
+
* to, and the phase is abandoned.
|
|
2200
|
+
*
|
|
2201
|
+
* A constant, not `dwell + margin`: the dwell has already been spent in
|
|
2202
|
+
* `holding` by the time this arms, so the total from phase entry is
|
|
2203
|
+
* unchanged. It also means the timer stays armed on a partially-resolved
|
|
2204
|
+
* payload, where `optimizingDwell` above collapses to zero.
|
|
2205
|
+
*/
|
|
2206
|
+
optimizingGrace: OPTIMIZING_WATCHDOG_MARGIN_MS
|
|
1925
2207
|
}
|
|
1926
2208
|
}).createMachine({
|
|
1927
2209
|
id: "idCapture",
|
|
@@ -1932,6 +2214,8 @@ const idCaptureMachine = setup({
|
|
|
1932
2214
|
return {
|
|
1933
2215
|
config: input.config,
|
|
1934
2216
|
captureFeedbackMode: input.captureFeedbackMode ?? "countdown",
|
|
2217
|
+
captureWindowMs: input.captureWindowMs,
|
|
2218
|
+
optimizingDwellMs: input.optimizingDwellMs,
|
|
1935
2219
|
currentMode,
|
|
1936
2220
|
selectedDocumentType: void 0,
|
|
1937
2221
|
stream: void 0,
|
|
@@ -1965,6 +2249,7 @@ const idCaptureMachine = setup({
|
|
|
1965
2249
|
frameRect: void 0,
|
|
1966
2250
|
detectionArea: void 0,
|
|
1967
2251
|
previewImageUrl: void 0,
|
|
2252
|
+
candidateImageUrl: void 0,
|
|
1968
2253
|
uploadProgress: void 0,
|
|
1969
2254
|
manualCaptureTriggered: false,
|
|
1970
2255
|
deepsightService: void 0,
|
|
@@ -2294,7 +2579,7 @@ const idCaptureMachine = setup({
|
|
|
2294
2579
|
guard: "needsDeepsightInit"
|
|
2295
2580
|
},
|
|
2296
2581
|
{
|
|
2297
|
-
target: "
|
|
2582
|
+
target: "choosingDetectionStrategy",
|
|
2298
2583
|
guard: "hasInitializedCamera"
|
|
2299
2584
|
},
|
|
2300
2585
|
{ target: "initializing" }
|
|
@@ -2329,7 +2614,7 @@ const idCaptureMachine = setup({
|
|
|
2329
2614
|
cameraFallbackLevel: context.cameraFallbackLevel
|
|
2330
2615
|
}),
|
|
2331
2616
|
onDone: {
|
|
2332
|
-
target: "
|
|
2617
|
+
target: "choosingDetectionStrategy",
|
|
2333
2618
|
actions: "setStreamAndCapturer"
|
|
2334
2619
|
},
|
|
2335
2620
|
onError: [{
|
|
@@ -2341,13 +2626,20 @@ const idCaptureMachine = setup({
|
|
|
2341
2626
|
actions: assign({ error: ({ event }) => String(event.error) })
|
|
2342
2627
|
}]
|
|
2343
2628
|
} },
|
|
2629
|
+
choosingDetectionStrategy: {
|
|
2630
|
+
entry: ["reportCaptureOptimizingAssignment"],
|
|
2631
|
+
always: [{
|
|
2632
|
+
target: "detectingWithOptimization",
|
|
2633
|
+
guard: and(["isCaptureOptimizingMode", "supportsOptimizingStatus"])
|
|
2634
|
+
}, { target: "detecting" }]
|
|
2635
|
+
},
|
|
2344
2636
|
detecting: {
|
|
2345
2637
|
always: [{
|
|
2346
2638
|
target: "manualCaptureWaiting",
|
|
2347
|
-
guard:
|
|
2639
|
+
guard: "hasManualCaptureTriggered",
|
|
2348
2640
|
actions: assign({ detectionStatus: () => "manualCapture" })
|
|
2349
2641
|
}],
|
|
2350
|
-
entry: [assign({ detectionStatus: () => "detecting" })
|
|
2642
|
+
entry: [assign({ detectionStatus: () => "detecting" })],
|
|
2351
2643
|
invoke: [{
|
|
2352
2644
|
id: "startRecording",
|
|
2353
2645
|
src: "startRecording",
|
|
@@ -2398,6 +2690,113 @@ const idCaptureMachine = setup({
|
|
|
2398
2690
|
ORIENTATION_CHANGE: { actions: "setOrientation" }
|
|
2399
2691
|
}
|
|
2400
2692
|
},
|
|
2693
|
+
/**
|
|
2694
|
+
* IDC-05 treatment arm: same detection as `detecting` above (kept in sync by
|
|
2695
|
+
* hand — see the comment there), plus the two-phase searching -> optimizing
|
|
2696
|
+
* phase.
|
|
2697
|
+
*
|
|
2698
|
+
* Two *distinct* state nodes (this one and `detecting`) rather than one
|
|
2699
|
+
* compound state, deliberately: `snapshot.matches({ capture: 'detecting' })`
|
|
2700
|
+
* keeps meaning exactly what it always meant. Folding the phase into
|
|
2701
|
+
* `detecting` as a substate would make every such selector silently true
|
|
2702
|
+
* while the user is being told the capture succeeded — a hazard no type or
|
|
2703
|
+
* test would catch.
|
|
2704
|
+
*/
|
|
2705
|
+
detectingWithOptimization: {
|
|
2706
|
+
entry: assign({ detectionStatus: () => "detecting" }),
|
|
2707
|
+
always: [{
|
|
2708
|
+
target: "manualCaptureWaiting",
|
|
2709
|
+
guard: "hasManualCaptureTriggered",
|
|
2710
|
+
actions: assign({ detectionStatus: () => "manualCapture" })
|
|
2711
|
+
}],
|
|
2712
|
+
invoke: [{
|
|
2713
|
+
id: "startRecording",
|
|
2714
|
+
src: "startRecording",
|
|
2715
|
+
input: ({ context }) => ({
|
|
2716
|
+
config: context.config,
|
|
2717
|
+
stream: context.stream,
|
|
2718
|
+
existing: context.recordingSession,
|
|
2719
|
+
currentMode: context.currentMode
|
|
2720
|
+
}),
|
|
2721
|
+
onDone: { actions: assign({ recordingSession: ({ context, event }) => {
|
|
2722
|
+
return event.output ?? context.recordingSession;
|
|
2723
|
+
} }) },
|
|
2724
|
+
onError: { actions: () => void 0 }
|
|
2725
|
+
}, {
|
|
2726
|
+
id: "runDetection",
|
|
2727
|
+
src: "runDetection",
|
|
2728
|
+
input: ({ context }) => ({
|
|
2729
|
+
frameCapturer: context.frameCapturer,
|
|
2730
|
+
provider: context.provider,
|
|
2731
|
+
config: context.config,
|
|
2732
|
+
currentMode: context.currentMode,
|
|
2733
|
+
detectionArea: context.detectionArea ?? context.config.detectionArea,
|
|
2734
|
+
optimizingAfterMs: context.captureWindowMs
|
|
2735
|
+
})
|
|
2736
|
+
}],
|
|
2737
|
+
on: {
|
|
2738
|
+
DETECTION_SUCCESS: {
|
|
2739
|
+
target: "capturing",
|
|
2740
|
+
actions: assign({ qualityElements: ({ event }) => event.qualityElements })
|
|
2741
|
+
},
|
|
2742
|
+
DETECTION_RESET_READY: { actions: assign({ resetDetection: ({ event }) => event.reset }) },
|
|
2743
|
+
ID_TYPE_CHANGE: { actions: "setIdType" },
|
|
2744
|
+
ORIENTATION_CHANGE: { actions: "setOrientation" },
|
|
2745
|
+
MANUAL_CAPTURE: { target: "#idCapture.capture.capturingManual" },
|
|
2746
|
+
SWITCH_TO_MANUAL_CAPTURE: {
|
|
2747
|
+
target: "#idCapture.capture.manualCaptureWaiting",
|
|
2748
|
+
actions: assign({
|
|
2749
|
+
detectionStatus: () => "manualCapture",
|
|
2750
|
+
manualCaptureTriggered: () => true
|
|
2751
|
+
})
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
initial: "searching",
|
|
2755
|
+
states: {
|
|
2756
|
+
searching: { on: {
|
|
2757
|
+
DETECTION_UPDATE: { actions: "setDetectionStatus" },
|
|
2758
|
+
ID_SIDE_CHANGE: { actions: assign({ detectionStatus: ({ context, event }) => getDetectionStatusFromSideChange(context, event.side) }) },
|
|
2759
|
+
BARCODE_DECODED: [{
|
|
2760
|
+
target: "#idCapture.capture.capturingBarcode",
|
|
2761
|
+
guard: "canRunBarcodeFastPath",
|
|
2762
|
+
actions: ["storeDecodedBarcode", "setBarcodeFastPathAttempted"]
|
|
2763
|
+
}, { actions: "storeDecodedBarcode" }],
|
|
2764
|
+
DETECTION_OPTIMIZING: {
|
|
2765
|
+
target: "optimizing",
|
|
2766
|
+
guard: "hasCandidateFrame",
|
|
2767
|
+
actions: "setCandidateFrame"
|
|
2768
|
+
}
|
|
2769
|
+
} },
|
|
2770
|
+
optimizing: {
|
|
2771
|
+
exit: "clearOptimizingPhase",
|
|
2772
|
+
on: {
|
|
2773
|
+
BARCODE_DECODED: { actions: "storeDecodedBarcode" },
|
|
2774
|
+
SWITCH_TO_MANUAL_CAPTURE: {
|
|
2775
|
+
guard: "hasStashedCommit",
|
|
2776
|
+
target: "#idCapture.capture.detectingWithOptimization.optimizing.released"
|
|
2777
|
+
},
|
|
2778
|
+
MANUAL_CAPTURE: {
|
|
2779
|
+
guard: "hasStashedCommit",
|
|
2780
|
+
target: "#idCapture.capture.detectingWithOptimization.optimizing.released"
|
|
2781
|
+
}
|
|
2782
|
+
},
|
|
2783
|
+
initial: "holding",
|
|
2784
|
+
states: {
|
|
2785
|
+
holding: {
|
|
2786
|
+
after: { optimizingDwell: { target: "released" } },
|
|
2787
|
+
on: { DETECTION_SUCCESS: { actions: "stashCommittedFrame" } }
|
|
2788
|
+
},
|
|
2789
|
+
released: {
|
|
2790
|
+
entry: "releaseStashedCommit",
|
|
2791
|
+
after: { optimizingGrace: {
|
|
2792
|
+
target: "#idCapture.capture.detectingWithOptimization.searching",
|
|
2793
|
+
actions: assign({ detectionStatus: () => "detecting" })
|
|
2794
|
+
} }
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
},
|
|
2401
2800
|
manualCaptureWaiting: { on: { MANUAL_CAPTURE: { target: "capturingManual" } } },
|
|
2402
2801
|
capturing: {
|
|
2403
2802
|
entry: [
|
|
@@ -2410,14 +2809,6 @@ const idCaptureMachine = setup({
|
|
|
2410
2809
|
target: "submittingBarcode",
|
|
2411
2810
|
guard: and(["hasCapturedImage", "shouldSubmitBarcode"])
|
|
2412
2811
|
},
|
|
2413
|
-
{
|
|
2414
|
-
target: "resultPreview",
|
|
2415
|
-
guard: and([
|
|
2416
|
-
"hasCapturedImage",
|
|
2417
|
-
"isCaptureResultPreviewMode",
|
|
2418
|
-
"supportsCaptureResultPreview"
|
|
2419
|
-
])
|
|
2420
|
-
},
|
|
2421
2812
|
{
|
|
2422
2813
|
target: "uploading",
|
|
2423
2814
|
guard: "hasCapturedImage"
|
|
@@ -2431,7 +2822,6 @@ const idCaptureMachine = setup({
|
|
|
2431
2822
|
}
|
|
2432
2823
|
]
|
|
2433
2824
|
},
|
|
2434
|
-
resultPreview: { on: { RESULT_PREVIEW_COMPLETE: { target: "uploading" } } },
|
|
2435
2825
|
submittingBarcode: { invoke: {
|
|
2436
2826
|
id: "submitBarcode",
|
|
2437
2827
|
src: "submitBarcode",
|
|
@@ -2467,7 +2857,7 @@ const idCaptureMachine = setup({
|
|
|
2467
2857
|
target: "success",
|
|
2468
2858
|
actions: ["storeBarcodeResponse", "markBarcodeFastPathCaptured"]
|
|
2469
2859
|
},
|
|
2470
|
-
onError: { target: "
|
|
2860
|
+
onError: { target: "choosingDetectionStrategy" }
|
|
2471
2861
|
} },
|
|
2472
2862
|
capturingManual: {
|
|
2473
2863
|
entry: [
|
|
@@ -2477,11 +2867,11 @@ const idCaptureMachine = setup({
|
|
|
2477
2867
|
],
|
|
2478
2868
|
always: [
|
|
2479
2869
|
{
|
|
2480
|
-
target: "
|
|
2870
|
+
target: "optimizingHold",
|
|
2481
2871
|
guard: and([
|
|
2482
2872
|
"hasCapturedImage",
|
|
2483
|
-
"
|
|
2484
|
-
"
|
|
2873
|
+
"isCaptureOptimizingMode",
|
|
2874
|
+
"supportsOptimizingStatus"
|
|
2485
2875
|
])
|
|
2486
2876
|
},
|
|
2487
2877
|
{
|
|
@@ -2497,6 +2887,7 @@ const idCaptureMachine = setup({
|
|
|
2497
2887
|
}
|
|
2498
2888
|
]
|
|
2499
2889
|
},
|
|
2890
|
+
optimizingHold: { after: { optimizingDwell: { target: "uploading" } } },
|
|
2500
2891
|
uploading: {
|
|
2501
2892
|
entry: assign({ uploadProgress: () => 0 }),
|
|
2502
2893
|
invoke: [{
|
|
@@ -2691,7 +3082,7 @@ const idCaptureMachine = setup({
|
|
|
2691
3082
|
}
|
|
2692
3083
|
},
|
|
2693
3084
|
onDone: [{
|
|
2694
|
-
target: "#idCapture.capture.
|
|
3085
|
+
target: "#idCapture.capture.choosingDetectionStrategy",
|
|
2695
3086
|
guard: "hasStream",
|
|
2696
3087
|
actions: "clearFlipPreview"
|
|
2697
3088
|
}, {
|
|
@@ -2743,7 +3134,7 @@ const idCaptureMachine = setup({
|
|
|
2743
3134
|
}
|
|
2744
3135
|
},
|
|
2745
3136
|
onDone: [{
|
|
2746
|
-
target: "#idCapture.capture.
|
|
3137
|
+
target: "#idCapture.capture.choosingDetectionStrategy",
|
|
2747
3138
|
guard: "hasStream",
|
|
2748
3139
|
actions: "clearFlipPreview"
|
|
2749
3140
|
}, {
|