@incodetech/core 0.0.0-dev-20260729-9e7b030a → 0.0.0-dev-20260729-a31df3b7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/{FaceDetectionProvider-BHweGBEM.d.ts → FaceDetectionProvider-Mh6q3EbA.d.ts} +1 -2
  2. package/dist/IIdCaptureCapability-Cj_aceph.d.ts +231 -0
  3. package/dist/{IStorageCapability-B8IGxCwe.d.ts → IStorageCapability-ORkQfK3F.d.ts} +112 -1
  4. package/dist/ae-signature.esm.js +1 -1
  5. package/dist/{apiError-CzR7Y6Ua.esm.js → apiError-CS9ZMDr2.esm.js} +1 -1
  6. package/dist/authentication.d.ts +3 -3
  7. package/dist/authentication.esm.js +2 -2
  8. package/dist/{authenticationManager-CmkHvt7_.esm.js → authenticationManager-Cd4tefr2.esm.js} +2 -2
  9. package/dist/{authenticationManager-BGM8bpD0.d.ts → authenticationManager-Y2_MuGXy.d.ts} +1 -1
  10. package/dist/{authenticationStateMachine-O3acuOqq.esm.js → authenticationStateMachine-BnH1mizY.esm.js} +2 -2
  11. package/dist/consent.d.ts +1 -1
  12. package/dist/{consentManager-uhDPdU6i.d.ts → consentManager-5HWbGwOa.d.ts} +9 -9
  13. package/dist/{deepsightLoader-CaUpu0Dy.esm.js → deepsightLoader-CN4B5ugn.esm.js} +3 -3
  14. package/dist/{deepsightService-CR-CYdka.d.ts → deepsightService-DLVQS_RH.d.ts} +2 -112
  15. package/dist/document-capture.d.ts +1 -1
  16. package/dist/document-upload.d.ts +4 -4
  17. package/dist/{documentCaptureManager-pEqMY_Oo.d.ts → documentCaptureManager-DeSYkGWc.d.ts} +70 -70
  18. package/dist/dynamic-forms.esm.js +2 -2
  19. package/dist/{dynamicFormsManager-BxUgij0L.esm.js → dynamicFormsManager-B80PGYEF.esm.js} +1 -1
  20. package/dist/{dynamicFormsStateMachine-D0cEsDMH.esm.js → dynamicFormsStateMachine-B7R09HRI.esm.js} +1 -1
  21. package/dist/ekyb.esm.js +3 -3
  22. package/dist/{ekybManager-LicVPYug.esm.js → ekybManager-Cg_boKM5.esm.js} +1 -1
  23. package/dist/{ekybStateMachine-IgLa1nXx.esm.js → ekybStateMachine-CAXjJ2gA.esm.js} +1 -1
  24. package/dist/electronic-signature.esm.js +1 -1
  25. package/dist/{electronicSignatureManager-DqPJCXMG.esm.js → electronicSignatureManager-JuycNykG.esm.js} +1 -1
  26. package/dist/extensibility.d.ts +14 -14
  27. package/dist/extensibility.esm.js +8 -8
  28. package/dist/{faceCaptureManagerFactory-2W_XduCs.esm.js → faceCaptureManagerFactory-Cd1uQnGS.esm.js} +1 -1
  29. package/dist/{faceCaptureManagerFactory-B92LFT16.d.ts → faceCaptureManagerFactory-K8iLKaRl.d.ts} +5 -4
  30. package/dist/{faceCaptureSetup-D-9s0vUS.esm.js → faceCaptureSetup-CS7exiIh.esm.js} +3 -2
  31. package/dist/flow.d.ts +5 -5
  32. package/dist/flow.esm.js +3 -3
  33. package/dist/{flowCompletionService-D_DJ1dNA.esm.js → flowCompletionService-C77_-GvG.esm.js} +1 -1
  34. package/dist/{flowServices-ZgW3UGcz.esm.js → flowServices-BuSZYjB9.esm.js} +3 -3
  35. package/dist/geolocation.esm.js +2 -2
  36. package/dist/{geolocationManager-oLdeMbdy.esm.js → geolocationManager-D2ptN5BO.esm.js} +1 -1
  37. package/dist/{geolocationStateMachine-CSDKlrHE.esm.js → geolocationStateMachine-fOQUnMxw.esm.js} +1 -1
  38. package/dist/id-ocr.d.ts +1 -1
  39. package/dist/id-selfie-flow.d.ts +179 -0
  40. package/dist/id-selfie-flow.esm.js +917 -0
  41. package/dist/id.d.ts +3 -2
  42. package/dist/id.esm.js +3 -2
  43. package/dist/{idCaptureManager-DAsfVDWq.esm.js → idCaptureManager-BokR8KKO.esm.js} +2 -1
  44. package/dist/{idCaptureManager-B2w2CGuV.d.ts → idCaptureManager-wKz4MXR8.d.ts} +10 -2
  45. package/dist/idCaptureServices-C3nAZ64o.esm.js +353 -0
  46. package/dist/{idCaptureStateMachine-BMZCf9M7.esm.js → idCaptureStateMachine-CM8EP_MM.esm.js} +38 -374
  47. package/dist/{idOcrManager-DXc_xRN7.d.ts → idOcrManager-dLuxey6y.d.ts} +1 -1
  48. package/dist/identity-reuse.d.ts +1 -1
  49. package/dist/{identityReuseManager-v2TH5g7e.d.ts → identityReuseManager-D6pD4hcZ.d.ts} +1 -1
  50. package/dist/index.esm.js +1 -1
  51. package/dist/mandatory-consent.d.ts +2 -2
  52. package/dist/{mandatoryConsentManager-DOZSFZA2.d.ts → mandatoryConsentManager-DXlHM88Z.d.ts} +6 -6
  53. package/dist/manualReviewService-BnFXpAg9.esm.js +15 -0
  54. package/dist/{onDeviceFaceResults-D8FzOXsM.esm.js → onDeviceFaceResults-DFEU5wcC.esm.js} +3 -3
  55. package/dist/personhood.d.ts +4 -4
  56. package/dist/personhood.esm.js +3 -3
  57. package/dist/qe-signature.esm.js +1 -1
  58. package/dist/{recordingService-D6Hbc408.esm.js → recordingService-BwaHFQSV.esm.js} +2 -16
  59. package/dist/redirect-to-mobile.d.ts +2 -2
  60. package/dist/{redirectToMobileManager-BiVH2Ymm.d.ts → redirectToMobileManager-BD2PvT5B.d.ts} +1 -1
  61. package/dist/{runChildModule-CVKUUVl2.esm.js → runChildModule-1jsZfyb9.esm.js} +1 -1
  62. package/dist/selfie.d.ts +4 -4
  63. package/dist/selfie.esm.js +4 -4
  64. package/dist/{selfieManager-Cnw55Hp_.esm.js → selfieManager-hwv10r0j.esm.js} +2 -2
  65. package/dist/{selfieManager-CxeMb2tM.d.ts → selfieManager-tMxWiUk6.d.ts} +1 -1
  66. package/dist/{selfieStateMachine-D-wolitw.esm.js → selfieStateMachine-Cwb5y4OR.esm.js} +2 -2
  67. package/dist/{session-XugVyrIq.esm.js → session-DWfXLkyx.esm.js} +4 -4
  68. package/dist/session.esm.js +4 -4
  69. package/dist/{sessionInitializer-B3g2uAsl.esm.js → sessionInitializer-B58TmxdW.esm.js} +2 -2
  70. package/dist/{setup-BVuwm-QH.esm.js → setup-sxUVsl_J.esm.js} +4 -4
  71. package/dist/trueSight-D7YiTge6.esm.js +1 -0
  72. package/dist/{trueSightService-DGkNMx-E.esm.js → trueSightService-BIbE2pK1.esm.js} +1 -1
  73. package/dist/{types-4-pU2Nhm.d.ts → types-BsTkSXlg2.d.ts} +13 -232
  74. package/dist/{types-CrUhpMDP.d.ts → types-EELWyWpd.d.ts} +1 -1
  75. package/dist/{types-BBQjIaK4.d.ts → types-Hmd7U0JA.d.ts} +1 -1
  76. package/dist/video-selfie.d.ts +8 -100
  77. package/dist/video-selfie.esm.js +17 -608
  78. package/dist/videoSelfieCameraWarmer-lG-12ONu.esm.js +614 -0
  79. package/dist/videoSelfieStepDetector-144x423X.d.ts +117 -0
  80. package/dist/wasm.esm.js +1 -1
  81. package/dist/workflow.d.ts +1 -1
  82. package/dist/workflow.esm.js +14 -14
  83. package/package.json +8 -3
  84. package/dist/trueSight-DWfPmAL_.esm.js +0 -1
  85. /package/dist/{types-BX_XkMsQ.d.ts → types-BX_XkMsQ2.d.ts} +0 -0
  86. /package/dist/{types-BfgmV7s72.d.ts → types-BfgmV7s7.d.ts} +0 -0
  87. /package/dist/{types-DPIDuhut2.d.ts → types-DPIDuhut.d.ts} +0 -0
