@incodetech/core 2.0.0-alpha.8 → 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/Actor-CI32dTbG.d.ts +2 -0
  2. package/dist/BaseWasmProvider-C_DLEI40.esm.js +1118 -0
  3. package/dist/BrowserStorageProvider-CuOW1Er2.esm.js +55 -0
  4. package/dist/BrowserTimerProvider-DhNc_x02.esm.js +22 -0
  5. package/dist/ITimerCapability-C67ZRskg.esm.js +7 -0
  6. package/dist/IpifyProvider-D7jx52AL.esm.js +139 -0
  7. package/dist/MotionSensorProvider-4v7xkqAp.esm.js +254 -0
  8. package/dist/OpenViduRecordingProvider-CMu6XVdc.esm.js +87 -0
  9. package/dist/StateMachine-BCQrZJhf.d.ts +2 -0
  10. package/dist/WasmUtilProvider-j98OJf-S.esm.js +114 -0
  11. package/dist/addressSearch-BpTbTWCa.esm.js +430 -0
  12. package/dist/ae-signature-DDDZmWXj.esm.js +12 -0
  13. package/dist/ae-signature.d.ts +25 -0
  14. package/dist/ae-signature.esm.js +8 -0
  15. package/dist/antifraud.d.ts +57 -0
  16. package/dist/antifraud.esm.js +45 -0
  17. package/dist/antifraudStateMachine-O0TMf6yc.esm.js +39 -0
  18. package/dist/api-CESGtpbH.esm.js +53 -0
  19. package/dist/authentication.d.ts +12 -0
  20. package/dist/authentication.esm.js +25 -0
  21. package/dist/authenticationManager-5M-fKzXx.esm.js +67 -0
  22. package/dist/authenticationManager-C83GNIhl.d.ts +66 -0
  23. package/dist/authenticationStateMachine-BMZqatiF.esm.js +139 -0
  24. package/dist/backCameraStream-DMdMeGk2.esm.js +346 -0
  25. package/dist/browserSimulation-gxD8cSpM.esm.js +20 -0
  26. package/dist/camera-DBSxa6ML.d.ts +4 -0
  27. package/dist/camera-PA2Ljri3.esm.js +22 -0
  28. package/dist/camera.d.ts +15 -0
  29. package/dist/camera.esm.js +5 -0
  30. package/dist/consent.d.ts +398 -0
  31. package/dist/consent.esm.js +79 -0
  32. package/dist/consentStateMachine-CCT-B60O.esm.js +151 -0
  33. package/dist/cpf-PPz2Njto.esm.js +38 -0
  34. package/dist/cpf-ocr.d.ts +204 -0
  35. package/dist/cpf-ocr.esm.js +177 -0
  36. package/dist/cross-document-data-match.d.ts +34 -0
  37. package/dist/cross-document-data-match.esm.js +71 -0
  38. package/dist/curp-validation.d.ts +188 -0
  39. package/dist/curp-validation.esm.js +110 -0
  40. package/dist/curpValidationStateMachine-CitWLr2c.esm.js +595 -0
  41. package/dist/custom-fields.d.ts +115 -0
  42. package/dist/custom-fields.esm.js +177 -0
  43. package/dist/custom-watchlist.d.ts +66 -0
  44. package/dist/custom-watchlist.esm.js +86 -0
  45. package/dist/dateUtils-UoN5xswP.esm.js +23 -0
  46. package/dist/deepsightLoader-Cm4JIT_z.esm.js +52 -0
  47. package/dist/deepsightService-CEVxzehb.d.ts +412 -0
  48. package/dist/deepsightService-O74l4Y__.esm.js +489 -0
  49. package/dist/device.d.ts +46 -0
  50. package/dist/device.esm.js +106 -0
  51. package/dist/displayErrors-DqJ_IbsG.d.ts +39 -0
  52. package/dist/document-capture.d.ts +906 -0
  53. package/dist/document-capture.esm.js +156 -0
  54. package/dist/document-upload.d.ts +331 -0
  55. package/dist/document-upload.esm.js +203 -0
  56. package/dist/documentCaptureStateMachine-BqzTDy9k.esm.js +394 -0
  57. package/dist/dynamic-forms.d.ts +178 -0
  58. package/dist/dynamic-forms.esm.js +323 -0
  59. package/dist/ekyb.d.ts +148 -0
  60. package/dist/ekyb.esm.js +127 -0
  61. package/dist/ekybStateMachine-B59rQjgj.esm.js +674 -0
  62. package/dist/ekyc.d.ts +164 -0
  63. package/dist/ekyc.esm.js +104 -0
  64. package/dist/ekycStateMachine-oeO0Iekd.esm.js +10626 -0
  65. package/dist/electronic-signature.d.ts +4 -0
  66. package/dist/electronic-signature.esm.js +7 -0
  67. package/dist/electronicSignatureManager-D9OHzTpG.esm.js +428 -0
  68. package/dist/email.d.ts +3 -263
  69. package/dist/email.esm.js +7 -477
  70. package/dist/emailManager-DIfnS5g1.d.ts +352 -0
  71. package/dist/emailManager-wAV0LE-H.esm.js +238 -0
  72. package/dist/emailStateMachine-DOf4j58N.esm.js +292 -0
  73. package/dist/endpoints-CnN3SyDa.esm.js +87 -0
  74. package/dist/events-D6-e4vok.esm.js +596 -0
  75. package/dist/events.d.ts +265 -0
  76. package/dist/events.esm.js +4 -0
  77. package/dist/extensibility.d.ts +122 -0
  78. package/dist/extensibility.esm.js +43 -0
  79. package/dist/face-match.d.ts +228 -0
  80. package/dist/face-match.esm.js +173 -0
  81. package/dist/faceCaptureManagerFactory-Dh2PdGlF.esm.js +290 -0
  82. package/dist/faceCaptureManagerFactory-yqtpxjnN.d.ts +690 -0
  83. package/dist/faceCaptureSetup-B3faSpYA.esm.js +873 -0
  84. package/dist/faceMatchStateMachine-DNFrxTFS.esm.js +127 -0
  85. package/dist/flow-events.d.ts +6 -0
  86. package/dist/flow-events.esm.js +0 -0
  87. package/dist/flow.d.ts +101 -321
  88. package/dist/flow.esm.js +370 -173
  89. package/dist/flowCompletionService-DhkT4SRY.d.ts +10 -0
  90. package/dist/flowCompletionService-P54yzGvA.esm.js +13 -0
  91. package/dist/flowServices-DTsm-Vf1.esm.js +188 -0
  92. package/dist/geolocation.d.ts +127 -0
  93. package/dist/geolocation.esm.js +89 -0
  94. package/dist/geolocationStateMachine-asasuHY2.esm.js +105 -0
  95. package/dist/getBrowser-BSXUTWXw.esm.js +41 -0
  96. package/dist/getDeviceClass-BSntT9_j.esm.js +14 -0
  97. package/dist/government-validation.d.ts +67 -0
  98. package/dist/government-validation.esm.js +81 -0
  99. package/dist/governmentValidationStateMachine-BDDYrJTo.esm.js +271 -0
  100. package/dist/home.d.ts +99 -0
  101. package/dist/home.esm.js +61 -0
  102. package/dist/http.d.ts +68 -0
  103. package/dist/http.esm.js +3 -0
  104. package/dist/id-ocr.d.ts +635 -0
  105. package/dist/id-ocr.esm.js +86 -0
  106. package/dist/id-verification.d.ts +190 -0
  107. package/dist/id-verification.esm.js +43 -0
  108. package/dist/id.d.ts +24 -0
  109. package/dist/id.esm.js +164 -0
  110. package/dist/idCaptureManager-B9TGA5dq.d.ts +956 -0
  111. package/dist/idCaptureManager-DMK0GIt3.esm.js +581 -0
  112. package/dist/idCaptureStateMachine-Bq0fVZXl.esm.js +2954 -0
  113. package/dist/idOcrStateMachine-YbjjC_Gg.esm.js +388 -0
  114. package/dist/idVerificationStateMachine-xbw9HP1Z.esm.js +71 -0
  115. package/dist/identity-reuse.d.ts +530 -0
  116. package/dist/identity-reuse.esm.js +274 -0
  117. package/dist/index-BLKtMA0g.d.ts +1177 -0
  118. package/dist/index-BcRG8rtJ.d.ts +97 -0
  119. package/dist/index.d.ts +3 -226
  120. package/dist/index.esm.js +11 -154
  121. package/dist/invokeOnCaptureCallback-rc6kBHo5.esm.js +30 -0
  122. package/dist/{lib-Bu9XGMBW.esm.js → lib-BB0B_qQX.esm.js} +801 -2
  123. package/dist/mandatory-consent.d.ts +412 -0
  124. package/dist/mandatory-consent.esm.js +78 -0
  125. package/dist/mandatoryConsentStateMachine-Cnco1jvn.esm.js +126 -0
  126. package/dist/openviduLazy-Cm0XFh_v.esm.js +3 -0
  127. package/dist/openviduLazy-Cok70ZSg.esm.js +12 -0
  128. package/dist/permissionServices-D_i6nzEw.esm.js +50 -0
  129. package/dist/phone.d.ts +3 -291
  130. package/dist/phone.esm.js +7 -548
  131. package/dist/phoneManager-B6M30hKE.d.ts +397 -0
  132. package/dist/phoneManager-DAJbGhlY.esm.js +256 -0
  133. package/dist/phoneStateMachine-CuPARRaT.esm.js +351 -0
  134. package/dist/platform-CfrjKhmi.esm.js +83 -0
  135. package/dist/qe-signature-DFo_Cc-I.esm.js +12 -0
  136. package/dist/qe-signature.d.ts +25 -0
  137. package/dist/qe-signature.esm.js +8 -0
  138. package/dist/recordingService-Ig2UgbLv.esm.js +1003 -0
  139. package/dist/redirect-to-mobile.d.ts +107 -0
  140. package/dist/redirect-to-mobile.esm.js +102 -0
  141. package/dist/redirectToMobileStateMachine-BOEqe46A.esm.js +249 -0
  142. package/dist/runChildModule-CqqwqAkW.esm.js +219 -0
  143. package/dist/selfie.d.ts +21 -754
  144. package/dist/selfie.esm.js +113 -962
  145. package/dist/selfieManager-D0lSgd-J.d.ts +68 -0
  146. package/dist/selfieManager-Duisl7qN.esm.js +60 -0
  147. package/dist/selfieStateMachine-D76whWEf.esm.js +68 -0
  148. package/dist/session-BS-d_vuE.esm.js +3206 -0
  149. package/dist/session.d.ts +217 -0
  150. package/dist/session.esm.js +9 -0
  151. package/dist/setup-Buy-hyj4.esm.js +887 -0
  152. package/dist/setup-C5AITV8m.d.ts +254 -0
  153. package/dist/signature.d.ts +94 -0
  154. package/dist/signature.esm.js +66 -0
  155. package/dist/signatureStateMachine-B5-QVUve.esm.js +132 -0
  156. package/dist/stats-CIfiPzb1.esm.js +16 -0
  157. package/dist/stats.d.ts +16 -0
  158. package/dist/stats.esm.js +4 -0
  159. package/dist/trust-graph.d.ts +54 -0
  160. package/dist/trust-graph.esm.js +56 -0
  161. package/dist/types-B06Ypu2F.d.ts +49 -0
  162. package/dist/types-BP1m8VRw.d.ts +340 -0
  163. package/dist/types-CFV9G_7j.d.ts +24 -0
  164. package/dist/{warmup-CEJTfxQr.d.ts → warmup-CEcppFiS.d.ts} +11 -3
  165. package/dist/wasm.d.ts +15 -0
  166. package/dist/wasm.esm.js +12 -0
  167. package/dist/watchlist-for-business.d.ts +79 -0
  168. package/dist/watchlist-for-business.esm.js +148 -0
  169. package/dist/watchlist.d.ts +62 -0
  170. package/dist/watchlist.esm.js +86 -0
  171. package/dist/watchlistServices-DMbUhkBX.esm.js +12 -0
  172. package/dist/workflow.d.ts +907 -0
  173. package/dist/workflow.esm.js +702 -0
  174. package/dist/{xstate.esm-B_rda9yU.esm.js → xstate.esm-B70JrNqo.esm.js} +144 -1
  175. package/package.json +203 -6
  176. package/dist/OpenViduLogger-BdPfiZO6.esm.js +0 -3
  177. package/dist/OpenViduLogger-CQyDxBvM.esm.js +0 -803
  178. package/dist/StateMachine-DRE1oH2B.d.ts +0 -2
  179. package/dist/addEvent-W0ORK0jT.esm.js +0 -16
  180. package/dist/endpoints-BSTFaHYo.esm.js +0 -1706
  181. package/dist/permissionServices-I6vX6DBy.esm.js +0 -72
  182. /package/dist/{Manager-BGfxEmyv.d.ts → Manager-C8PrhBOx.d.ts} +0 -0
  183. /package/dist/{chunk-C_Yo44FK.esm.js → chunk-CRF6K_H_.esm.js} +0 -0
  184. /package/dist/{types-iZi2rawo.d.ts → types-CAD4va6a.d.ts} +0 -0
