@incodetech/core 0.0.0-dev-20260407-2c382da → 0.0.0-dev-20260407-ebd92aa

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 (114) hide show
  1. package/dist/{BaseWasmProvider--AzPfwOm.esm.js → BaseWasmProvider-CQgeReKV.esm.js} +1 -785
  2. package/dist/MotionSensorProvider-BTDLt08l.esm.js +254 -0
  3. package/dist/OpenViduRecordingProvider-PvkEhhDq.esm.js +87 -0
  4. package/dist/StateMachine-DU1S4Cik.d.ts +2 -0
  5. package/dist/{WasmUtilProvider-BonqBWdj.esm.js → WasmUtilProvider-C45HStLj.esm.js} +1 -1
  6. package/dist/{addressSearch-B8Fo4v9V.esm.js → addressSearch-C5qzDvkN.esm.js} +4 -4
  7. package/dist/antifraud.d.ts +1 -1
  8. package/dist/antifraud.esm.js +3 -3
  9. package/dist/authentication.d.ts +11 -9
  10. package/dist/authentication.esm.js +18 -13
  11. package/dist/{authenticationManager-Bd10YGSO.d.ts → authenticationManager-CXTPgE_2.d.ts} +2 -2
  12. package/dist/{authenticationManager-B4gSCgrO.esm.js → authenticationManager-D4esVx-z.esm.js} +7 -6
  13. package/dist/{backCameraStream-DF_d7hTz.esm.js → backCameraStream-CUo1USCT.esm.js} +2 -2
  14. package/dist/camera.d.ts +16 -0
  15. package/dist/camera.esm.js +5 -0
  16. package/dist/consent.d.ts +52 -52
  17. package/dist/consent.esm.js +3 -3
  18. package/dist/curp-validation.d.ts +5 -5
  19. package/dist/curp-validation.esm.js +3 -3
  20. package/dist/{deepsightLoader-DKgEdLIS.esm.js → deepsightLoader-Bs5jHgca.esm.js} +10 -7
  21. package/dist/deepsightService-C3gQ_9Ml.esm.js +493 -0
  22. package/dist/{deepsightService-Ciu0atv5.d.ts → deepsightService-CyEcAH-I.d.ts} +193 -197
  23. package/dist/device.d.ts +46 -0
  24. package/dist/device.esm.js +106 -0
  25. package/dist/document-capture.d.ts +3 -3
  26. package/dist/document-capture.esm.js +6 -6
  27. package/dist/document-upload.d.ts +4 -4
  28. package/dist/document-upload.esm.js +6 -6
  29. package/dist/ekyb.d.ts +3 -3
  30. package/dist/ekyb.esm.js +9 -9
  31. package/dist/ekyc.d.ts +3 -3
  32. package/dist/ekyc.esm.js +5 -5
  33. package/dist/email.d.ts +3 -3
  34. package/dist/email.esm.js +5 -5
  35. package/dist/{emailManager-CpAwvP3v.esm.js → emailManager-CZ2IGW0g.esm.js} +4 -4
  36. package/dist/{emailManager-BQPIJR_O.d.ts → emailManager-CtA6Tvry.d.ts} +2 -2
  37. package/dist/events-BlVGS-8F.esm.js +472 -0
  38. package/dist/events.d.ts +232 -0
  39. package/dist/events.esm.js +4 -0
  40. package/dist/extensibility.d.ts +15 -13
  41. package/dist/extensibility.esm.js +25 -20
  42. package/dist/face-match.d.ts +1 -1
  43. package/dist/face-match.esm.js +3 -3
  44. package/dist/{faceCaptureManagerFactory-CdwpYykv.d.ts → faceCaptureManagerFactory-Bp_mC9b2.d.ts} +8 -6
  45. package/dist/faceCaptureSetup-DzlTPjT9.esm.js +828 -0
  46. package/dist/flow.d.ts +17 -9
  47. package/dist/flow.esm.js +68 -21
  48. package/dist/{flowCompletionService-TnWijsSw.esm.js → flowCompletionService-N6-xypy-.esm.js} +2 -2
  49. package/dist/{flowServices-CO2sIR3D.esm.js → flowServices-xgqnJ64L.esm.js} +3 -3
  50. package/dist/geolocation.d.ts +2 -2
  51. package/dist/geolocation.esm.js +4 -4
  52. package/dist/getBrowser-B800KRAi.esm.js +41 -0
  53. package/dist/getDeviceClass-BKd2FOAf.esm.js +14 -0
  54. package/dist/government-validation.d.ts +2 -2
  55. package/dist/government-validation.esm.js +4 -4
  56. package/dist/http.d.ts +57 -0
  57. package/dist/http.esm.js +3 -0
  58. package/dist/id.d.ts +11 -10
  59. package/dist/id.esm.js +20 -16
  60. package/dist/{idCaptureManager-IdqsAASp.d.ts → idCaptureManager-Bx298yFC.d.ts} +9 -7
  61. package/dist/{idCaptureManager-DaLU1ug7.esm.js → idCaptureManager-bowLKHEs.esm.js} +75 -58
  62. package/dist/index.d.ts +3 -611
  63. package/dist/index.esm.js +10 -562
  64. package/dist/{lib-i-4qLKdG.esm.js → lib-Cq5Wc-Hd.esm.js} +1 -1
  65. package/dist/mandatory-consent.d.ts +55 -55
  66. package/dist/mandatory-consent.esm.js +3 -3
  67. package/dist/openviduLazy-BdsxPGgp.esm.js +12 -0
  68. package/dist/openviduLazy-NTgGtkeK.esm.js +3 -0
  69. package/dist/permissionServices-CDqkkway.esm.js +50 -0
  70. package/dist/phone.d.ts +3 -3
  71. package/dist/phone.esm.js +5 -5
  72. package/dist/{phoneManager-BAPHSXx8.esm.js → phoneManager-CscgaER2.esm.js} +4 -4
  73. package/dist/{phoneManager-DA8WUJtl.d.ts → phoneManager-DlaiZtwZ.d.ts} +2 -2
  74. package/dist/recordingService-BDx_6XR9.esm.js +1024 -0
  75. package/dist/redirect-to-mobile.d.ts +2 -2
  76. package/dist/redirect-to-mobile.esm.js +4 -4
  77. package/dist/selfie.d.ts +11 -9
  78. package/dist/selfie.esm.js +18 -13
  79. package/dist/{selfieManager-BSkzncsa.esm.js → selfieManager-DVvvlMMf.esm.js} +5 -4
  80. package/dist/{selfieManager-Dft5wZQ_.d.ts → selfieManager-UBgYgVnw.d.ts} +2 -2
  81. package/dist/{session-OglUYC9l.esm.js → session-PLjFj_T3.esm.js} +5 -19
  82. package/dist/session.d.ts +46 -2
  83. package/dist/session.esm.js +7 -4
  84. package/dist/setup-DfinQjIF.d.ts +120 -0
  85. package/dist/setup-fvDWE7f8.esm.js +450 -0
  86. package/dist/signature.d.ts +2 -2
  87. package/dist/signature.esm.js +3 -3
  88. package/dist/{stats-BVigf5Rn.esm.js → stats-BZ1kFW4p.esm.js} +1 -1
  89. package/dist/stats.esm.js +2 -2
  90. package/dist/{types-CiLyvJGb.d.ts → types-DcqFYowa.d.ts} +1 -1
  91. package/dist/{types-D8Y1faI5.d.ts → types-DufUK-_f.d.ts} +1 -1
  92. package/dist/types-UPqCMQKa.d.ts +5 -0
  93. package/dist/{StateMachine-CM_aOfNL.d.ts → warmup-yzJJKXFi.d.ts} +1 -4
  94. package/dist/wasm.d.ts +15 -0
  95. package/dist/wasm.esm.js +10 -0
  96. package/dist/{Manager-C6AGXEq_.esm.js → xstate.esm-BXrcOcSC.esm.js} +103 -507
  97. package/package.json +18 -2
  98. package/dist/StreamCanvasCapture-DkpkIgyL.esm.js +0 -410
  99. package/dist/endpoints-CI5-28jT.esm.js +0 -69
  100. package/dist/faceCaptureSetup-DbXgtcp7.esm.js +0 -1561
  101. package/dist/id-CzDAG_kM.esm.js +0 -0
  102. package/dist/index-BuVKc9bv.d.ts +0 -1
  103. /package/dist/{Actor-By8WNHi-.d.ts → Actor-Cz_KnN47.d.ts} +0 -0
  104. /package/dist/{BrowserEnvironmentProvider-BECjyw3_.esm.js → BrowserEnvironmentProvider--xQUs3sg.esm.js} +0 -0
  105. /package/dist/{BrowserTimerProvider-CIqH6hJp.esm.js → BrowserTimerProvider-CkHEwJFa.esm.js} +0 -0
  106. /package/dist/{Manager-CCT_-HuZ.d.ts → Manager-N01KfnVA.d.ts} +0 -0
  107. /package/dist/{api-C2uzxrpN.esm.js → api-CyI8qTie.esm.js} +0 -0
  108. /package/dist/{browserSimulation-DYAk0Ipe.esm.js → browserSimulation-yzDdWSBf.esm.js} +0 -0
  109. /package/dist/{camera-6XJdOeBF.d.ts → camera-BcVDCZxW.d.ts} +0 -0
  110. /package/dist/{camera-DAuRUvnp.esm.js → camera-CsT06Zqy.esm.js} +0 -0
  111. /package/dist/{chunk-D6hkk-hd.esm.js → chunk-BcQWkoAq.esm.js} +0 -0
  112. /package/dist/{displayErrors-CGJwvrTl.d.ts → displayErrors-C0mf2glb.d.ts} +0 -0
  113. /package/dist/{flowCompletionService-BOd4Rk8n.d.ts → flowCompletionService-Dloh6J8n.d.ts} +0 -0
  114. /package/dist/{platform-DdhkonXk.esm.js → platform-Dcuv6w60.esm.js} +0 -0