@@ -1,543 +1,13 @@
1
1
  import { t as WasmUtilProvider } from "./WasmUtilProvider-D13sxEmA.esm.js";
2
- import { t as ensureWasmPipelines } from "./setup-BVuwm-QH.esm.js";
2
+ import { t as ensureWasmPipelines } from "./setup-sxUVsl_J.esm.js";
3
3
  import { t as BrowserTimerProvider } from "./BrowserTimerProvider-BXhgNxLt.esm.js";
4
4
  import { t as api } from "./api-Bx3gKW_y.esm.js";
5
5
  import { t as endpoints } from "./endpoints-DOfmJylF.esm.js";
6
- import { d as videoSelfieWasmPipelines, l as facingModeForStep, u as resolveVideoSelfieSteps } from "./flowServices-ZgW3UGcz.esm.js";
6
+ import { d as videoSelfieWasmPipelines, l as facingModeForStep, u as resolveVideoSelfieSteps } from "./flowServices-BuSZYjB9.esm.js";
7
7
  import { c as createManager, o as createActor, r as assign, t as setup } from "./xstate.esm-Cf4e0Cfd.esm.js";
8
8
  import { n as fromPromise, t as fromCallback } from "./xstate-actors.esm-CtI5S0f4.esm.js";
9
- import { t as checkPermission } from "./permissionServices-GFikEK6S.esm.js";
10
- import { i as requestCameraWithFallback, r as requestCameraAccess } from "./camera-BmFEyPP_.esm.js";
11
- import { _ as StreamCanvasProcessingSession, a as encryptSelfieImage, l as startDetection, v as FaceDetectionProvider } from "./onDeviceFaceResults-D8FzOXsM.esm.js";
12
- import { n as getBackCameraStream } from "./backCameraStream-BqiXcU28.esm.js";
13
- import { n as getWindowDimensions } from "./getDeviceClass-CeBiEayS.esm.js";
14
- import { t as FACE_ERROR_CODES } from "./types-BN5RPPIZ.esm.js";
15
- import { i as uploadSelfie, r as processFace, t as getFaceErrorCodeFromUnknown } from "./selfieErrorUtils-DCRRRj38.esm.js";
16
- import { t as StreamCanvasCapture } from "./StreamCanvasCapture-D2XcXCZ4.esm.js";
17
9
  import { n as getSupportedMediaRecorderMimeType } from "./mediaRecorderOptions-CUsftWlH.esm.js";