@@ -1,995 +1,146 @@
1
- import { n as requestPermission, r as getDeviceClass, t as checkPermission } from "./permissionServices-I6vX6DBy.esm.js";
2
- import { c as FaceDetectionProvider, d as StreamCanvasProcessingSession, f as StreamCanvasCapture, g as createManager, h as stopCameraStream, m as requestCameraAccess, n as api, s as WasmUtilProvider, t as endpoints, u as OpenViduRecordingProvider } from "./endpoints-BSTFaHYo.esm.js";
3
- import { a as createActor, i as fromPromise, n as assign, r as fromCallback, t as setup } from "./xstate.esm-B_rda9yU.esm.js";
4
- import { t as addEvent } from "./addEvent-W0ORK0jT.esm.js";
1
+ import "./BaseWasmProvider-C_DLEI40.esm.js";
2
+ import { t as WasmUtilProvider } from "./WasmUtilProvider-j98OJf-S.esm.js";
3
+ import "./api-CESGtpbH.esm.js";
4
+ import { n as eventModuleNames } from "./events-D6-e4vok.esm.js";
5
+ import "./endpoints-CnN3SyDa.esm.js";
6
+ import "./browserSimulation-gxD8cSpM.esm.js";
7
+ import { a as fromPromise, r as assign, s as createActor } from "./xstate.esm-B70JrNqo.esm.js";
8
+ import { t as createFaceCaptureManagerFromActor } from "./faceCaptureManagerFactory-Dh2PdGlF.esm.js";
9
+ import { t as BrowserStorageProvider } from "./BrowserStorageProvider-CuOW1Er2.esm.js";
10
+ import { t as faceCaptureMachine } from "./faceCaptureSetup-B3faSpYA.esm.js";
11
+ import "./ITimerCapability-C67ZRskg.esm.js";
12
+ import "./camera-PA2Ljri3.esm.js";
13
+ import "./deepsightService-O74l4Y__.esm.js";
14
+ import { f as processFace } from "./recordingService-Ig2UgbLv.esm.js";
15
+ import "./platform-CfrjKhmi.esm.js";
16
+ import "./backCameraStream-DMdMeGk2.esm.js";
17
+ import "./getBrowser-BSXUTWXw.esm.js";
18
+ import "./stats-CIfiPzb1.esm.js";
19
+ import "./getDeviceClass-BSntT9_j.esm.js";
20
+ import "./MotionSensorProvider-4v7xkqAp.esm.js";
21
+ import "./permissionServices-D_i6nzEw.esm.js";
22
+ import { t as selfieMachine } from "./selfieStateMachine-D76whWEf.esm.js";
23
+ import { t as createSelfieManager } from "./selfieManager-Duisl7qN.esm.js";
24
+ import { t as invokeOnCaptureCallback } from "./invokeOnCaptureCallback-rc6kBHo5.esm.js";
5
25
 