@@ -0,0 +1,1024 @@
1
+ import { i as WasmPipelineType, r as mlWasmJSApi_default, t as BaseWasmProvider } from "./BaseWasmProvider-CQgeReKV.esm.js";
2
+ import { t as api } from "./api-CyI8qTie.esm.js";
3
+ import { _ as endpoints } from "./events-BlVGS-8F.esm.js";
4
+ import { n as IncodeCanvas } from "./deepsightService-C3gQ_9Ml.esm.js";
5
+ import { i as stopCameraStream, r as requestCameraAccess } from "./camera-CsT06Zqy.esm.js";
6
+ import { t as addDeviceStats } from "./stats-BZ1kFW4p.esm.js";
7
+ import { a as isIPhone14OrHigher, n as isDesktop, r as isIOS, t as isAndroid } from "./platform-Dcuv6w60.esm.js";
8
+ import { n as getDesktopCameraStream, t as getBackCameraStream } from "./backCameraStream-CUo1USCT.esm.js";
9
+ import { t as getDeviceClass } from "./getDeviceClass-BKd2FOAf.esm.js";
10
+
11
+ //#region ../infra/src/providers/browser/BrowserStorageProvider.ts
12
+ /**
13
+ * Browser-based storage provider wrapping localStorage.
14
+ * Handles JSON serialization/deserialization automatically.
15
+ */
16
+ var BrowserStorageProvider = class {
17
+ /**
18
+ * Retrieves a value from localStorage.
19
+ * @param key - The storage key
20
+ * @returns The stored value or null if not found
21
+ * @throws Error if stored value is invalid JSON
22
+ */
23
+ async get(key) {
24
+ try {
25
+ const item = localStorage.getItem(key);
26
+ if (item === null) return null;
27
+ return JSON.parse(item);
28
+ } catch (error) {
29
+ if (error instanceof SyntaxError) throw new Error(`Invalid JSON stored at key "${key}": ${error.message}`);
30
+ throw error;
31
+ }
32
+ }
33
+ /**
34
+ * Stores a value in localStorage.
35
+ * @param key - The storage key
36
+ * @param value - The value to store (will be serialized as JSON)
37
+ * @throws Error if value cannot be serialized or quota is exceeded
38
+ */
39
+ async set(key, value) {
40
+ try {
41
+ const serialized = JSON.stringify(value);
42
+ localStorage.setItem(key, serialized);
43
+ } catch (error) {
44
+ if ((error instanceof DOMException || error.name === "QuotaExceededError") && error.name === "QuotaExceededError") throw new Error(`Storage quota exceeded for key "${key}"`);
45
+ if (error instanceof TypeError) throw new Error(`Failed to serialize value for key "${key}": ${error.message}`);
46
+ throw error;
47
+ }
48
+ }
49
+ /**
50
+ * Removes a value from localStorage.
51
+ * @param key - The storage key to remove
52
+ */
53
+ async remove(key) {
54
+ localStorage.removeItem(key);
55
+ }
56
+ /**
57
+ * Clears all values from localStorage.
58
+ */
59
+ async clear() {
60
+ localStorage.clear();
61
+ }
62
+ };
63
+
64
+ //#endregion
65
+ //#region src/internal/faceCapture/types.ts
66
+ const FACE_ERROR_CODES = {
67
+ FACE_OCCLUDED: "FACE_OCCLUDED",
68
+ LIVENESS: "LIVENESS_ERROR",
69
+ BRIGHTNESS: "BRIGHTNESS_ERROR",
70
+ LENSES: "LENSES_ERROR",
71
+ MASK: "MASK_ERROR",
72
+ CLOSED_EYES: "CLOSED_EYES_ERROR",
73
+ HEAD_COVER: "HEAD_COVER_ERROR",
74
+ SERVER: "SERVER_ERROR",
75
+ FACE_NOT_FOUND: "FACE_NOT_FOUND",
76
+ MULTIPLE_FACES: "MULTIPLE_FACES",
77
+ TOO_BLURRY: "TOO_BLURRY_ERROR",
78
+ TOO_DARK: "TOO_DARK_ERROR",
79
+ USER_IS_NOT_RECOGNIZED: "USER_IS_NOT_RECOGNIZED",
80
+ SPOOF_ATTEMPT_DETECTED: "SPOOF_ATTEMPT_DETECTED",
81
+ FACE_TOO_DARK: "FACE_TOO_DARK",
82
+ LENSES_DETECTED: "LENSES_DETECTED",
83
+ FACE_MASK_DETECTED: "FACE_MASK_DETECTED",
84
+ CLOSED_EYES_DETECTED: "CLOSED_EYES_DETECTED",
85
+ HEAD_COVER_DETECTED: "HEAD_COVER_DETECTED",
86
+ FACE_CROPPING_FAILED: "FACE_CROPPING_FAILED",
87
+ FACE_TOO_SMALL: "FACE_TOO_SMALL",
88
+ FACE_TOO_BLURRY: "FACE_TOO_BLURRY",
89
+ BAD_PHOTO_QUALITY: "BAD_PHOTO_QUALITY",
90
+ PROCESSING_ERROR: "PROCESSING_ERROR",
91
+ BAD_REQUEST: "BAD_REQUEST",
92
+ NONEXISTENT_CUSTOMER: "NONEXISTENT_CUSTOMER",
93
+ HINT_NOT_PROVIDED: "HINT_NOT_PROVIDED",
94
+ SELFIE_IMAGE_LOW_QUALITY: "SELFIE_IMAGE_LOW_QUALITY"
95
+ };
96
+
97
+ //#endregion
98
+ //#region ../infra/src/media/StreamCanvasCapture.ts
99
+ var StreamCanvasCapture = class {
100
+ constructor(stream, options) {
101
+ this.hasFrame = false;
102
+ this.disposed = false;
103
+ this.eventTarget = new EventTarget();
104
+ this.video = document.createElement("video");
105
+ this.video.srcObject = stream;
106
+ this.video.autoplay = true;
107
+ this.video.playsInline = true;
108
+ this.video.muted = true;
109
+ const settings = stream.getVideoTracks()[0]?.getSettings();
110
+ const initialWidth = options?.width ?? settings?.width ?? 1280;
111
+ const initialHeight = options?.height ?? settings?.height ?? 720;
112
+ this.canvas = document.createElement("canvas");
113
+ this.canvas.width = initialWidth;
114
+ this.canvas.height = initialHeight;
115
+ this.ctx = this.canvas.getContext("2d", { willReadFrequently: true });
116
+ const fps = options?.fps ?? 10;
117
+ const intervalMs = fps > 0 ? Math.max(16, Math.floor(1e3 / fps)) : 0;
118
+ this.video.addEventListener("loadedmetadata", () => {
119
+ if (this.video.videoWidth > 0 && this.video.videoHeight > 0) {
120
+ this.canvas.width = this.video.videoWidth;
121
+ this.canvas.height = this.video.videoHeight;
122
+ }
123
+ });
124
+ try {
125
+ this.video.play();
126
+ } catch {}
127
+ this.rafLoop(intervalMs);
128
+ }
129
+ addEventListener(type, listener, options) {
130
+ this.eventTarget.addEventListener(type, listener, options);
131
+ }
132
+ removeEventListener(type, listener, options) {
133
+ this.eventTarget.removeEventListener(type, listener, options);
134
+ }
135
+ /**
136
+ * Returns the latest cached frame as an {@link IncodeCanvas}.
137
+ */
138
+ getLatestCanvas() {
139
+ if (!this.hasFrame) this.tick();
140
+ if (!this.hasFrame) return null;
141
+ return new IncodeCanvas(this.canvas);
142
+ }
143
+ /**
144
+ * Returns the latest cached frame as raw {@link ImageData}.
145
+ */
146
+ getLatestFrame() {
147
+ if (!this.ctx) return null;
148
+ if (!this.hasFrame) this.tick();
149
+ if (!this.hasFrame) return null;
150
+ try {
151
+ return this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Disposes internal resources and stops the capture loop.
158
+ */
159
+ dispose() {
160
+ if (this.disposed) return;
161
+ this.disposed = true;
162
+ if (this.rafId !== void 0) {
163
+ window.cancelAnimationFrame(this.rafId);
164
+ this.rafId = void 0;
165
+ }
166
+ this.video.srcObject = null;
167
+ this.canvas.width = 0;
168
+ this.canvas.height = 0;
169
+ this.hasFrame = false;
170
+ }
171
+ rafLoop(intervalMs) {
172
+ const loop = (timeMs) => {
173
+ if (this.disposed) return;
174
+ if (intervalMs <= 0 || this.lastTickTimeMs === void 0 || timeMs - this.lastTickTimeMs >= intervalMs) {
175
+ this.lastTickTimeMs = timeMs;
176
+ const previousFrameTimeSeconds = this.lastFrameTimeSeconds;
177
+ this.tick();
178
+ const currentFrameTimeSeconds = this.video.currentTime;
179
+ if (previousFrameTimeSeconds === void 0) {
180
+ if (this.hasFrame) {
181
+ this.lastFrameTimeSeconds = currentFrameTimeSeconds;
182
+ this.eventTarget.dispatchEvent(new Event("frame"));
183
+ }
184
+ } else if (this.hasFrame && currentFrameTimeSeconds !== previousFrameTimeSeconds) {
185
+ this.lastFrameTimeSeconds = currentFrameTimeSeconds;
186
+ this.eventTarget.dispatchEvent(new Event("frame"));
187
+ }
188
+ }
189
+ this.rafId = window.requestAnimationFrame(loop);
190
+ };
191
+ this.rafId = window.requestAnimationFrame(loop);
192
+ }
193
+ tick() {
194
+ if (!this.ctx) return;
195
+ if (this.video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA) return;
196
+ const videoWidth = this.video.videoWidth;
197
+ const videoHeight = this.video.videoHeight;
198
+ if (videoWidth === 0 || videoHeight === 0) return;
199
+ if (this.canvas.width !== videoWidth || this.canvas.height !== videoHeight) {
200
+ this.canvas.width = videoWidth;
201
+ this.canvas.height = videoHeight;
202
+ }
203
+ try {
204
+ this.ctx.drawImage(this.video, 0, 0);
205
+ this.hasFrame = true;
206
+ } catch {
207
+ this.hasFrame = false;
208
+ }
209
+ }
210
+ };
211
+
212
+ //#endregion
213
+ //#region src/internal/manualReview/manualReviewService.ts
214
+ async function flagFaceManualReview() {
215
+ try {
216
+ await api.put(endpoints.updateSession, { manualSelfieCheckNeeded: true });
217
+ } catch {}
218
+ }
219
+ async function flagIdManualReview() {
220
+ try {
221
+ await api.put(endpoints.updateSession, { manualIdCheckNeeded: true });
222
+ } catch {}
223
+ }
224
+
225
+ //#endregion
226
+ //#region ../infra/src/providers/wasm/FaceDetectionProvider.ts
227
+ var FaceDetectionProvider = class extends BaseWasmProvider {
228
+ constructor() {
229
+ super(WasmPipelineType.SelfieWithQualityMetrics);
230
+ this.defaultThresholds = {
231
+ frameMinX: 0,
232
+ frameMinY: 0,
233
+ frameMaxX: 1,
234
+ frameMaxY: 1,
235
+ brightnessThreshold: 50,
236
+ blurrinessThreshold: 50,
237
+ tiltRotationAngleThreshold: 15,
238
+ minMagicCropSize: 200,
239
+ headwearThreshold: .86,
240
+ lensesThreshold: .95,
241
+ closedEyesThreshold: .9,
242
+ maskThreshold: .85,
243
+ minFaceQualityScore: .63,
244
+ faceOcclusionThreshold: .3
245
+ };
246
+ this.currentFrame = null;
247
+ this.bestCanvas = null;
248
+ this.bestFace = null;
249
+ }
250
+ async processFrame(image) {
251
+ this.currentFrame = image;
252
+ await this.processFrameWasm(image);
253
+ }
254
+ async initialize(config) {
255
+ await this.initializeBase(config, "selfie");
256
+ this.applyDefaults(config.autocaptureInterval ?? 0);
257
+ }
258
+ setCallbacks(callbacks) {
259
+ this.ensureInitialized();
260
+ const onBestShotWrapper = (face) => {
261
+ this.bestFace = face;
262
+ if (this.currentFrame) {
263
+ const frameCanvas = IncodeCanvas.fromImageData(this.currentFrame);
264
+ this.bestCanvas = frameCanvas.clone() ?? frameCanvas;
265
+ }
266
+ callbacks.onBestShot?.(face);
267
+ };
268
+ const onCaptureWrapper = () => {
269
+ let frameCanvas = this.bestCanvas;
270
+ if (!frameCanvas && this.currentFrame) frameCanvas = IncodeCanvas.fromImageData(this.currentFrame);
271
+ if (!frameCanvas) return;
272
+ const faceCoordinates = this.bestFace ? this.formatFaceCoordinates(this.bestFace) : this.createDefaultFaceCoordinates(frameCanvas);
273
+ try {
274
+ frameCanvas.updateBase64Image();
275
+ frameCanvas.updateBlob();
276
+ } catch {}
277
+ callbacks.onCapture?.(frameCanvas, faceCoordinates);
278
+ this.bestCanvas = null;
279
+ this.bestFace = null;
280
+ };
281
+ mlWasmJSApi_default.setFaceDetectionCallbacks(this.getPipelineType(), callbacks.onFarAway ?? (() => {}), callbacks.onTooClose ?? (() => {}), callbacks.onTooManyFaces ?? (() => {}), callbacks.onNoFace ?? (() => {}), onCaptureWrapper, callbacks.onGetReady ?? (() => {}), callbacks.onGetReadyFinished ?? (() => {}), callbacks.onCenterFace ?? (() => {}), callbacks.onDark ?? (() => {}), callbacks.onBlur ?? (() => {}), callbacks.onFaceAngle ?? (() => {}), onBestShotWrapper, callbacks.onLenses ?? (() => {}), callbacks.onMask ?? (() => {}), callbacks.onEyesClosed ?? (() => {}), callbacks.onHeadWear ?? (() => {}), callbacks.onSwitchToManualCapture ?? (() => {}), callbacks.onFaceOccluded ?? (() => {}));
282
+ }
283
+ setPositionConstraints(constraints) {
284
+ this.ensureInitialized();
285
+ mlWasmJSApi_default.setFacePositionConstraints(this.getPipelineType(), constraints.minX, constraints.minY, constraints.maxX, constraints.maxY);
286
+ }
287
+ applyDefaults(autocaptureInterval = 0) {
288
+ this.ensureInitialized();
289
+ this.setThresholds({
290
+ brightnessThreshold: this.defaultThresholds.brightnessThreshold,
291
+ blurrinessThreshold: this.defaultThresholds.blurrinessThreshold,
292
+ tiltRotationAngleThreshold: this.defaultThresholds.tiltRotationAngleThreshold,
293
+ minMagicCropSize: this.defaultThresholds.minMagicCropSize,
294
+ autocaptureInterval,
295
+ minFaceQualityScore: this.defaultThresholds.minFaceQualityScore,
296
+ faceOcclusionThreshold: this.defaultThresholds.faceOcclusionThreshold
297
+ });
298
+ this.setPositionConstraints({
299
+ minX: this.defaultThresholds.frameMinX,
300
+ minY: this.defaultThresholds.frameMinY,
301
+ maxX: this.defaultThresholds.frameMaxX,
302
+ maxY: this.defaultThresholds.frameMaxY
303
+ });
304
+ this.setAttributesThresholds({
305
+ headwearThreshold: this.defaultThresholds.headwearThreshold,
306
+ lensesThreshold: this.defaultThresholds.lensesThreshold,
307
+ closedEyesThreshold: this.defaultThresholds.closedEyesThreshold,
308
+ maskThreshold: this.defaultThresholds.maskThreshold
309
+ });
310
+ }
311
+ setAutocaptureInterval(interval) {
312
+ this.ensureInitialized();
313
+ if (!this.currentThresholds) {
314
+ this.applyDefaults(interval);
315
+ return;
316
+ }
317
+ this.setThresholds({
318
+ ...this.currentThresholds,
319
+ autocaptureInterval: interval
320
+ });
321
+ }
322
+ setThresholds(thresholds) {
323
+ this.ensureInitialized();
324
+ this.currentThresholds = { ...thresholds };
325
+ mlWasmJSApi_default.setFaceDetectionThresholds(this.getPipelineType(), thresholds.brightnessThreshold, thresholds.blurrinessThreshold, thresholds.tiltRotationAngleThreshold, thresholds.minMagicCropSize, thresholds.autocaptureInterval, thresholds.minFaceQualityScore, thresholds.faceOcclusionThreshold);
326
+ }
327
+ setAttributesThresholds(thresholds) {
328
+ this.ensureInitialized();
329
+ mlWasmJSApi_default.setFaceAttributesThresholds(this.getPipelineType(), thresholds.headwearThreshold, thresholds.lensesThreshold, thresholds.closedEyesThreshold, thresholds.maskThreshold);
330
+ }
331
+ setChecksEnabled(config) {
332
+ this.ensureInitialized();
333
+ mlWasmJSApi_default.setFaceChecksEnabled(this.getPipelineType(), config.lenses, config.mask, config.closedEyes, config.headWear, config.occlusion);
334
+ }
335
+ setVideoSelfieMode(enabled) {
336
+ this.ensureInitialized();
337
+ mlWasmJSApi_default.setFaceDetectionMode(this.getPipelineType(), enabled);
338
+ }
339
+ reset() {
340
+ super.reset();
341
+ this.currentFrame = null;
342
+ this.bestCanvas = null;
343
+ this.bestFace = null;
344
+ }
345
+ createDefaultFaceCoordinates(canvas) {
346
+ return {
347
+ rightEyeX: 0,
348
+ rightEyeY: 0,
349
+ leftEyeX: 0,
350
+ leftEyeY: 0,
351
+ noseTipX: 0,
352
+ noseTipY: 0,
353
+ rightMouthX: 0,
354
+ rightMouthY: 0,
355
+ mouthX: 0,
356
+ mouthY: 0,
357
+ x: 0,
358
+ y: 0,
359
+ width: canvas.width() ?? 0,
360
+ height: canvas.height() ?? 0
361
+ };
362
+ }
363
+ formatFaceCoordinates(face) {
364
+ return {
365
+ rightEyeX: face.rightEye.x,
366
+ rightEyeY: face.rightEye.y,
367
+ leftEyeX: face.leftEye.x,
368
+ leftEyeY: face.leftEye.y,
369
+ noseTipX: face.noseTip.x,
370
+ noseTipY: face.noseTip.y,
371
+ rightMouthX: face.rightMouthCorner.x,
372
+ rightMouthY: face.rightMouthCorner.y,
373
+ mouthX: face.leftMouthCorner.x,
374
+ mouthY: face.leftMouthCorner.y,
375
+ x: face.rect.x,
376
+ y: face.rect.y,
377
+ width: face.rect.width,
378
+ height: face.rect.height
379
+ };
380
+ }
381
+ };
382
+
383
+ //#endregion
384
+ //#region ../infra/src/media/StreamCanvasProcessingSession.ts
385
+ var StreamCanvasProcessingSession = class {
386
+ /**
387
+ * Creates a processing session that reacts to `StreamCanvasCapture` frame events
388
+ * and drives a provider's `processFrame()` method with backpressure.
389
+ */
390
+ constructor(params) {
391
+ this.disposed = false;
392
+ this.isProcessing = false;
393
+ this.onFrameEvent = () => {
394
+ if (this.disposed || this.isProcessing) return;
395
+ const frame = this.capturer.getLatestFrame();
396
+ if (!frame) return;
397
+ this.onFrame?.(frame);
398
+ this.isProcessing = true;
399
+ this.provider.processFrame(frame).catch(() => {}).finally(() => {
400
+ this.isProcessing = false;
401
+ });
402
+ };
403
+ this.capturer = params.capturer;
404
+ this.provider = params.provider;
405
+ this.onFrame = params.onFrame;
406
+ this.capturer.addEventListener("frame", this.onFrameEvent);
407
+ }
408
+ /**
409
+ * Unsubscribes from frame events and resets the provider.
410
+ */
411
+ dispose() {
412
+ if (this.disposed) return;
413
+ this.disposed = true;
414
+ this.capturer.removeEventListener("frame", this.onFrameEvent);
415
+ this.provider.reset();
416
+ }
417
+ /**
418
+ * Returns whether the session has been disposed.
419
+ */
420
+ isDisposed() {
421
+ return this.disposed;
422
+ }
423
+ };
424
+
425
+ //#endregion
426
+ //#region src/internal/recordings/recordingsRepository.ts
427
+ async function createRecordingSession(type) {
428
+ return (await api.post(endpoints.recordingCreateSessionV2, { type })).data;
429
+ }
430
+ async function startRecording(params) {
431
+ return (await api.post(endpoints.recordingStartV2, {
432
+ videoRecordingId: params.videoRecordingId,
433
+ frameRate: 30,
434
+ outputMode: "COMPOSED",
435
+ resolution: params.resolution,
436
+ type: params.type,
437
+ hasAudio: params.hasAudio ?? false
438
+ })).data;
439
+ }
440
+ async function stopRecording$1(videoRecordingId) {
441
+ return (await api.post(endpoints.recordingStopV2, { videoRecordingId })).data;
442
+ }
443
+ async function uploadDeepsightVideo(encryptedVideo, token) {
444
+ try {
445
+ return (await api.post(endpoints.deepsightVideoImport, {
446
+ video: encryptedVideo,
447
+ type: "selfie"
448
+ }, { headers: { "X-Incode-Hardware-Id": token } })).data.recordingId ?? "";
449
+ } catch {
450
+ return "";
451
+ }
452
+ }
453
+
454
+ //#endregion
455
+ //#region src/modules/selfie/selfieUploadService.ts
456
+ /**
457
+ * Uploads a selfie image to the backend.
458
+ */
459
+ async function uploadSelfie({ encryptedBase64Image, faceCoordinates, signal, metadata, recordingId }) {
460
+ try {
461
+ const payload = {
462
+ base64Image: encryptedBase64Image,
463
+ faceCoordinates: faceCoordinates ?? void 0,
464
+ encrypted: true,
465
+ clientInfo: { deviceClass: getDeviceClass() },
466
+ metadata: metadata ?? void 0
467
+ };
468
+ const query = { imageType: "selfie" };
469
+ if (recordingId) query.recordingId = recordingId;
470
+ const res = await api.post(endpoints.selfie, payload, {
471
+ signal,
472
+ query
473
+ });
474
+ if (!res.ok) throw new Error(`POST ${endpoints.selfie} failed: ${res.status} ${res.statusText}`);
475
+ return res.data;
476
+ } catch (error) {
477
+ const errorCode = getFaceErrorCodeFromHttpError(error);
478
+ if (errorCode) throw new Error(errorCode);
479
+ throw new Error(FACE_ERROR_CODES.SERVER);
480
+ }
481
+ }
482
+ const getFaceErrorCodeFromHttpError = (error) => {
483
+ const err = error;
484
+ if (err.ok !== false || typeof err.status !== "number") return;
485
+ if (err.status !== 400) return FACE_ERROR_CODES.SERVER;
486
+ if (typeof err.data?.status !== "number") return FACE_ERROR_CODES.BAD_REQUEST;
487
+ return {
488
+ 3004: FACE_ERROR_CODES.FACE_NOT_FOUND,
489
+ 3005: FACE_ERROR_CODES.FACE_NOT_FOUND,
490
+ 3006: FACE_ERROR_CODES.TOO_BLURRY,
491
+ 3007: FACE_ERROR_CODES.TOO_DARK,
492
+ 4010: FACE_ERROR_CODES.MULTIPLE_FACES,
493
+ 4019: FACE_ERROR_CODES.FACE_NOT_FOUND,
494
+ 4077: FACE_ERROR_CODES.BAD_PHOTO_QUALITY,
495
+ 4078: FACE_ERROR_CODES.FACE_OCCLUDED
496
+ }[err.data.status] ?? FACE_ERROR_CODES.BAD_REQUEST;
497
+ };
498
+ async function processFace(imageType = "selfie", signal) {
499
+ return (await api.post(endpoints.processFace, {}, {
500
+ query: { imageType },
501
+ signal
502
+ })).data;
503
+ }
504
+
505
+ //#endregion
506
+ //#region src/internal/faceCapture/faceCaptureServices.ts
507
+ function sendLabelInspectionEvent() {
508
+ return addDeviceStats({ cameraLabelInspectionStatus: "FAIL" });
509
+ }
510
+ const MOBILE_SELFIE_CONSTRAINTS = {
511
+ video: {
512
+ facingMode: "user",
513
+ height: { ideal: 480 },
514
+ width: { ideal: 640 }
515
+ },
516
+ audio: false
517
+ };
518
+ const IPHONE14_SELFIE_CONSTRAINTS = {
519
+ video: { height: { ideal: 1080 } },
520
+ audio: false
521
+ };
522
+ function buildCameraConstraints(resolution) {
523
+ if (resolution) return {
524
+ video: {
525
+ facingMode: "user",
526
+ height: resolution.height ? { ideal: resolution.height } : void 0,
527
+ width: resolution.width ? { ideal: resolution.width } : void 0
528
+ },
529
+ audio: false
530
+ };
531
+ if (isIPhone14OrHigher()) return IPHONE14_SELFIE_CONSTRAINTS;
532
+ return MOBILE_SELFIE_CONSTRAINTS;
533
+ }
534
+ function stopStream(stream) {
535
+ stopCameraStream(stream);
536
+ }
537
+ async function getFrontCameraStream(resolution) {
538
+ if (resolution) return requestCameraAccess(buildCameraConstraints(resolution));
539
+ if (isDesktop()) return getDesktopCameraStream({});
540
+ return requestCameraAccess(buildCameraConstraints());
541
+ }
542
+ async function initializeCamera(params) {
543
+ const { config } = params;
544
+ const prcConstraints = buildCameraConstraints(config.cameraResolution);
545
+ const provider = new FaceDetectionProvider();
546
+ await provider.initialize({ autocaptureInterval: config.autoCaptureTimeout * 1e3 });
547
+ provider.setChecksEnabled({
548
+ lenses: config.validateLenses ?? false,
549
+ mask: config.validateFaceMask ?? false,
550
+ closedEyes: config.validateClosedEyes ?? false,
551
+ headWear: config.validateHeadCover ?? false,
552
+ occlusion: false
553
+ });
554
+ await params.deepsightService.performPrcCheck({ constraints: { video: prcConstraints.video } });
555
+ return {
556
+ stream: config.assistedOnboarding ? (await getBackCameraStream()).stream : await getFrontCameraStream(config.cameraResolution),
557
+ provider
558
+ };
559
+ }
560
+ /**
561
+ * Encrypts the provided selfie image using WASM.
562
+ */
563
+ async function encryptSelfieImage({ canvas, dependencies }) {
564
+ const base64Image = canvas.getBase64Image();
565
+ if (!base64Image) throw new Error("Canvas image is empty or null");
566
+ return (await dependencies.getWasmUtil()).encryptImage(base64Image);
567
+ }
568
+ function startDetection(params) {
569
+ let lastStatus;
570
+ let session;
571
+ const { provider } = params;
572
+ const setStatus = (status) => {
573
+ if (session?.isDisposed() === true) return;
574
+ if (lastStatus === status) return;
575
+ lastStatus = status;
576
+ params.onUpdate(status);
577
+ };
578
+ const stopDetectionLoop = () => {
579
+ session?.dispose();
580
+ };
581
+ const reset = () => {
582
+ provider.reset();
583
+ };
584
+ const cleanup = () => {
585
+ stopDetectionLoop();
586
+ };
587
+ (async () => {
588
+ try {
589
+ provider.setCallbacks({
590
+ onFarAway: () => setStatus("tooFar"),
591
+ onTooClose: () => setStatus("tooClose"),
592
+ onTooManyFaces: () => setStatus("tooManyFaces"),
593
+ onNoFace: () => setStatus("idle"),
594
+ onCenterFace: () => setStatus("centerFace"),
595
+ onGetReady: () => setStatus("getReady"),
596
+ onGetReadyFinished: () => setStatus("getReadyFinished"),
597
+ onDark: () => {
598
+ if (params.config.validateBrightness) setStatus("dark");
599
+ },
600
+ onBlur: () => setStatus("blur"),
601
+ onFaceAngle: () => setStatus("faceAngle"),
602
+ onLenses: () => {
603
+ if (params.config.validateLenses) setStatus("lenses");
604
+ },
605
+ onMask: () => {
606
+ if (params.config.validateFaceMask) setStatus("faceMask");
607
+ },
608
+ onEyesClosed: () => {
609
+ if (params.config.validateClosedEyes) setStatus("eyesClosed");
610
+ },
611
+ onHeadWear: () => {
612
+ if (params.config.validateHeadCover) setStatus("headWear");
613
+ },
614
+ onFaceOccluded: () => {},
615
+ onSwitchToManualCapture: () => {
616
+ setStatus("manualCapture");
617
+ stopDetectionLoop();
618
+ },
619
+ onCapture: (canvas, faceCoordinates) => {
620
+ setStatus("success");
621
+ params.onSuccess(canvas, faceCoordinates);
622
+ cleanup();
623
+ }
624
+ });
625
+ setStatus("detecting");
626
+ session = new StreamCanvasProcessingSession({
627
+ capturer: params.capturer,
628
+ provider,
629
+ onFrame: params.onFrame
630
+ });
631
+ } catch {
632
+ setStatus("error");
633
+ cleanup();
634
+ }
635
+ })();
636
+ return {
637
+ cleanup,
638
+ reset
639
+ };
640
+ }
641
+ function buildResolutionFromStream(stream) {
642
+ const track = stream.getVideoTracks()[0];
643
+ if (!track) return;
644
+ const settings = track.getSettings();
645
+ const width = settings.width;
646
+ const height = settings.height;
647
+ if (typeof width === "number" && typeof height === "number") return `${width}x${height}`;
648
+ }
649
+ async function createOpenViduRecordingProvider() {
650
+ const { OpenViduRecordingProvider } = await import("./OpenViduRecordingProvider-PvkEhhDq.esm.js");
651
+ return new OpenViduRecordingProvider();
652
+ }
653
+ async function startRecordingSession(params) {
654
+ if (params.config.deepsightLiveness === "VIDEOLIVENESS") return;
655
+ if (params.config.enableFaceRecording !== true) return;
656
+ if (params.existing) return params.existing;
657
+ const provider = params.config.recording?.capability ?? await createOpenViduRecordingProvider();
658
+ const clonedStream = params.clonedStream;
659
+ const hasAudio = clonedStream.getAudioTracks().length > 0;
660
+ const resolution = buildResolutionFromStream(clonedStream);
661
+ const session = await createRecordingSession("selfie");
662
+ const connection = await provider.connect({
663
+ sessionToken: session.token,
664
+ stream: clonedStream,
665
+ events: {}
666
+ });
667
+ await startRecording({
668
+ videoRecordingId: session.videoRecordingId,
669
+ type: "selfie",
670
+ resolution,
671
+ hasAudio
672
+ });
673
+ return {
674
+ token: session.token,
675
+ sessionId: session.sessionId,
676
+ videoRecordingId: session.videoRecordingId,
677
+ connection,
678
+ resolution,
679
+ hasAudio
680
+ };
681
+ }
682
+ async function stopRecording(session) {
683
+ try {
684
+ await stopRecording$1(session.videoRecordingId);
685
+ } finally {
686
+ await session.connection.disconnect();
687
+ }
688
+ }
689
+ async function initializeDeepsightSession(options) {
690
+ const { loadDeepsightSession } = await import("./deepsightLoader-Bs5jHgca.esm.js");
691
+ return loadDeepsightSession({
692
+ ds: options.ds,
693
+ storage: options.storage
694
+ });
695
+ }
696
+
697
+ //#endregion
698
+ //#region ../infra/src/media/VideoTrimmer.ts
699
+ async function getDurationBySeek(videoBlob) {
700
+ const video = document.createElement("video");
701
+ video.preload = "metadata";
702
+ video.src = URL.createObjectURL(videoBlob);
703
+ try {
704
+ if (!Number.isFinite(video.duration)) {
705
+ video.currentTime = Number.MAX_SAFE_INTEGER;
706
+ await new Promise((resolve) => {
707
+ const onDurationChange = () => {
708
+ if (Number.isFinite(video.duration)) {
709
+ video.removeEventListener("durationchange", onDurationChange);
710
+ video.removeEventListener("timeupdate", onTimeUpdate);
711
+ resolve(video.duration);
712
+ }
713
+ };
714
+ const onTimeUpdate = () => {
715
+ if (Number.isFinite(video.duration)) {
716
+ video.removeEventListener("timeupdate", onTimeUpdate);
717
+ video.removeEventListener("durationchange", onDurationChange);
718
+ resolve(video.duration);
719
+ }
720
+ };
721
+ video.addEventListener("durationchange", onDurationChange);
722
+ video.addEventListener("timeupdate", onTimeUpdate);
723
+ });
724
+ }
725
+ const duration = video.duration;
726
+ return Number.isFinite(duration) ? duration : null;
727
+ } finally {
728
+ URL.revokeObjectURL(video.src);
729
+ video.src = "";
730
+ }
731
+ }
732
+ async function trimLastNSecondsUsingPlayback(videoBlob, seconds) {
733
+ const video = document.createElement("video");
734
+ video.preload = "metadata";
735
+ video.playsInline = true;
736
+ video.muted = true;
737
+ const videoURL = URL.createObjectURL(videoBlob);
738
+ video.src = videoURL;
739
+ const duration = await getDurationBySeek(videoBlob);
740
+ if (!duration || duration < seconds) {
741
+ URL.revokeObjectURL(videoURL);
742
+ return videoBlob;
743
+ }
744
+ const startTime = Math.max(0, Math.floor(duration) - seconds);
745
+ await new Promise((resolve) => {
746
+ if (video.readyState >= 2) resolve();
747
+ else video.addEventListener("loadedmetadata", () => resolve(), { once: true });
748
+ });
749
+ const canvas = document.createElement("canvas");
750
+ canvas.width = 230;
751
+ canvas.height = 320;
752
+ const captureFps = isAndroid() ? 15 : 24;
753
+ const stream = canvas.captureStream(captureFps);
754
+ const ctx = canvas.getContext("2d");
755
+ video.currentTime = startTime;
756
+ await new Promise((resolve) => {
757
+ video.addEventListener("seeked", () => resolve(), { once: true });
758
+ });
759
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
760
+ const mimeType = videoBlob.type || (MediaRecorder.isTypeSupported("video/webm") ? "video/webm" : "video/mp4");
761
+ const mediaRecorder = new MediaRecorder(stream.clone(), {
762
+ mimeType,
763
+ videoBitsPerSecond: 5e5,
764
+ bitsPerSecond: 5e5
765
+ });
766
+ const chunks = [];
767
+ mediaRecorder.ondataavailable = (event) => {
768
+ if (event.data.size > 0) chunks.push(event.data);
769
+ };
770
+ const recordingPromise = new Promise((resolve) => {
771
+ mediaRecorder.onstop = () => {
772
+ const trimmedBlob$1 = new Blob(chunks, { type: mimeType });
773
+ URL.revokeObjectURL(videoURL);
774
+ mediaRecorder.stream?.getTracks().forEach((track) => track.stop());
775
+ stream.getTracks().forEach((track) => track.stop());
776
+ video.src = "";
777
+ resolve(trimmedBlob$1);
778
+ };
779
+ });
780
+ video.addEventListener("play", () => {
781
+ function drawVideo() {
782
+ if (video.currentTime >= duration) {
783
+ mediaRecorder.stop();
784
+ video.pause();
785
+ return;
786
+ }
787
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
788
+ requestAnimationFrame(drawVideo);
789
+ }
790
+ drawVideo();
791
+ setTimeout(() => {
792
+ mediaRecorder.start(100);
793
+ }, 500);
794
+ });
795
+ video.play().catch(() => {
796
+ URL.revokeObjectURL(videoURL);
797
+ });
798
+ const trimmedBlob = await recordingPromise;
799
+ console.timeEnd("trimLastNSecondsUsingPlayback");
800
+ return trimmedBlob;
801
+ }
802
+ async function trimLastNSeconds(videoBlob, seconds) {
803
+ return await trimLastNSecondsUsingPlayback(videoBlob, seconds);
804
+ }
805
+
806
+ //#endregion
807
+ //#region ../infra/src/providers/browser/LocalRecordingProvider.ts
808
+ function toBase64(videoBlob) {
809
+ return new Promise((resolve, reject) => {
810
+ const reader = new FileReader();
811
+ reader.onloadend = () => {
812
+ const base64 = reader.result.split(",")[1];
813
+ resolve(base64);
814
+ };
815
+ reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("FileReader error"));
816
+ reader.readAsDataURL(videoBlob);
817
+ });
818
+ }
819
+ function getSupportedMediaRecorderMimeType() {
820
+ const possibleTypes = isIOS() ? [
821
+ "video/mp4",
822
+ "video/webm",
823
+ "video/webm;codecs=vp9",
824
+ "video/webm;codecs=vp8"
825
+ ] : [
826
+ "video/webm",
827
+ "video/webm;codecs=vp9",
828
+ "video/webm;codecs=vp8",
829
+ "video/mp4"
830
+ ];
831
+ for (const type of possibleTypes) if (MediaRecorder.isTypeSupported(type)) return type;
832
+ return "";
833
+ }
834
+ function getAdaptiveMediaRecorderOptions() {
835
+ const mimeType = getSupportedMediaRecorderMimeType();
836
+ if (isIOS()) return {
837
+ mimeType,
838
+ videoBitsPerSecond: 1e6,
839
+ bitsPerSecond: 1e6
840
+ };
841
+ return {
842
+ mimeType,
843
+ videoBitsPerSecond: 5e5,
844
+ bitsPerSecond: 5e5
845
+ };
846
+ }
847
+ var LocalRecordingProvider = class {
848
+ constructor() {
849
+ this.mediaRecorder = null;
850
+ this._isRecording = false;
851
+ this._hasError = false;
852
+ this._error = null;
853
+ this.mimeType = "";
854
+ this.stream = null;
855
+ this.pauseRecordingBound = this.pauseRecording.bind(this);
856
+ }
857
+ get isRecording() {
858
+ return this._isRecording;
859
+ }
860
+ get hasError() {
861
+ return this._hasError;
862
+ }
863
+ get error() {
864
+ return this._error;
865
+ }
866
+ startRecording(stream) {
867
+ this.reset();
868
+ this.registerEventListeners();
869
+ this.stream = stream;
870
+ try {
871
+ const options = getAdaptiveMediaRecorderOptions();
872
+ this.mimeType = options.mimeType;
873
+ const recorder = new MediaRecorder(stream.clone(), options);
874
+ recorder.onerror = (event) => {
875
+ this._error = `Recording error: ${event}`;
876
+ this._isRecording = false;
877
+ this._hasError = true;
878
+ };
879
+ recorder.start();
880
+ this.mediaRecorder = recorder;
881
+ this._isRecording = true;
882
+ this._error = null;
883
+ this._hasError = false;
884
+ } catch (err) {
885
+ this._error = `Failed to start recording: ${err}`;
886
+ this._hasError = true;
887
+ }
888
+ }
889
+ async stopRecording(trimSeconds, encrypt, generateChecksum) {
890
+ const recorder = this.mediaRecorder;
891
+ return new Promise((resolve, reject) => {
892
+ this.removeEventListeners();
893
+ if (recorder && this._isRecording) {
894
+ const chunks = [];
895
+ recorder.ondataavailable = (event) => {
896
+ if (event.data.size > 0) chunks.push(event.data);
897
+ };
898
+ recorder.onstop = async () => {
899
+ try {
900
+ const trimmedVideo = await trimLastNSeconds(new Blob(chunks, { type: this.mimeType }), trimSeconds);
901
+ const encryptedVideoBase64 = encrypt(await toBase64(trimmedVideo));
902
+ generateChecksum(await trimmedVideo.arrayBuffer());
903
+ this._isRecording = false;
904
+ resolve({
905
+ trimmedBlob: trimmedVideo,
906
+ encryptedVideo: encryptedVideoBase64
907
+ });
908
+ } catch (error) {
909
+ this._isRecording = false;
910
+ this._error = `Recording stop failed: ${error}`;
911
+ this._hasError = true;
912
+ reject(error);
913
+ }
914
+ };
915
+ recorder.stop();
916
+ this._isRecording = false;
917
+ } else resolve({
918
+ trimmedBlob: new Blob([], { type: this.mimeType }),
919
+ encryptedVideo: ""
920
+ });
921
+ recorder?.stream?.getTracks().forEach((track) => track.stop());
922
+ });
923
+ }
924
+ reset() {
925
+ this._isRecording = false;
926
+ this._error = null;
927
+ this._hasError = false;
928
+ }
929
+ pauseRecording() {
930
+ if (!this._isRecording) return;
931
+ if (this.mediaRecorder?.state === "recording") try {
932
+ this.mediaRecorder.pause();
933
+ } catch {}
934
+ }
935
+ registerEventListeners() {
936
+ document.addEventListener("visibilitychange", this.pauseRecordingBound);
937
+ }
938
+ removeEventListeners() {
939
+ document.removeEventListener("visibilitychange", this.pauseRecordingBound);
940
+ }
941
+ };
942
+
943
+ //#endregion
944
+ //#region src/internal/faceCapture/recordingService.ts
945
+ let openViduProviderImportPromise;
946
+ let openViduBrowserImportPromise;
947
+ function loadOpenViduProviderModule() {
948
+ if (!openViduProviderImportPromise) openViduProviderImportPromise = import("./OpenViduRecordingProvider-PvkEhhDq.esm.js");
949
+ return openViduProviderImportPromise;
950
+ }
951
+ function loadOpenViduBrowserModule() {
952
+ if (!openViduBrowserImportPromise) openViduBrowserImportPromise = (async () => {
953
+ return (await import("./openviduLazy-NTgGtkeK.esm.js")).loadOpenVidu();
954
+ })();
955
+ return openViduBrowserImportPromise;
956
+ }
957
+ function preloadOpenViduProvider() {
958
+ loadOpenViduProviderModule();
959
+ loadOpenViduBrowserModule();
960
+ }
961
+ /**
962
+ * Records on-device and uploads encrypted video for liveness.
963
+ */
964
+ var LocalRecordingService = class {
965
+ constructor(wasmUtil, sessionToken) {
966
+ this.recorder = new LocalRecordingProvider();
967
+ this.sessionToken = sessionToken;
968
+ this.wasmUtil = wasmUtil;
969
+ }
970
+ async start(stream) {
971
+ this.recorder.startRecording(stream);
972
+ }
973
+ async stop() {
974
+ if (!this.recorder.isRecording) return { recordingId: null };
975
+ const result = await this.recorder.stopRecording(10, (base64) => this.wasmUtil.encryptImage(base64), (buffer) => this.wasmUtil.ckvcks(buffer));
976
+ if (!this.sessionToken) return { recordingId: null };
977
+ return { recordingId: await uploadDeepsightVideo(result.encryptedVideo, this.sessionToken) };
978
+ }
979
+ cleanup() {
980
+ this.recorder.reset();
981
+ }
982
+ };
983
+ /**
984
+ * Records through OpenVidu and manages the session lifecycle.
985
+ */
986
+ var OpenViduRecordingService = class {
987
+ constructor(config) {
988
+ this.config = config;
989
+ }
990
+ async start(stream) {
991
+ if (!this.provider) {
992
+ const { OpenViduRecordingProvider } = await loadOpenViduProviderModule();
993
+ this.provider = new OpenViduRecordingProvider();
994
+ }
995
+ this.session = await startRecordingSession({
996
+ config: {
997
+ ...this.config,
998
+ recording: { capability: this.provider }
999
+ },
1000
+ clonedStream: stream.clone(),
1001
+ existing: this.session
1002
+ });
1003
+ }
1004
+ async stop() {
1005
+ if (this.session) {
1006
+ stopRecording(this.session);
1007
+ this.session = void 0;
1008
+ }
1009
+ return { recordingId: null };
1010
+ }
1011
+ cleanup() {
1012
+ if (this.session) stopRecording(this.session);
1013
+ }
1014
+ };
1015
+ /**
1016
+ * Creates the recording service for the current configuration.
1017
+ */
1018
+ function createRecordingService(params) {
1019
+ if (params.config.deepsightLiveness === "VIDEOLIVENESS") return new LocalRecordingService(params.wasmUtil, params.sessionToken);
1020
+ if (params.config.enableFaceRecording === true) return new OpenViduRecordingService(params.config);
1021
+ }
1022
+
1023
+ //#endregion
1024
+ export { StreamCanvasCapture as _, initializeDeepsightSession as a, stopStream as c, createRecordingSession as d, startRecording as f, flagIdManualReview as g, flagFaceManualReview as h, initializeCamera as i, processFace as l, StreamCanvasProcessingSession as m, preloadOpenViduProvider as n, sendLabelInspectionEvent as o, stopRecording$1 as p, encryptSelfieImage as r, startDetection as s, createRecordingService as t, uploadSelfie as u, FACE_ERROR_CODES as v, BrowserStorageProvider as y };