18
- import { n as DEFAULT_ID_CAPTURE_THRESHOLDS, r as IdCaptureProvider, t as DEFAULT_ID_CAPTURE_GEOMETRY } from "./idCaptureDefaults-BDS43fFL.esm.js";
19
- //#region src/modules/video-selfie/videoSelfieCompareService.ts
20
- async function postVideoSelfieCompare(endpoint, base64Image) {
21
- return (await api.post(endpoint, { base64Image })).data;
22
- }
23
- /** Compare a front-ID frame from the video selfie against the captured ID. */
24
- const compareVideoSelfieId = (base64Image) => postVideoSelfieCompare(endpoints.videoSelfieCompareId, base64Image);
25
- /** Compare OCR from a front-ID frame against the captured ID's OCR. */
26
- const compareVideoSelfieOcr = (base64Image) => postVideoSelfieCompare(endpoints.videoSelfieCompareOcr, base64Image);
27
- /** Compare a back-ID frame from the video selfie against the captured ID. */
28
- const compareVideoSelfieBackId = (base64Image) => postVideoSelfieCompare(endpoints.videoSelfieCompareBackId, base64Image);
29
- /** Compare OCR from a back-ID frame against the captured ID's OCR. */
30
- const compareVideoSelfieBackOcr = (base64Image) => postVideoSelfieCompare(endpoints.videoSelfieCompareBackOcr, base64Image);
31
- //#endregion
32
- //#region src/modules/video-selfie/videoSelfieStepDetector.ts
33
- /** Default autocapture window for the selfie step when the config omits one. */
34
- const SELFIE_AUTOCAPTURE_MS = 1e4;
35
- /**
36
- * Whether the backend reported a confident face match on a front-ID compare.
37
- * The backend normalizes `faceRecognitionConfidence` to 0 (no match) or 1 (match).
38
- */
39
- function isFaceRecognitionMatch(confidence) {
40
- if (typeof confidence !== "number") return true;
41
- return confidence >= 1;
42
- }
43
- const SELFIE_SUCCESS_DWELL_MS = 900;
44
- /**
45
- * How long the ID step holds on the "success" state (green check) after the
46
- * backend validates a side, before advancing. Gives the user a beat to see the
47
- * capture succeeded instead of the step jumping silently. Mirrors V1's
48
- * IdVideoSelfie success flash.
49
- */
50
- const ID_SUCCESS_DWELL_MS = 900;
51
- const SELFIE_VALIDATION_ERROR_CODES = [
52
- FACE_ERROR_CODES.FACE_OCCLUDED,
53
- FACE_ERROR_CODES.LIVENESS,
54
- FACE_ERROR_CODES.BRIGHTNESS,
55
- FACE_ERROR_CODES.LENSES,
56
- FACE_ERROR_CODES.MASK,
57
- FACE_ERROR_CODES.CLOSED_EYES,
58
- FACE_ERROR_CODES.HEAD_COVER,
59
- FACE_ERROR_CODES.FACE_NOT_FOUND,
60
- FACE_ERROR_CODES.MULTIPLE_FACES,
61
- FACE_ERROR_CODES.TOO_BLURRY,
62
- FACE_ERROR_CODES.TOO_DARK,
63
- FACE_ERROR_CODES.USER_IS_NOT_RECOGNIZED,
64
- FACE_ERROR_CODES.SPOOF_ATTEMPT_DETECTED,
65
- FACE_ERROR_CODES.FACE_TOO_DARK,
66
- FACE_ERROR_CODES.LENSES_DETECTED,
67
- FACE_ERROR_CODES.FACE_MASK_DETECTED,
68
- FACE_ERROR_CODES.CLOSED_EYES_DETECTED,
69
- FACE_ERROR_CODES.HEAD_COVER_DETECTED,
70
- FACE_ERROR_CODES.FACE_CROPPING_FAILED,
71
- FACE_ERROR_CODES.FACE_TOO_SMALL,
72
- FACE_ERROR_CODES.FACE_TOO_BLURRY,
73
- FACE_ERROR_CODES.BAD_PHOTO_QUALITY,
74
- FACE_ERROR_CODES.SELFIE_IMAGE_LOW_QUALITY
75
- ];
76
- const SELFIE_VALIDATION_STATUS_CODES = [
77
- 3004,
78
- 3005,
79
- 3006,
80
- 3007,
81
- 4010,
82
- 4019,
83
- 4077,
84
- 4078
85
- ];
86
- function getErrorStatus(error) {
87
- if (typeof error !== "object" || error === null) return;
88
- if ("data" in error) {
89
- const data = error.data;
90
- if (typeof data === "object" && data !== null && "status" in data && typeof data.status === "number") return data.status;
91
- }
92
- if ("status" in error && typeof error.status === "number") return error.status;
93
- }
94
- function isSelfieValidationError(error) {
95
- const errorCode = getFaceErrorCodeFromUnknown(error);
96
- if (errorCode !== void 0 && SELFIE_VALIDATION_ERROR_CODES.includes(errorCode)) return true;
97
- const status = getErrorStatus(error);
98
- return status !== void 0 && SELFIE_VALIDATION_STATUS_CODES.includes(status);
99
- }
100
- function isIdComparisonValidationError(error) {
101
- return typeof error === "object" && error !== null && "status" in error && typeof error.status === "number" && error.status > 0;
102
- }
103
- function abortError() {
104
- return /* @__PURE__ */ new Error("aborted");
105
- }
106
- function throwIfAborted(signal) {
107
- if (signal.aborted) throw abortError();
108
- }
109
- /** Resolves after `ms`, or rejects early if `signal` aborts. */
110
- function delay(ms, signal) {
111
- if (signal.aborted) return Promise.reject(abortError());
112
- const timerProvider = BrowserTimerProvider.getInstance();
113
- return new Promise((resolve, reject) => {
114
- const timer = timerProvider.setTimeout(() => {
115
- signal.removeEventListener("abort", onAbort);
116
- resolve();
117
- }, ms);
118
- const onAbort = () => {
119
- timerProvider.clearTimeout(timer);
120
- reject(abortError());
121
- };
122
- signal.addEventListener("abort", onAbort, { once: true });
123
- if (signal.aborted) onAbort();
124
- });
125
- }
126
- function isWrongVideoSelfieIdSide(step, reportedSide) {
127
- const side = reportedSide.toLowerCase();
128
- if (side === "wrong") return true;
129
- const isFront = side.includes("front") && !side.includes("back");
130
- const isBack = side.includes("back") && !side.includes("front");
131
- return step === "frontId" ? isBack : isFront;
132
- }
133
- /**
134
- * Builds the default per-step detector from the module config. Today it
135
- * auto-captures the `selfie` step (face detection → `add/face` → `process/face`);
136
- * ID / proof-of-address / questions / speech advance manually until their
137
- * detectors land.
138
- */
139
- function createDefaultStepDetector(config, deps) {
140
- return ({ step, stream, signal, onStatus }) => {
141
- if (step === "selfie") return detectSelfie({
142
- config,
143
- deps,
144
- stream,
145
- signal,
146
- onStatus
147
- });
148
- if (step === "frontId" || step === "backId") return detectId({
149
- step,
150
- config,
151
- stream,
152
- signal,
153
- onStatus
154
- });
155
- return new Promise(() => {});
156
- };
157
- }
158
- async function detectSelfie(params) {
159
- const { config, deps, stream, signal, onStatus } = params;
160
- const provider = new FaceDetectionProvider();
161
- await provider.initialize({
162
- autocaptureInterval: SELFIE_AUTOCAPTURE_MS,
163
- useOnDeviceWorkflow: false,
164
- videoSelfie: true
165
- });
166
- if (signal.aborted) {
167
- provider.dispose();
168
- throw abortError();
169
- }
170
- provider.setChecksEnabled({
171
- lenses: config.validateLenses ?? false,
172
- mask: config.validateFaceMask ?? false,
173
- closedEyes: config.validateClosedEyes ?? false,
174
- headWear: config.validateHeadCover ?? false,
175
- occlusion: false
176
- });
177
- const imageType = config.useAsSelfie === true ? "selfie" : "videoSelfie";
178
- const capturer = new StreamCanvasCapture(stream);
179
- const detectionConfig = {
180
- showTutorial: false,
181
- validateBrightness: false,
182
- validateLenses: config.validateLenses ?? false,
183
- validateFaceMask: config.validateFaceMask ?? false,
184
- validateClosedEyes: config.validateClosedEyes ?? false,
185
- validateHeadCover: config.validateHeadCover ?? false,
186
- autoCaptureTimeout: SELFIE_AUTOCAPTURE_MS / 1e3
187
- };
188
- const runAttempt = () => new Promise((resolve, reject) => {
189
- if (signal.aborted) {
190
- reject(abortError());
191
- return;
192
- }
193
- let settled = false;
194
- function settle(run) {
195
- if (settled) return;
196
- settled = true;
197
- signal.removeEventListener("abort", onAbort);
198
- run();
199
- }
200
- const detection = startDetection({
201
- config: detectionConfig,
202
- capturer,
203
- provider,
204
- onUpdate: (status) => onStatus?.(status),
205
- onSuccess: (canvas, faceCoordinates) => {
206
- onStatus?.("capturing");
207
- (async () => {
208
- try {
209
- const encryptedBase64Image = await encryptSelfieImage({
210
- canvas,
211
- dependencies: { getWasmUtil: async () => deps.getWasmUtil() }
212
- });
213
- throwIfAborted(signal);
214
- await uploadSelfie({
215
- encryptedBase64Image,
216
- faceCoordinates,
217
- imageType
218
- });
219
- throwIfAborted(signal);
220
- await processFace(imageType);
221
- throwIfAborted(signal);
222
- onStatus?.("success");
223
- await delay(SELFIE_SUCCESS_DWELL_MS, signal);
224
- settle(resolve);
225
- } catch (error) {
226
- settle(() => reject(error instanceof Error ? error : new Error(String(error))));
227
- }
228
- })();
229
- }
230
- });
231
- function onAbort() {
232
- detection.cleanup();
233
- settle(() => reject(abortError()));
234
- }
235
- signal.addEventListener("abort", onAbort, { once: true });
236
- if (signal.aborted) onAbort();
237
- });
238
- try {
239
- await runAttempt();
240
- return { outcome: "success" };
241
- } catch (error) {
242
- if (signal.aborted || !isSelfieValidationError(error)) throw error;
243
- onStatus?.("error");
244
- return {
245
- outcome: "validationFailed",
246
- failure: "selfieVerificationFailed"
247
- };
248
- } finally {
249
- capturer.dispose();
250
- provider.dispose();
251
- }
252
- }
253
- async function detectId(params) {
254
- const { step, config, stream, signal, onStatus } = params;
255
- const provider = new IdCaptureProvider();
256
- await provider.initialize({ videoSelfie: true });
257
- if (signal.aborted) {
258
- provider.dispose();
259
- throw abortError();
260
- }
261
- const windowDimensions = getWindowDimensions();
262
- provider.setGeometry({
263
- ...DEFAULT_ID_CAPTURE_GEOMETRY,
264
- windowOuterWidth: windowDimensions.outerWidth,
265
- windowOuterHeight: windowDimensions.outerHeight,
266
- windowInnerWidth: windowDimensions.innerWidth,
267
- windowInnerHeight: windowDimensions.innerHeight
268
- });
269
- provider.setSettings({
270
- isFixedMask: false,
271
- isIPhone14OrHigher: false,
272
- idType: step === "backId" ? "BackId" : "FrontId",
273
- blurCheckEnabled: true,
274
- glareCheckEnabled: false,
275
- faceQualityCheckEnabled: false,
276
- iouCheckEnabled: true,
277
- fpsLimitEnabled: false
278
- });
279
- provider.setThresholds(DEFAULT_ID_CAPTURE_THRESHOLDS);
280
- provider.setModelType("v2");
281
- const capturer = new StreamCanvasCapture(stream);
282
- return new Promise((resolve, reject) => {
283
- let validating = false;
284
- let wrongSide = false;
285
- let settled = false;
286
- let toreDown = false;
287
- const session = new StreamCanvasProcessingSession({
288
- capturer,
289
- provider
290
- });
291
- function teardown() {
292
- if (toreDown) return;
293
- toreDown = true;
294
- session.dispose();
295
- capturer.dispose();
296
- provider.dispose();
297
- }
298
- function settle(run) {
299
- if (settled) return;
300
- settled = true;
301
- signal.removeEventListener("abort", onAbort);
302
- teardown();
303
- run();
304
- }
305
- function onAbort() {
306
- settle(() => reject(abortError()));
307
- }
308
- provider.setCallbacks({
309
- onFarAway: () => {
310
- if (!validating && !wrongSide) onStatus?.("tooFar");
311
- },
312
- onBlur: () => {
313
- if (!validating && !wrongSide) onStatus?.("blur");
314
- },
315
- onIdSideChange: (side) => {
316
- wrongSide = isWrongVideoSelfieIdSide(step, side);
317
- onStatus?.(wrongSide ? "wrongSide" : "detecting");
318
- },
319
- onCapturing: () => {
320
- if (validating || wrongSide) return;
321
- const base64Image = capturer.getLatestCanvas()?.getBase64Image();
322
- if (!base64Image) return;
323
- validating = true;
324
- onStatus?.("capturing");
325
- (async () => {
326
- try {
327
- const result = await validateIdFrame(step, config, base64Image);
328
- if (result.outcome === "success") {
329
- throwIfAborted(signal);
330
- onStatus?.("success");
331
- await delay(ID_SUCCESS_DWELL_MS, signal);
332
- settle(() => resolve(result));
333
- return;
334
- }
335
- onStatus?.("error");
336
- settle(() => resolve(result));
337
- } catch (error) {
338
- if (signal.aborted) {
339
- settle(() => reject(error instanceof Error ? error : new Error(String(error))));
340
- return;
341
- }
342
- onStatus?.("error");
343
- if (isIdComparisonValidationError(error)) {
344
- settle(() => resolve({
345
- outcome: "validationFailed",
346
- failure: "idScanFailed"
347
- }));
348
- return;
349
- }
350
- settle(() => resolve({
351
- outcome: "retryableFailure",
352
- failure: "idScanFailed"
353
- }));
354
- }
355
- })();
356
- }
357
- });
358
- onStatus?.("detecting");
359
- signal.addEventListener("abort", onAbort, { once: true });
360
- if (signal.aborted) onAbort();
361
- });
362
- }
363
- /** Runs the configured ID/OCR comparisons for a captured ID frame. */
364
- async function validateIdFrame(step, config, base64Image) {
365
- if (step === "backId") {
366
- if (config.compareBackIdEnabled) {
367
- const result = await compareVideoSelfieBackId(base64Image);
368
- if (result.idSideMatched === false || result.success === false) return {
369
- outcome: "validationFailed",
370
- failure: "idTypeMismatch"
371
- };
372
- }
373
- if (config.compareBackOcrEnabled) {
374
- if ((await compareVideoSelfieBackOcr(base64Image)).success === false) return {
375
- outcome: "validationFailed",
376
- failure: "nameVerificationFailed"
377
- };
378
- }
379
- return { outcome: "success" };
380
- }
381
- if (config.compareIdEnabled) {
382
- const result = await compareVideoSelfieId(base64Image);
383
- if (result.idTypeMatched === false) return {
384
- outcome: "validationFailed",
385
- failure: "idTypeMismatch"
386
- };
387
- if (typeof result.faceRecognitionConfidence === "number" && !isFaceRecognitionMatch(result.faceRecognitionConfidence)) return {
388
- outcome: "validationFailed",
389
- failure: "photoVerificationFailed"
390
- };
391
- }
392
- if (config.compareOcrEnabled) {
393
- if ((await compareVideoSelfieOcr(base64Image)).success === false) return {
394
- outcome: "validationFailed",
395
- failure: "nameVerificationFailed"
396
- };
397
- }
398
- return { outcome: "success" };
399
- }
400
- //#endregion
401
- //#region ../infra/src/media/CameraCompositor.ts
402
- const MAX_COMPOSITE_LONG_EDGE = 1280;
403
- const MAX_COMPOSITE_SHORT_EDGE = 720;
404
- const FRAME_INTERVAL_TOLERANCE_RATIO = .9;
405
- /**
406
- * Composites the *active* camera into a canvas and exposes the canvas as a
407
- * single, stable `MediaStream` to record. Switching cameras (front ↔ back)
408
- * just repoints what the canvas draws — the output stream and its audio track
409
- * are never replaced — so a single `MediaRecorder` produces one continuous
410
- * video that follows the camera switches (V1 parity), instead of restarting
411
- * per camera.
412
- *
413
- * The audio track from the initial stream is carried through unchanged across
414
- * video switches, so there's no audio glitch when the camera flips.
415
- */
416
- var CameraCompositor = class {
417
- constructor(options = {}) {
418
- this.canvas = null;
419
- this.ctx = null;
420
- this.video = null;
421
- this.frameRequestId = null;
422
- this.frameRequestKind = null;
423
- this.lastDrawTimeMs = null;
424
- this.lastVideoTimeSeconds = null;
425
- this.audioTrack = null;
426
- this.videoTrack = null;
427
- this.composite = null;
428
- this.fps = options.fps ?? 30;
429
- this.frameIntervalMs = this.fps > 0 ? 1e3 / this.fps : 0;
430
- }
431
- /**
432
- * Begin compositing `stream` and return the composite stream to record. The
433
- * stream's audio track (if any) is carried through unchanged across later
434
- * `switchVideo` calls.
435
- */
436
- start(stream) {
437
- this.canvas = document.createElement("canvas");
438
- this.ctx = this.canvas.getContext("2d");
439
- this.video = document.createElement("video");
440
- this.video.muted = true;
441
- this.video.playsInline = true;
442
- this.audioTrack = stream.getAudioTracks()[0] ?? null;
443
- this.setVideoSource(stream);
444
- const composite = this.canvas.captureStream(this.fps);
445
- if (this.audioTrack) composite.addTrack(this.audioTrack);
446
- this.composite = composite;
447
- this.startDrawLoop();
448
- return composite;
449
- }
450
- /**
451
- * Swap the camera feeding the canvas without interrupting the composite
452
- * stream. Pass a video-only stream (audio is kept from `start`). The previous
453
- * video track is stopped.
454
- */
455
- switchVideo(stream) {
456
- if (!this.composite) return;
457
- const previousVideoTrack = this.videoTrack;
458
- this.setVideoSource(stream);
459
- this.startDrawLoop();
460
- if (previousVideoTrack) previousVideoTrack.stop();
461
- }
462
- /** The composite stream being recorded, if compositing is active. */
463
- get stream() {
464
- return this.composite ?? void 0;
465
- }
466
- /** Stop compositing and release the canvas, audio, and video tracks. */
467
- stop() {
468
- this.cancelDrawLoop();
469
- this.videoTrack?.stop();
470
- this.audioTrack?.stop();
471
- this.composite?.getTracks().forEach((track) => track.stop());
472
- if (this.video) this.video.srcObject = null;
473
- this.canvas = null;
474
- this.ctx = null;
475
- this.video = null;
476
- this.videoTrack = null;
477
- this.audioTrack = null;
478
- this.composite = null;
479
- }
480
- setVideoSource(stream) {
481
- this.videoTrack = stream.getVideoTracks()[0] ?? null;
482
- const video = this.video;
483
- if (!video) return;
484
- video.srcObject = new MediaStream(stream.getVideoTracks());
485
- video.play().catch(() => {});
486
- }
487
- startDrawLoop() {
488
- this.cancelDrawLoop();
489
- this.lastDrawTimeMs = null;
490
- this.lastVideoTimeSeconds = null;
491
- const draw = (timeMs) => {
492
- const elapsed = this.lastDrawTimeMs === null ? Infinity : timeMs - this.lastDrawTimeMs;
493
- if ((this.frameIntervalMs === 0 || elapsed >= this.frameIntervalMs * FRAME_INTERVAL_TOLERANCE_RATIO) && this.drawCurrentFrame()) this.lastDrawTimeMs = timeMs;
494
- this.scheduleNextFrame(draw);
495
- };
496
- this.scheduleNextFrame(draw);
497
- }
498
- scheduleNextFrame(callback) {
499
- const video = this.video;
500
- if (video && typeof video.requestVideoFrameCallback === "function") {
501
- this.frameRequestKind = "video";
502
- this.frameRequestId = video.requestVideoFrameCallback(callback);
503
- return;
504
- }
505
- this.frameRequestKind = "animation";
506
- this.frameRequestId = requestAnimationFrame(callback);
507
- }
508
- cancelDrawLoop() {
509
- if (this.frameRequestId === null) return;
510
- if (this.frameRequestKind === "video" && this.video && typeof this.video.cancelVideoFrameCallback === "function") this.video.cancelVideoFrameCallback(this.frameRequestId);
511
- else cancelAnimationFrame(this.frameRequestId);
512
- this.frameRequestId = null;
513
- this.frameRequestKind = null;
514
- }
515
- drawCurrentFrame() {
516
- const video = this.video;
517
- const canvas = this.canvas;
518
- const ctx = this.ctx;
519
- if (!video || !canvas || !ctx || video.videoWidth <= 0) return false;
520
- if (this.lastVideoTimeSeconds === video.currentTime) return false;
521
- const dimensions = this.getBoundedDimensions(video.videoWidth, video.videoHeight);
522
- if (canvas.width !== dimensions.width || canvas.height !== dimensions.height) {
523
- canvas.width = dimensions.width;
524
- canvas.height = dimensions.height;
525
- }
526
- ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
527
- this.lastVideoTimeSeconds = video.currentTime;
528
- return true;
529
- }
530
- getBoundedDimensions(width, height) {
531
- const longEdge = Math.max(width, height);
532
- const shortEdge = Math.min(width, height);
533
- const scale = Math.min(1, MAX_COMPOSITE_LONG_EDGE / longEdge, MAX_COMPOSITE_SHORT_EDGE / shortEdge);
534
- return {
535
- width: Math.max(1, Math.round(width * scale)),
536
- height: Math.max(1, Math.round(height * scale))
537
- };
538
- }
539
- };
540
- //#endregion
10
+ import { i as createDefaultStepDetector, n as defaultAcquireCamera, r as CameraCompositor, t as createVideoSelfieCameraWarmer } from "./videoSelfieCameraWarmer-lG-12ONu.esm.js";
541
11
  //#region ../infra/src/providers/browser/MultipartRecordingProvider.ts