6
- //#region src/modules/selfie/types.ts
7
- const FACE_ERROR_CODES = {
8
- FACE_OCCLUDED: "FACE_OCCLUDED",
9
- LIVENESS: "LIVENESS_ERROR",
10
- BRIGHTNESS: "BRIGHTNESS_ERROR",
11
- LENSES: "LENSES_ERROR",
12
- MASK: "MASK_ERROR",
13
- CLOSED_EYES: "CLOSED_EYES_ERROR",
14
- HEAD_COVER: "HEAD_COVER_ERROR",
15
- SERVER: "SERVER_ERROR",
16
- FACE_NOT_FOUND: "FACE_NOT_FOUND",
17
- MULTIPLE_FACES: "MULTIPLE_FACES",
18
- TOO_BLURRY: "TOO_BLURRY_ERROR",
19
- TOO_DARK: "TOO_DARK_ERROR",
20
- USER_IS_NOT_RECOGNIZED: "USER_IS_NOT_RECOGNIZED",
21
- SPOOF_ATTEMPT_DETECTED: "SPOOF_ATTEMPT_DETECTED",
22
- FACE_TOO_DARK: "FACE_TOO_DARK",
23
- LENSES_DETECTED: "LENSES_DETECTED",
24
- FACE_MASK_DETECTED: "FACE_MASK_DETECTED",
25
- CLOSED_EYES_DETECTED: "CLOSED_EYES_DETECTED",
26
- HEAD_COVER_DETECTED: "HEAD_COVER_DETECTED",
27
- FACE_CROPPING_FAILED: "FACE_CROPPING_FAILED",
28
- FACE_TOO_SMALL: "FACE_TOO_SMALL",
29
- FACE_TOO_BLURRY: "FACE_TOO_BLURRY",
30
- BAD_PHOTO_QUALITY: "BAD_PHOTO_QUALITY",
31
- PROCESSING_ERROR: "PROCESSING_ERROR",
32
- BAD_REQUEST: "BAD_REQUEST",
33
- NONEXISTENT_CUSTOMER: "NONEXISTENT_CUSTOMER",
34
- HINT_NOT_PROVIDED: "HINT_NOT_PROVIDED",
35
- SELFIE_IMAGE_LOW_QUALITY: "SELFIE_IMAGE_LOW_QUALITY"
36
- };
37
-
38
- //#endregion
39
- //#region src/modules/selfie/selfieErrorUtils.ts
40
- const FACE_ERROR_CODE_VALUES = Object.values(FACE_ERROR_CODES);
41
- const isFaceErrorCode = (value) => {
42
- return FACE_ERROR_CODE_VALUES.includes(value);
43
- };
44
- const getFaceErrorCodeFromUnknown = (value) => {
45
- if (value instanceof Error && typeof value.message === "string") return isFaceErrorCode(value.message) ? value.message : void 0;
46
- if (typeof value === "string") return isFaceErrorCode(value) ? value : void 0;
47
- };
48
- const validateUploadResponse = ({ config, response }) => {
49
- if (!response) return FACE_ERROR_CODES.SERVER;
50
- if (response.confidence !== 0) return FACE_ERROR_CODES.LIVENESS;
51
- if (config.validateBrightness && !response.isBright) return FACE_ERROR_CODES.BRIGHTNESS;
52
- if (config.validateLenses && response.hasLenses) return FACE_ERROR_CODES.LENSES;
53
- if (config.validateFaceMask && response.hasFaceMask) return FACE_ERROR_CODES.MASK;
54
- if (config.validateClosedEyes && response.hasClosedEyes) return FACE_ERROR_CODES.CLOSED_EYES;
55
- if (config.validateHeadCover && response.hasHeadCover) return FACE_ERROR_CODES.HEAD_COVER;
56
- };
57
-
58
- //#endregion
59
- //#region src/internal/recordings/recordingsRepository.ts
60
- async function createRecordingSession(type) {
61
- return (await api.post(endpoints.recordingCreateSessionV2, { type })).data;
62
- }
63
- async function startRecording(params) {
64
- return (await api.post(endpoints.recordingStartV2, {
65
- videoRecordingId: params.videoRecordingId,
66
- frameRate: 30,
67
- outputMode: "COMPOSED",
68
- resolution: params.resolution,
69
- type: params.type,
70
- hasAudio: params.hasAudio ?? false
71
- })).data;
72
- }
73
- async function stopRecording$1(videoRecordingId) {
74
- return (await api.post(endpoints.recordingStopV2, { videoRecordingId })).data;
75
- }
76
-
77
- //#endregion
78
- //#region src/internal/recordings/streamingEvents.ts
79
- const streamingEvents = {
80
- strSessionDidConnect: "strSessionDidConnect",
81
- strSessionDidDisconnect: "strSessionDidDisconnect",
82
- strSessionDidFailWithError: "strSessionDidFailWithError",
83
- strStreamVideoCaptureStart: "strStreamVideoCaptureStart",
84
- strStreamVideoCaptureStop: "strStreamVideoCaptureStop",
85
- strStreamPublisherCreated: "strStreamPublisherCreated",
86
- strStreamPublisherDestroyed: "strStreamPublisherDestroyed",
87
- strStreamPublisherDidFailWithError: "strStreamPublisherDidFailWithError"
88
- };
89
-
90
- //#endregion
91
- //#region src/modules/selfie/selfieUploadService.ts
26
+ //#region src/modules/selfie/selfieCaptureOnlyStateMachine.ts
92
27
  /**
93
- * Uploads a selfie image to the backend.
28
+ * Performs local finalization of a captured face frame: stops local video
29
+ * recording (if any), runs deepsight metadata extraction, and assembles the
30
+ * rich payload delivered to the integrator's `onCapture` callback.
94
31
  */