542
12
  /**
543
13
  * S3 multipart requires every part except the last to be at least 5 MB, so we
@@ -767,80 +237,6 @@ var MultipartRecordingService = class {
767
237
  }
768
238
  };
769
239
  //#endregion
770
- //#region src/modules/video-selfie/videoSelfieCameraWarmer.ts
771
- /**
772
- * Acquire the camera for a Video Selfie step. The rear (`environment`) steps
773
- * (frontId / backId / poa) route through {@link getBackCameraStream} so the
774
- * main autofocus lens is selected — Samsung's default `facingMode:environment`
775
- * camera is often a fixed-focus auxiliary lens, which blurs the ID steps and
776
- * stalls capture (client-side blur check is on). This mirrors the ID Capture
777
- * module. `getBackCameraStream` returns video only, so audio is acquired and
778
- * merged separately when the step needs it. Front (`user`) steps keep the
779
- * plain fallback path. Used both as the recording machine's default
780
- * `acquireCamera` dependency and as this warmer's own acquire/fallback.
781
- */
782
- async function defaultAcquireCamera(facingMode, withAudio) {
783
- if (facingMode !== "environment") return requestCameraWithFallback({
784
- video: { facingMode },
785
- audio: withAudio
786
- });
787
- const { stream } = await getBackCameraStream();
788
- if (withAudio) try {
789
- const audioStream = await requestCameraAccess({ audio: true });
790
- for (const audioTrack of audioStream.getAudioTracks()) stream.addTrack(audioTrack);
791
- } catch (error) {
792
- stream.getTracks().forEach((track) => track.stop());
793
- throw error;
794
- }
795
- return stream;
796
- }
797
- function stopStream(stream) {
798
- stream?.getTracks().forEach((track) => track.stop());
799
- }
800
- /**
801
- * Creates a {@link VideoSelfieCameraWarmer} for the given config. The first
802
- * camera (and whether it needs audio) is derived from the resolved step order —
803
- * the same first acquire the recording machine performs on `START`.
804
- */
805
- function createVideoSelfieCameraWarmer(config) {
806
- const firstFacing = facingModeForStep(resolveVideoSelfieSteps(config)[0] ?? "selfie");
807
- let warming = null;
808
- let warmed = null;
809
- let consumed = false;
810
- return {
811
- warm() {
812
- if (warming) return;
813
- warming = (async () => {
814
- try {
815
- if (await checkPermission() !== "granted") return null;
816
- warmed = await defaultAcquireCamera(firstFacing, true);
817
- return warmed;
818
- } catch {
819
- return null;
820
- }
821
- })();
822
- },
823
- async acquireCamera(facingMode, withAudio) {
824
- if (!consumed && facingMode === firstFacing && withAudio) {
825
- consumed = true;
826
- const stream = warmed ?? (warming ? await warming : null);
827
- warmed = null;
828
- if (stream) return stream;
829
- }
830
- return defaultAcquireCamera(facingMode, withAudio);
831
- },
832
- dispose() {
833
- if (consumed) return;
834
- if (warmed) {
835
- stopStream(warmed);
836
- warmed = null;
837
- } else if (warming) warming.then((stream) => {
838
- if (!consumed) stopStream(stream);
839
- });
840
- }
841
- };
842
- }
843
- //#endregion
844
240
  //#region src/modules/video-selfie/videoSelfieRecordingMachine.ts
845
241
  /** A detector that never settles — used when a step has no auto-capture yet. */
846
242
  const manualStepDetector = () => new Promise(() => {});
@@ -865,6 +261,9 @@ function validationFailureFromEvent(event) {
865
261
  function retryableFailureFromEvent(event) {
866
262
  return event.failure;
867
263
  }
264
+ function orientationFromEvent(event) {
265
+ return event.orientation;
266
+ }
868
267
  const SWITCHING_CAMERA_MIN_DWELL_MS = 3e3;
869
268
  const ID_FLIP_DWELL_MS = 4e3;
870
269
  const FINAL_SUCCESS_DWELL_MS = 3e3;
@@ -968,6 +367,10 @@ const videoSelfieRecordingSetup = setup({
968
367
  onStatus: (status) => sendBack({
969
368
  type: "DETECTION_STATUS",
970
369
  status
370
+ }),
371
+ onOrientation: (orientation) => sendBack({
372
+ type: "ORIENTATION",
373
+ orientation
971
374
  })
972
375
  }).then((result) => {
973
376
  if (result.outcome === "retryableFailure") {
@@ -1006,6 +409,7 @@ const videoSelfieRecordingSetup = setup({
1006
409
  validationFailure: () => void 0
1007
410
  }),
1008
411
  setDetectionStatus: assign({ detectionStatus: ({ event }) => detectionStatusFromEvent(event) }),
412
+ setOrientation: assign({ orientation: ({ event }) => orientationFromEvent(event) }),
1009
413
  setDetectionError: assign({ detectionError: ({ event }) => detectionErrorFromEvent(event) }),
1010
414
  setValidationFailure: assign(({ context, event }) => ({
1011
415
  attemptsRemaining: context.attemptsRemaining === UNLIMITED_ATTEMPTS ? UNLIMITED_ATTEMPTS : Math.max(0, context.attemptsRemaining - 1),
@@ -1020,7 +424,8 @@ const videoSelfieRecordingSetup = setup({
1020
424
  clearDetectionFeedback: assign({
1021
425
  detectionStatus: () => void 0,
1022
426
  detectionError: () => void 0,
1023
- validationFailure: () => void 0
427
+ validationFailure: () => void 0,
428
+ orientation: () => void 0
1024
429
  }),
1025
430
  setUploadFailedError: assign({ error: () => "Video upload failed" }),
1026
431
  resetSession: assign(({ context }) => ({
@@ -1028,6 +433,7 @@ const videoSelfieRecordingSetup = setup({
1028
433
  stream: void 0,
1029
434
  composite: void 0,
1030
435
  detectionStatus: void 0,
436
+ orientation: void 0,
1031
437
  detectionError: void 0,
1032
438
  validationFailure: void 0,
1033
439
  attemptsRemaining: context.attemptsPerStep,
@@ -1087,6 +493,7 @@ const videoSelfieRecordingMachine = videoSelfieRecordingSetup.createMachine({
1087
493
  stream: void 0,
1088
494
  composite: void 0,
1089
495
  detectionStatus: void 0,
496
+ orientation: void 0,
1090
497
  detectionError: void 0,
1091
498
  validationFailure: void 0,
1092
499
  attemptsPerStep: normalizeAttempts(input.attemptsPerStep),
@@ -1143,6 +550,7 @@ const videoSelfieRecordingMachine = videoSelfieRecordingSetup.createMachine({
1143
550
  },
1144
551
  on: {
1145
552
  DETECTION_STATUS: { actions: "setDetectionStatus" },
553
+ ORIENTATION: { actions: "setOrientation" },
1146
554
  DETECTION_DONE: ADVANCE_TRANSITIONS,
1147
555
  NEXT_STEP: ADVANCE_TRANSITIONS,
1148
556
  VALIDATION_FAILED: {
@@ -1286,6 +694,7 @@ function mapState(snapshot) {
1286
694
  stream: context.stream,
1287
695
  composite: context.composite,
1288
696
  detectionStatus: context.detectionStatus,
697
+ orientation: context.orientation,
1289
698
  validationFailure: context.validationFailure,
1290
699
  attemptsRemaining: context.attemptsRemaining,
1291
700
  recordingId: context.recordingId,