95
- async function uploadSelfie({ encryptedBase64Image, faceCoordinates, signal }) {
32
+ async function finalizeFaceCaptureOnly(ctx, signal) {
33
+ const canvas = ctx.capturedImage;
34
+ if (!canvas) throw new Error("selfieCaptureOnly: no captured image");
35
+ let videoBase64;
96
36
  try {
97
- const res = await api.post(endpoints.selfie, {
98
- base64Image: encryptedBase64Image,
99
- faceCoordinates: faceCoordinates ?? void 0,
100
- encrypted: true,
101
- clientInfo: { deviceClass: getDeviceClass() }
102
- }, {
103
- signal,
104
- query: { imageType: "selfie" }
105
- });
106
- if (!res.ok) throw new Error(`POST ${endpoints.selfie} failed: ${res.status} ${res.statusText}`);
107
- return res.data;
108
- } catch (error) {
109
- const errorCode = getFaceErrorCodeFromHttpError(error);
110
- if (errorCode) throw new Error(errorCode);
111
- throw new Error(FACE_ERROR_CODES.SERVER);
37
+ videoBase64 = (await ctx.recordingService?.stopAndGetVideo())?.videoBase64;
38
+ } catch {
39
+ videoBase64 = void 0;
112
40
  }
113
- }
114
- const getFaceErrorCodeFromHttpError = (error) => {
115
- const err = error;
116
- if (err.ok !== false || typeof err.status !== "number") return;
117
- if (err.status !== 400) return FACE_ERROR_CODES.SERVER;
118
- if (typeof err.data?.status !== "number") return FACE_ERROR_CODES.BAD_REQUEST;
119
- return {
120
- 3004: FACE_ERROR_CODES.FACE_NOT_FOUND,
121
- 3005: FACE_ERROR_CODES.FACE_NOT_FOUND,
122
- 3006: FACE_ERROR_CODES.TOO_BLURRY,
123
- 3007: FACE_ERROR_CODES.TOO_DARK,
124
- 4010: FACE_ERROR_CODES.MULTIPLE_FACES,
125
- 4019: FACE_ERROR_CODES.FACE_NOT_FOUND,
126
- 4077: FACE_ERROR_CODES.BAD_PHOTO_QUALITY,
127
- 4078: FACE_ERROR_CODES.FACE_OCCLUDED
128
- }[err.data.status] ?? FACE_ERROR_CODES.BAD_REQUEST;
129
- };
130
-
131
- //#endregion
132
- //#region src/modules/selfie/selfieServices.ts
133
- const CAMERA_CONSTRAINTS = {
134
- video: {
135
- facingMode: "user",
136
- height: { ideal: 480 },
137
- width: { ideal: 640 }
138
- },
139
- audio: false
140
- };
141
- function stopStream(stream) {
142
- stopCameraStream(stream);
143
- }
144
- async function initializeCamera(config) {
145
- const provider = new FaceDetectionProvider();
146
- await provider.initialize({ autocaptureInterval: config.autoCaptureTimeout * 1e3 });
147
- provider.setChecksEnabled({
148
- lenses: config.validateLenses,
149
- mask: config.validateFaceMask,
150
- closedEyes: config.validateClosedEyes,
151
- headWear: config.validateHeadCover,
152
- occlusion: false
153
- });
154
- return {
155
- stream: await requestCameraAccess({
156
- video: CAMERA_CONSTRAINTS.video,
157
- audio: false
158
- }),
159
- provider
160
- };
161
- }
162
- /**
163
- * Encrypts the provided selfie image using WASM.
164
- */
165
- async function encryptSelfieImage({ canvas }) {
166
- const base64Image = canvas.getBase64Image();
167
- if (!base64Image) throw new Error("Canvas image is empty or null");
168
- return (await WasmUtilProvider.getInstance()).encryptImage(base64Image);
169
- }
170
- function startDetection(params) {
171
- let lastStatus;
172
- let session;
173
- const { provider } = params;
174
- const setStatus = (status) => {
175
- if (session?.isDisposed() === true) return;
176
- if (lastStatus === status) return;
177
- lastStatus = status;
178
- params.onUpdate(status);
179
- };
180
- const stopDetectionLoop = () => {
181
- session?.dispose();
182
- };
183
- const reset = () => {
184
- provider.reset();
185
- };
186
- const cleanup = () => {
187
- stopDetectionLoop();
188
- };
189
- (async () => {
190
- try {
191
- provider.setCallbacks({
192
- onFarAway: () => setStatus("tooFar"),
193
- onTooClose: () => setStatus("tooClose"),
194
- onTooManyFaces: () => setStatus("tooManyFaces"),
195
- onNoFace: () => setStatus("idle"),
196
- onCenterFace: () => setStatus("centerFace"),
197
- onGetReady: () => setStatus("getReady"),
198
- onGetReadyFinished: () => setStatus("getReadyFinished"),
199
- onDark: () => {
200
- if (params.config.validateBrightness) setStatus("dark");
201
- },
202
- onBlur: () => setStatus("blur"),
203
- onFaceAngle: () => setStatus("faceAngle"),
204
- onLenses: () => {
205
- if (params.config.validateLenses) setStatus("lenses");
206
- },
207
- onMask: () => {
208
- if (params.config.validateFaceMask) setStatus("faceMask");
209
- },
210
- onEyesClosed: () => {
211
- if (params.config.validateClosedEyes) setStatus("eyesClosed");
212
- },
213
- onHeadWear: () => {
214
- if (params.config.validateHeadCover) setStatus("headWear");
215
- },
216
- onFaceOccluded: () => {},
217
- onSwitchToManualCapture: () => {
218
- setStatus("manualCapture");
219
- stopDetectionLoop();
220
- },
221
- onCapture: (canvas, faceCoordinates) => {
222
- setStatus("success");
223
- params.onSuccess(canvas, faceCoordinates);
224
- cleanup();
225
- }
226
- });
227
- setStatus("detecting");
228
- session = new StreamCanvasProcessingSession({
229
- capturer: params.capturer,
230
- provider,
231
- onFrame: params.onFrame
232
- });
233
- } catch {
234
- setStatus("error");
235
- cleanup();
236
- }
237
- })();
238
- return {
239
- cleanup,
240
- reset
241
- };
242
- }
243
- function buildResolutionFromStream(stream) {
244
- const track = stream.getVideoTracks()[0];
245
- if (!track) return;
246
- const settings = track.getSettings();
247
- const width = settings.width;
248
- const height = settings.height;
249
- if (typeof width === "number" && typeof height === "number") return `${width}x${height}`;
250
- }
251
- async function startRecordingSession(params) {
252
- if (params.config.enableFaceRecording !== true) return;
253
- if (params.existing) return params.existing;
254
- const provider = params.config.recording?.capability ?? new OpenViduRecordingProvider();
255
- const clonedStream = params.clonedStream;
256
- const hasAudio = clonedStream.getAudioTracks().length > 0;
257
- const resolution = buildResolutionFromStream(clonedStream);
258
- const session = await createRecordingSession("selfie");
259
- const connection = await provider.connect({
260
- sessionToken: session.token,
261
- stream: clonedStream,
262
- events: {
263
- onSessionConnected: (sessionId) => {
264
- addEvent({
265
- code: streamingEvents.strSessionDidConnect,
266
- payload: {
267
- message: "Recording session connected",
268
- sessionId
269
- }
270
- });
271
- },
272
- onSessionDisconnected: (sessionId) => {
273
- addEvent({
274
- code: streamingEvents.strSessionDidDisconnect,
275
- payload: {
276
- message: "Recording session disconnected",
277
- sessionId
278
- }
279
- });
280
- },
281
- onSessionException: (event) => {
282
- addEvent({
283
- code: streamingEvents.strSessionDidFailWithError,
284
- payload: {
285
- message: "Recording session failed due to an error",
286
- eventName: event.name,
287
- type: "OpenViduException",
288
- errorMessage: event.message,
289
- sessionId: event.sessionId
290
- }
291
- });
292
- },
293
- onPublisherCreated: (p) => {
294
- addEvent({
295
- code: streamingEvents.strStreamPublisherCreated,
296
- payload: {
297
- message: "Recording publisher created",
298
- sessionId: p.sessionId,
299
- streamId: p.streamId
300
- }
301
- });
302
- },
303
- onPublisherError: (p) => {
304
- addEvent({
305
- code: streamingEvents.strStreamPublisherDidFailWithError,
306
- payload: {
307
- message: "Recording publisher failed due to an error",
308
- sessionId: p.sessionId,
309
- streamId: p.streamId,
310
- error: { message: p.message ?? "Unknown error" }
311
- }
312
- });
313
- }
314
- }
315
- });
316
- await startRecording({
317
- videoRecordingId: session.videoRecordingId,
318
- type: "selfie",
319
- resolution,
320
- hasAudio
321
- });
322
- addEvent({
323
- code: streamingEvents.strStreamVideoCaptureStart,
324
- payload: {
325
- message: "Recording capture started",
326
- resolution,
327
- videoRecordingId: session.videoRecordingId,
328
- sessionId: session.sessionId,
329
- streamId: connection.publisher.getStreamId()
41
+ if (signal.aborted) throw new DOMException("Aborted", "AbortError");
42
+ let metadata = "";
43
+ try {
44
+ if (ctx.deepsightService) {
45
+ const imageData = canvas.getImageData();
46
+ if (imageData) await ctx.deepsightService.analyzeFrame(imageData);
47
+ if (signal.aborted) throw new DOMException("Aborted", "AbortError");
48
+ metadata = ctx.deepsightService.getMetadata();
330
49
  }
331
- });
50
+ } catch (error) {
51
+ if (error?.name === "AbortError") throw error;
52
+ metadata = "";
53
+ }
54
+ const imageBase64 = canvas.getBase64Image() ?? "";
55
+ const blobData = canvas.getBlobData();
56
+ if (!blobData) throw new Error("selfieCaptureOnly: failed to build image blob");
332
57
  return {
333
- token: session.token,
334
- sessionId: session.sessionId,
335
- videoRecordingId: session.videoRecordingId,
336
- connection,
337
- resolution,
338
- hasAudio
58
+ imageBase64,
59
+ blob: blobData.blob,
60
+ url: blobData.url,
61
+ metadata,
62
+ videoBase64
339
63
  };
340
64
  }
341
- function stopRecording(session) {
342
- (async () => {
343
- try {
344
- addEvent({
345
- code: streamingEvents.strStreamVideoCaptureStop,
346
- payload: {
347
- message: "Recording capture stopped",
348
- videoRecordingId: session.videoRecordingId,
349
- sessionId: session.sessionId,
350
- streamId: session.connection.publisher.getStreamId()
351
- }
352
- });
353
- await stopRecording$1(session.videoRecordingId);
354
- addEvent({
355
- code: streamingEvents.strStreamPublisherDestroyed,
356
- payload: {
357
- message: "Recording publisher destroyed",
358
- sessionId: session.sessionId,
359
- streamId: session.connection.publisher.getStreamId()
360
- }
361
- });
362
- } finally {
363
- await session.connection.disconnect();
364
- addEvent({
365
- code: streamingEvents.strSessionDidDisconnect,
366
- payload: {
367
- message: "Recording session disconnected",
368
- sessionId: session.sessionId
369
- }
370
- });
371
- }
372
- })();
373
- }
374
-
375
- //#endregion
376
- //#region src/modules/selfie/selfieStateMachine.ts
377
- const _selfieMachine = setup({
378
- types: {
379
- context: {},
380
- events: {},
381
- input: {}
382
- },
65
+ const _selfieCaptureOnlyMachine = faceCaptureMachine.provide({
383
66
  actors: {
384
- checkPermission: fromPromise(async () => {
385
- return checkPermission();
386
- }),
387
- requestPermission: fromPromise(async () => {
388
- return requestPermission();
389
- }),
390
- initializeCamera: fromPromise(async ({ input }) => {
391
- return initializeCamera(input);
392
- }),
393
- runDetection: fromCallback(({ input, sendBack }) => {
394
- if (!input.frameCapturer || !input.provider) {
395
- sendBack({
396
- type: "DETECTION_UPDATE",
397
- status: "error"
398
- });
399
- return () => {};
400
- }
401
- const { cleanup, reset } = startDetection({
402
- config: input.config,
403
- capturer: input.frameCapturer,
404
- onUpdate: (status) => sendBack({
405
- type: "DETECTION_UPDATE",
406
- status
407
- }),
408
- onFrame: (frame) => sendBack({
409
- type: "DETECTION_FRAME",
410
- frame
411
- }),
412
- onSuccess: (canvas, faceCoordinates) => sendBack({
413
- type: "DETECTION_SUCCESS",
414
- canvas,
415
- faceCoordinates
416
- }),
417
- provider: input.provider
418
- });
419
- sendBack({
420
- type: "DETECTION_RESET_READY",
421
- reset
422
- });
423
- return cleanup;
424
- }),
425
- uploadSelfie: fromPromise(async ({ input, signal }) => {
426
- return uploadSelfie({
427
- encryptedBase64Image: await encryptSelfieImage({ canvas: input.canvas }),
428
- faceCoordinates: input.faceCoordinates,
429
- signal
430
- });
431
- }),
432
- startRecording: fromPromise(async ({ input }) => {
433
- if (!input.clonedStream) return;
434
- return startRecordingSession({
435
- config: input.config,
436
- clonedStream: input.clonedStream,
437
- existing: input.existing
438
- });
439
- })
440
- },
441
- actions: {
442
- stopMediaStream: assign(({ context }) => {
443
- context.frameCapturer?.dispose();
444
- if (context.stream) stopStream(context.stream);
445
- context.provider?.dispose();
446
- return {
447
- stream: void 0,
448
- provider: void 0,
449
- frameCapturer: void 0
450
- };
451
- }),
452
- setStreamAndCapturer: assign({
453
- stream: ({ event }) => {
454
- if ("output" in event) return event.output.stream;
455
- },
456
- provider: ({ event }) => {
457
- if ("output" in event) return event.output.provider;
458
- },
459
- frameCapturer: ({ event }) => {
460
- if ("output" in event) return new StreamCanvasCapture(event.output.stream);
461
- }
462
- }),
463
- trackTutorialSelfie: () => {
464
- addEvent({
465
- code: "tutorialSelfie",
466
- payload: { tutorialSelfie: true }
467
- });
468
- },
469
- trackContinue: () => {
470
- addEvent({ code: "continue" });
471
- },
472
- resetContext: assign(({ context }) => ({
473
- stream: void 0,
474
- provider: void 0,
475
- frameCapturer: void 0,
476
- error: void 0,
477
- detectionStatus: "idle",
478
- debugFrame: void 0,
479
- capturedImage: void 0,
480
- faceCoordinates: void 0,
481
- uploadResponse: void 0,
482
- recordingSession: void 0,
483
- recordingStream: void 0,
484
- attemptsRemaining: context.config.captureAttempts,
485
- uploadError: void 0,
486
- permissionResult: void 0,
487
- resetDetection: void 0
67
+ prepareFaceUpload: fromPromise(async () => ({
68
+ encryptedBase64Image: "",
69
+ recordingId: null
488
70
  })),
489
- resetDetection: ({ context }) => {
490
- context.resetDetection?.();
491
- },
492
- captureImage: assign({ capturedImage: ({ context }) => {
493
- if (context.capturedImage) return context.capturedImage;
494
- return context.frameCapturer?.getLatestCanvas() ?? void 0;
495
- } }),
496
- captureLatestFrame: assign({ capturedImage: ({ context }) => {
497
- return context.frameCapturer?.getLatestCanvas() ?? void 0;
498
- } }),
499
- clearUploadFailure: assign({
500
- uploadError: () => void 0,
501
- detectionStatus: () => "idle",
502
- capturedImage: () => void 0
503
- }),
504
- decrementAttemptsRemaining: assign(({ context }) => ({ attemptsRemaining: context.attemptsRemaining - 1 })),
505
- setUploadErrorFromUploadValidation: assign({ uploadError: ({ context }) => validateUploadResponse({
506
- config: context.config,
507
- response: context.uploadResponse
508
- }) ?? FACE_ERROR_CODES.SERVER }),
509
- stopMediaRecording: ({ context }) => {
510
- if (context.recordingSession) stopRecording(context.recordingSession);
511
- },
512
- clearRecordingSession: assign({
513
- recordingSession: () => void 0,
514
- recordingStream: () => void 0
71
+ uploadFace: fromPromise(async ({ input, signal }) => {
72
+ return await finalizeFaceCaptureOnly(input, signal);
515
73
  }),
516
- createRecordingStream: assign({ recordingStream: ({ context }) => {
517
- if (!context.config.enableFaceRecording) return;
518
- if (context.recordingStream) return context.recordingStream;
519
- return context.stream?.clone();
520
- } }),
521
- stopRecordingStream: ({ context }) => {
522
- if (context.recordingStream) stopStream(context.recordingStream);
523
- }
74
+ processFace: fromPromise(async () => ({}))
524
75
  },
76
+ actions: { setUploadResponseFromEvent: assign({ captureOnlyResult: ({ event }) => event.output }) },
525
77
  guards: {
526
- hasShowTutorial: ({ context }) => context.config.showTutorial,
527
- isPermissionGranted: ({ event }) => {
528
- if ("output" in event) return event.output === "granted";
529
- return false;
530
- },
531
- isPermissionDeniedError: ({ event }) => {
532
- if ("error" in event) {
533
- const error = event.error;
534
- return error?.name === "NotAllowedError" || error?.name === "PermissionDeniedError";
535
- }
536
- return false;
537
- },
538
- hasStream: ({ context }) => context.stream !== void 0,
539
- hasAttemptsRemaining: ({ context }) => context.attemptsRemaining > 0,
540
- hasCapturedImage: ({ context }) => context.capturedImage !== void 0,
541
- hasUploadValidationError: ({ context }) => validateUploadResponse({
542
- config: context.config,
543
- response: context.uploadResponse
544
- }) !== void 0
545
- }
546
- }).createMachine({
547
- id: "selfie",
548
- initial: "idle",
549
- context: ({ input }) => ({
550
- config: input.config,
551
- stream: void 0,
552
- provider: void 0,
553
- frameCapturer: void 0,
554
- error: void 0,
555
- detectionStatus: "idle",
556
- debugFrame: void 0,
557
- capturedImage: void 0,
558
- faceCoordinates: void 0,
559
- uploadResponse: void 0,
560
- recordingSession: void 0,
561
- recordingStream: void 0,
562
- attemptsRemaining: input.config.captureAttempts,
563
- uploadError: void 0,
564
- permissionResult: void 0,
565
- resetDetection: void 0
566
- }),
567
- on: { QUIT: { target: "#selfie.closed" } },
568
- states: {
569
- idle: { on: { LOAD: [{
570
- target: "tutorial",
571
- guard: "hasShowTutorial"
572
- }, { target: "loading" }] } },
573
- loading: { invoke: {
574
- id: "checkPermissionLoading",
575
- src: "checkPermission",
576
- onDone: [{
577
- target: "capture",
578
- guard: "isPermissionGranted",
579
- actions: assign({ permissionResult: ({ event }) => event.output })
580
- }, {
581
- target: "permissions",
582
- actions: assign({ permissionResult: ({ event }) => event.output })
583
- }]
584
- } },
585
- tutorial: {
586
- initial: "checkingPermission",
587
- entry: "trackTutorialSelfie",
588
- states: {
589
- checkingPermission: { invoke: {
590
- id: "checkPermissionTutorial",
591
- src: "checkPermission",
592
- onDone: [{
593
- target: "initializingCamera",
594
- guard: "isPermissionGranted",
595
- actions: assign({ permissionResult: ({ event }) => event.output })
596
- }, {
597
- target: "ready",
598
- actions: assign({ permissionResult: ({ event }) => event.output })
599
- }]
600
- } },
601
- initializingCamera: {
602
- initial: "booting",
603
- invoke: {
604
- id: "tutorialInitCamera",
605
- src: "initializeCamera",
606
- input: ({ context }) => context.config,
607
- onDone: { actions: "setStreamAndCapturer" },
608
- onError: [{
609
- target: "ready",
610
- guard: "isPermissionDeniedError",
611
- actions: assign({ permissionResult: () => "denied" })
612
- }, {
613
- target: "ready",
614
- actions: assign({ error: ({ event }) => String(event.error) })
615
- }]
616
- },
617
- states: {
618
- booting: {
619
- always: [{
620
- target: "#tutorialCameraReady",
621
- guard: "hasStream"
622
- }],
623
- on: { NEXT_STEP: {
624
- target: "waitingForStream",
625
- actions: "trackContinue"
626
- } }
627
- },
628
- waitingForStream: { always: [{
629
- target: "#selfie.capture",
630
- guard: "hasStream"
631
- }] }
632
- }
633
- },
634
- cameraReady: {
635
- id: "tutorialCameraReady",
636
- on: { NEXT_STEP: {
637
- target: "#selfie.capture",
638
- actions: "trackContinue"
639
- } }
640
- },
641
- ready: { on: { NEXT_STEP: {
642
- target: "waitingForPermission",
643
- actions: "trackContinue"
644
- } } },
645
- waitingForPermission: { invoke: {
646
- id: "checkPermissionWaiting",
647
- src: "checkPermission",
648
- onDone: [{
649
- target: "#selfie.capture",
650
- guard: "isPermissionGranted",
651
- actions: assign({ permissionResult: ({ event }) => event.output })
652
- }, {
653
- target: "#selfie.permissions",
654
- actions: assign({ permissionResult: ({ event }) => event.output })
655
- }]
656
- } }
657
- }
658
- },
659
- permissions: {
660
- initial: "idle",
661
- states: {
662
- idle: {
663
- invoke: {
664
- id: "checkPermissionIdle",
665
- src: "checkPermission",
666
- onDone: [{
667
- target: "#selfie.capture",
668
- guard: "isPermissionGranted",
669
- actions: assign({ permissionResult: ({ event }) => event.output })
670
- }, {
671
- target: "denied",
672
- guard: ({ event }) => event.output === "denied",
673
- actions: assign({ permissionResult: ({ event }) => event.output })
674
- }]
675
- },
676
- on: {
677
- REQUEST_PERMISSION: "requesting",
678
- GO_TO_LEARN_MORE: "learnMore"
679
- }
680
- },
681
- learnMore: { on: {
682
- BACK: "idle",
683
- REQUEST_PERMISSION: "requesting"
684
- } },
685
- requesting: { invoke: {
686
- id: "requestPermission",
687
- src: "requestPermission",
688
- onDone: [
689
- {
690
- target: "#selfie.capture",
691
- guard: "isPermissionGranted",
692
- actions: assign({ permissionResult: ({ event }) => event.output })
693
- },
694
- {
695
- target: "denied",
696
- guard: ({ event }) => event.output === "denied",
697
- actions: assign({ permissionResult: ({ event }) => event.output })
698
- },
699
- {
700
- target: "idle",
701
- actions: assign({ permissionResult: ({ event }) => event.output })
702
- }
703
- ],
704
- onError: { target: "denied" }
705
- } },
706
- denied: { entry: assign({ permissionResult: () => "refresh" }) }
707
- }
708
- },
709
- capture: {
710
- initial: "checkingStream",
711
- exit: [
712
- "stopMediaRecording",
713
- "stopRecordingStream",
714
- "clearRecordingSession",
715
- "stopMediaStream"
716
- ],
717
- states: {
718
- checkingStream: { always: [{
719
- target: "detecting",
720
- guard: "hasStream"
721
- }, { target: "initializing" }] },
722
- initializing: { invoke: {
723
- id: "initializeCamera",
724
- src: "initializeCamera",
725
- input: ({ context }) => context.config,
726
- onDone: {
727
- target: "detecting",
728
- actions: "setStreamAndCapturer"
729
- },
730
- onError: [{
731
- target: "#selfie.permissions",
732
- guard: "isPermissionDeniedError",
733
- actions: assign({ permissionResult: () => "denied" })
734
- }, {
735
- target: "#selfie.error",
736
- actions: assign({ error: ({ event }) => String(event.error) })
737
- }]
738
- } },
739
- detecting: {
740
- entry: [assign({ detectionStatus: () => "detecting" }), "createRecordingStream"],
741
- invoke: [{
742
- id: "startRecording",
743
- src: "startRecording",
744
- input: ({ context }) => ({
745
- config: context.config,
746
- clonedStream: context.recordingStream,
747
- existing: context.recordingSession
748
- }),
749
- onDone: { actions: assign({ recordingSession: ({ context, event }) => {
750
- return event.output ?? context.recordingSession;
751
- } }) },
752
- onError: { actions: () => void 0 }
753
- }, {
754
- id: "runDetection",
755
- src: "runDetection",
756
- input: ({ context }) => ({
757
- frameCapturer: context.frameCapturer,
758
- provider: context.provider,
759
- config: context.config
760
- })
761
- }],
762
- on: {
763
- DETECTION_UPDATE: { actions: assign({ detectionStatus: ({ event }) => event.status }) },
764
- DETECTION_FRAME: { actions: assign({ debugFrame: ({ event }) => event.frame }) },
765
- DETECTION_RESET_READY: { actions: assign({ resetDetection: ({ event }) => event.reset }) },
766
- DETECTION_SUCCESS: {
767
- target: "capturing",
768
- actions: assign({
769
- capturedImage: ({ event }) => event.canvas,
770
- faceCoordinates: ({ event }) => event.faceCoordinates
771
- })
772
- },
773
- MANUAL_CAPTURE: { target: "capturingManual" }
774
- }
775
- },
776
- capturing: {
777
- entry: ["captureImage"],
778
- always: [{
779
- target: "uploading",
780
- guard: "hasCapturedImage"
781
- }, {
782
- target: "uploadError",
783
- actions: assign(({ context }) => ({
784
- uploadError: FACE_ERROR_CODES.SERVER,
785
- attemptsRemaining: context.attemptsRemaining - 1
786
- }))
787
- }]
788
- },
789
- capturingManual: {
790
- entry: ["captureLatestFrame"],
791
- always: [{
792
- target: "uploading",
793
- guard: "hasCapturedImage"
794
- }, {
795
- target: "uploadError",
796
- actions: assign(({ context }) => ({
797
- uploadError: FACE_ERROR_CODES.SERVER,
798
- attemptsRemaining: context.attemptsRemaining - 1
799
- }))
800
- }]
801
- },
802
- uploading: { invoke: {
803
- id: "uploadSelfie",
804
- src: "uploadSelfie",
805
- input: ({ context }) => {
806
- const canvas = context.capturedImage;
807
- if (!canvas) throw new Error(FACE_ERROR_CODES.SERVER);
808
- return {
809
- canvas,
810
- faceCoordinates: context.faceCoordinates
811
- };
812
- },
813
- onDone: {
814
- target: "validatingUpload",
815
- actions: assign({ uploadResponse: ({ event }) => event.output })
816
- },
817
- onError: {
818
- target: "uploadError",
819
- actions: assign(({ context, event }) => ({
820
- uploadError: getFaceErrorCodeFromUnknown(event.error) ?? FACE_ERROR_CODES.SERVER,
821
- attemptsRemaining: context.attemptsRemaining - 1
822
- }))
823
- }
824
- } },
825
- validatingUpload: { always: [{
826
- target: "uploadError",
827
- guard: "hasUploadValidationError",
828
- actions: ["setUploadErrorFromUploadValidation", "decrementAttemptsRemaining"]
829
- }, { target: "success" }] },
830
- uploadError: { on: { RETRY_CAPTURE: {
831
- target: "detecting",
832
- guard: "hasAttemptsRemaining",
833
- actions: ["resetDetection", "clearUploadFailure"]
834
- } } },
835
- success: {
836
- entry: "stopMediaRecording",
837
- after: { 3e3: { target: "#selfie.finished" } }
838
- }
839
- }
840
- },
841
- finished: {
842
- entry: "stopMediaStream",
843
- on: { RESET: {
844
- target: "idle",
845
- actions: "resetContext"
846
- } }
847
- },
848
- closed: {
849
- entry: "stopMediaStream",
850
- type: "final"
851
- },
852
- error: {
853
- entry: "stopMediaStream",
854
- on: { RESET: {
855
- target: "idle",
856
- actions: "resetContext"
857
- } }
858
- }
78
+ hasUploadValidationError: () => false,
79
+ isTerminalUploadError: () => false
859
80
  }
860
81
  });
861
82
  /**
862
- * The selfie capture state machine.
863
- *
864
- * Note: Uses AnyStateMachine type for declaration file portability.
865
- * Type safety is ensured via the machine configuration.
83
+ * Selfie state machine variant for the captureOnly flow. Shares the full base
84
+ * face-capture state tree; only the upload/process step is local instead of
85
+ * remote.
866
86
  */
867
- const selfieMachine = _selfieMachine;
87
+ const selfieCaptureOnlyMachine = _selfieCaptureOnlyMachine;
868
88
 
869
89
  //#endregion
870
- //#region src/modules/selfie/selfieActor.ts
871
- function createSelfieActor(options) {
872
- return createActor(selfieMachine, { input: { config: options.config } }).start();
90
+ //#region src/modules/selfie/selfieCaptureOnlyActor.ts
91
+ function createSelfieCaptureOnlyActor(options) {
92
+ const dependencies = options.dependencies ?? {
93
+ storage: new BrowserStorageProvider(),
94
+ getWasmUtil: () => WasmUtilProvider.getInstance()
95
+ };
96
+ return createActor(selfieCaptureOnlyMachine, { input: {
97
+ config: options.config,
98
+ dependencies
99
+ } }).start();
873
100
  }
874
101
 
875
102
  //#endregion
876
- //#region src/modules/selfie/selfieManager.ts
877
- function getPermissionStatus(snapshot) {
878
- if (!snapshot.matches("permissions")) return;
879
- if (snapshot.matches({ permissions: "idle" })) return "idle";
880
- if (snapshot.matches({ permissions: "learnMore" })) return "learnMore";
881
- if (snapshot.matches({ permissions: "requesting" })) return "requesting";
882
- if (snapshot.matches({ permissions: "denied" })) return "denied";
883
- }
884
- function getCaptureStatus(snapshot) {
885
- if (snapshot.matches({ capture: "initializing" })) return "initializing";
886
- if (snapshot.matches({ capture: "detecting" })) return "detecting";
887
- if (snapshot.matches({ capture: "capturing" })) return "capturing";
888
- if (snapshot.matches({ capture: "capturingManual" })) return "capturing";
889
- if (snapshot.matches({ capture: "uploading" })) return "uploading";
890
- if (snapshot.matches({ capture: "uploadError" })) return "uploadError";
891
- if (snapshot.matches({ capture: "success" })) return "success";
892
- }
893
- function mapState(snapshot) {
894
- const { context } = snapshot;
895
- if (snapshot.matches("idle")) return { status: "idle" };
896
- if (snapshot.matches("loading")) return { status: "loading" };
897
- if (snapshot.matches("tutorial")) return { status: "tutorial" };
898
- if (snapshot.matches("closed")) return { status: "closed" };
899
- if (snapshot.matches("permissions")) {
900
- const permissionStatus = getPermissionStatus(snapshot);
901
- if (permissionStatus === void 0) return {
902
- status: "permissions",
903
- permissionStatus: "idle"
904
- };
905
- return {
906
- status: "permissions",
907
- permissionStatus
908
- };
909
- }
910
- if (snapshot.matches("capture")) return {
911
- status: "capture",
912
- captureStatus: getCaptureStatus(snapshot) ?? "initializing",
913
- stream: context.stream,
914
- detectionStatus: context.detectionStatus,
915
- debugFrame: context.debugFrame,
916
- attemptsRemaining: context.attemptsRemaining,
917
- uploadError: context.uploadError
918
- };
919
- if (snapshot.matches("finished")) return { status: "finished" };
920
- if (snapshot.matches("error")) return {
921
- status: "error",
922
- error: context.error ?? "Unknown error"
923
- };
924
- return { status: "idle" };
925
- }
926
- function createApi({ actor }) {
103
+ //#region src/modules/selfie/selfieCaptureOnlyManager.ts
104
+ /**
105
+ * Wraps a selfie captureOnly actor with a manager that fires `onCapture`
106
+ * exactly once when the actor reaches the `finished` final state. XState's
107
+ * `complete` lifecycle fires for both `finished` and `closed` (both are
108
+ * `type: 'final'` per the SDK terminal-state convention) gating on
109
+ * `finished` prevents a user-quit mid-capture from delivering a partial
110
+ * payload.
111
+ */
112
+ function attachOnCaptureFiring(actor, config) {
113
+ const subscription = actor.subscribe({ complete: () => {
114
+ const snapshot = actor.getSnapshot();
115
+ if (!snapshot.matches("finished")) return;
116
+ const payload = snapshot.context.captureOnlyResult;
117
+ if (!payload) return;
118
+ const response = { image: payload };
119
+ invokeOnCaptureCallback(config.onCapture, response, eventModuleNames.selfie);
120
+ } });
121
+ return () => subscription.unsubscribe();
122
+ }
123
+ function createSelfieCaptureOnlyManagerFromActor(actor, config) {
124
+ const unsubscribe = attachOnCaptureFiring(actor, config);
125
+ const manager = createFaceCaptureManagerFromActor(actor, eventModuleNames.selfie);
927
126
  return {
928
- load() {
929
- actor.send({ type: "LOAD" });
930
- },
931
- nextStep() {
932
- actor.send({ type: "NEXT_STEP" });
933
- },
934
- requestPermission() {
935
- actor.send({ type: "REQUEST_PERMISSION" });
936
- },
937
- goToLearnMore() {
938
- actor.send({ type: "GO_TO_LEARN_MORE" });
939
- },
940
- back() {
941
- actor.send({ type: "BACK" });
942
- },
943
- close() {
944
- actor.send({ type: "QUIT" });
945
- },
946
- reset() {
947
- actor.send({ type: "RESET" });
948
- },
949
- retryCapture() {
950
- actor.send({ type: "RETRY_CAPTURE" });
951
- },
952
- capture() {
953
- actor.send({ type: "MANUAL_CAPTURE" });
127
+ ...manager,
128
+ stop: () => {
129
+ unsubscribe();
130
+ manager.stop();
954
131
  }
955
132
  };
956
133
  }
957
134
  /**
958
- * Creates a selfie manager instance for handling selfie capture flow.
959
- *
960
- * The selfie manager provides:
961
- * - State management with statuses: `idle`, `loading`, `tutorial`, `permissions`, `capture`, `finished`, `closed`, `error`
962
- * - Permission handling with nested states: `idle`, `requesting`, `denied`, `learnMore`
963
- * - Capture handling with nested states: `initializing`, `startingRecorder`, `recordingActive`, `detecting`, `capturing`, `uploading`, `uploadError`, `success`
964
- * - Camera stream access when in `capture` state
965
- * - Detection status feedback during face detection
966
- * - Attempt tracking with `attemptsRemaining`
967
- *
968
- * @param options - Configuration for the selfie actor
969
- * @param options.config - The selfie module configuration from the flow
970
- * @returns A manager instance with state subscription, API methods, and lifecycle controls
971
- *
972
- * @example
973
- * ```ts
974
- * const selfieManager = createSelfieManager({ config: selfieConfig });
975
- *
976
- * selfieManager.subscribe((state) => {
977
- * if (state.status === 'capture') {
978
- * console.log('Camera ready:', state.stream);
979
- * console.log('Detection status:', state.detectionStatus);
980
- * }
981
- * });
135
+ * Creates a captureOnly selfie manager.
982
136
  *
983
- * selfieManager.load();
984
- * ```
137
+ * Unlike {@link createSelfieManager}, the captured face image is delivered
138
+ * locally to the integrator's `onCapture` callback instead of being uploaded
139
+ * to the Incode backend. `onCapture` is required.
985
140
  */
986
- function createSelfieManager(options) {
987
- return createManager({
988
- actor: createSelfieActor(options),
989
- mapState,
990
- createApi
991
- });
141
+ function createSelfieCaptureOnlyManager(options) {
142
+ return createSelfieCaptureOnlyManagerFromActor(createSelfieCaptureOnlyActor(options), options.config);
992
143
  }
993
144
 
994
145
  //#endregion
995
- export { createSelfieManager, selfieMachine };
146
+ export { createSelfieCaptureOnlyManager, createSelfieCaptureOnlyManagerFromActor, createSelfieManager, processFace, selfieCaptureOnlyMachine, selfieMachine };