smartcomply-web-sdk 1.0.3

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 (133) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +482 -0
  3. package/dist/camera/ActionDetector.d.ts +26 -0
  4. package/dist/camera/ActionDetector.d.ts.map +1 -0
  5. package/dist/camera/ActionDetector.js +114 -0
  6. package/dist/camera/ActionDetector.js.map +1 -0
  7. package/dist/camera/CameraManager.d.ts +7 -0
  8. package/dist/camera/CameraManager.d.ts.map +1 -0
  9. package/dist/camera/CameraManager.js +31 -0
  10. package/dist/camera/CameraManager.js.map +1 -0
  11. package/dist/camera/FaceDetector.d.ts +12 -0
  12. package/dist/camera/FaceDetector.d.ts.map +1 -0
  13. package/dist/camera/FaceDetector.js +55 -0
  14. package/dist/camera/FaceDetector.js.map +1 -0
  15. package/dist/camera/Permission.d.ts +42 -0
  16. package/dist/camera/Permission.d.ts.map +1 -0
  17. package/dist/camera/Permission.js +107 -0
  18. package/dist/camera/Permission.js.map +1 -0
  19. package/dist/camera/VideoRecorder.d.ts +12 -0
  20. package/dist/camera/VideoRecorder.d.ts.map +1 -0
  21. package/dist/camera/VideoRecorder.js +51 -0
  22. package/dist/camera/VideoRecorder.js.map +1 -0
  23. package/dist/client/Config.d.ts +9 -0
  24. package/dist/client/Config.d.ts.map +1 -0
  25. package/dist/client/Config.js +8 -0
  26. package/dist/client/Config.js.map +1 -0
  27. package/dist/client/HttpClient.d.ts +44 -0
  28. package/dist/client/HttpClient.d.ts.map +1 -0
  29. package/dist/client/HttpClient.js +149 -0
  30. package/dist/client/HttpClient.js.map +1 -0
  31. package/dist/client/Smartcomply.d.ts +50 -0
  32. package/dist/client/Smartcomply.d.ts.map +1 -0
  33. package/dist/client/Smartcomply.js +77 -0
  34. package/dist/client/Smartcomply.js.map +1 -0
  35. package/dist/components/CameraView.d.ts +59 -0
  36. package/dist/components/CameraView.d.ts.map +1 -0
  37. package/dist/components/CameraView.js +242 -0
  38. package/dist/components/CameraView.js.map +1 -0
  39. package/dist/components/DocumentUpload.d.ts +21 -0
  40. package/dist/components/DocumentUpload.d.ts.map +1 -0
  41. package/dist/components/DocumentUpload.js +220 -0
  42. package/dist/components/DocumentUpload.js.map +1 -0
  43. package/dist/errors/AuthError.d.ts +8 -0
  44. package/dist/errors/AuthError.d.ts.map +1 -0
  45. package/dist/errors/AuthError.js +15 -0
  46. package/dist/errors/AuthError.js.map +1 -0
  47. package/dist/errors/NetworkError.d.ts +8 -0
  48. package/dist/errors/NetworkError.d.ts.map +1 -0
  49. package/dist/errors/NetworkError.js +15 -0
  50. package/dist/errors/NetworkError.js.map +1 -0
  51. package/dist/errors/SDKError.d.ts +7 -0
  52. package/dist/errors/SDKError.d.ts.map +1 -0
  53. package/dist/errors/SDKError.js +14 -0
  54. package/dist/errors/SDKError.js.map +1 -0
  55. package/dist/flow/DocumentCapture.d.ts +54 -0
  56. package/dist/flow/DocumentCapture.d.ts.map +1 -0
  57. package/dist/flow/DocumentCapture.js +801 -0
  58. package/dist/flow/DocumentCapture.js.map +1 -0
  59. package/dist/flow/SmartComplyFlow.d.ts +104 -0
  60. package/dist/flow/SmartComplyFlow.d.ts.map +1 -0
  61. package/dist/flow/SmartComplyFlow.js +1726 -0
  62. package/dist/flow/SmartComplyFlow.js.map +1 -0
  63. package/dist/flow/theme.d.ts +30 -0
  64. package/dist/flow/theme.d.ts.map +1 -0
  65. package/dist/flow/theme.js +103 -0
  66. package/dist/flow/theme.js.map +1 -0
  67. package/dist/index.d.ts +20 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +29 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/modules/liveness/LivenessUI.d.ts +46 -0
  72. package/dist/modules/liveness/LivenessUI.d.ts.map +1 -0
  73. package/dist/modules/liveness/LivenessUI.js +609 -0
  74. package/dist/modules/liveness/LivenessUI.js.map +1 -0
  75. package/dist/modules/liveness/index.d.ts +5 -0
  76. package/dist/modules/liveness/index.d.ts.map +1 -0
  77. package/dist/modules/liveness/index.js +10 -0
  78. package/dist/modules/liveness/index.js.map +1 -0
  79. package/dist/modules/liveness/liveness.d.ts +85 -0
  80. package/dist/modules/liveness/liveness.d.ts.map +1 -0
  81. package/dist/modules/liveness/liveness.js +425 -0
  82. package/dist/modules/liveness/liveness.js.map +1 -0
  83. package/dist/modules/liveness/uploader.d.ts +34 -0
  84. package/dist/modules/liveness/uploader.d.ts.map +1 -0
  85. package/dist/modules/liveness/uploader.js +106 -0
  86. package/dist/modules/liveness/uploader.js.map +1 -0
  87. package/dist/modules/onboarding/index.d.ts +2 -0
  88. package/dist/modules/onboarding/index.d.ts.map +1 -0
  89. package/dist/modules/onboarding/index.js +6 -0
  90. package/dist/modules/onboarding/index.js.map +1 -0
  91. package/dist/modules/onboarding/onboarding.d.ts +25 -0
  92. package/dist/modules/onboarding/onboarding.d.ts.map +1 -0
  93. package/dist/modules/onboarding/onboarding.js +33 -0
  94. package/dist/modules/onboarding/onboarding.js.map +1 -0
  95. package/dist/smartcomply.browser.js +8147 -0
  96. package/dist/smartcomply.browser.js.map +7 -0
  97. package/dist/types/common.d.ts +27 -0
  98. package/dist/types/common.d.ts.map +1 -0
  99. package/dist/types/common.js +3 -0
  100. package/dist/types/common.js.map +1 -0
  101. package/dist/types/liveness.d.ts +142 -0
  102. package/dist/types/liveness.d.ts.map +1 -0
  103. package/dist/types/liveness.js +3 -0
  104. package/dist/types/liveness.js.map +1 -0
  105. package/dist/types/onboarding.d.ts +53 -0
  106. package/dist/types/onboarding.d.ts.map +1 -0
  107. package/dist/types/onboarding.js +3 -0
  108. package/dist/types/onboarding.js.map +1 -0
  109. package/dist/types/quality.d.ts +23 -0
  110. package/dist/types/quality.d.ts.map +1 -0
  111. package/dist/types/quality.js +6 -0
  112. package/dist/types/quality.js.map +1 -0
  113. package/dist/utils/ImageQuality.d.ts +48 -0
  114. package/dist/utils/ImageQuality.d.ts.map +1 -0
  115. package/dist/utils/ImageQuality.js +186 -0
  116. package/dist/utils/ImageQuality.js.map +1 -0
  117. package/dist/utils/constants.d.ts +59 -0
  118. package/dist/utils/constants.d.ts.map +1 -0
  119. package/dist/utils/constants.js +70 -0
  120. package/dist/utils/constants.js.map +1 -0
  121. package/dist/utils/device.d.ts +14 -0
  122. package/dist/utils/device.d.ts.map +1 -0
  123. package/dist/utils/device.js +55 -0
  124. package/dist/utils/device.js.map +1 -0
  125. package/dist/utils/logger.d.ts +28 -0
  126. package/dist/utils/logger.d.ts.map +1 -0
  127. package/dist/utils/logger.js +67 -0
  128. package/dist/utils/logger.js.map +1 -0
  129. package/dist/utils/validator.d.ts +35 -0
  130. package/dist/utils/validator.d.ts.map +1 -0
  131. package/dist/utils/validator.js +116 -0
  132. package/dist/utils/validator.js.map +1 -0
  133. package/package.json +51 -0
@@ -0,0 +1,85 @@
1
+ import { HttpClient } from "../../client/HttpClient";
2
+ import { ChallengeAction, LivenessCreateRequest, LivenessCreateResponse, LivenessSubmitResponse } from "../../types/liveness";
3
+ /**
4
+ * Liveness module — 3-step flow aligned with Adhere backend.
5
+ *
6
+ * Flow:
7
+ * Step 1: liveness.create() → POST /v1/sdk/liveness/create/ (multipart)
8
+ * Step 2: [camera runs, user performs challenge actions]
9
+ * Step 3: liveness.submit() → POST /v1/sdk/liveness/submit/ (multipart)
10
+ *
11
+ * After submit, the backend returns status: "processing".
12
+ * Final results are delivered via webhook (liveness.completed event).
13
+ */
14
+ export declare class LivenessModule {
15
+ private http;
16
+ constructor(http: HttpClient);
17
+ /**
18
+ * Step 1: Create a liveness challenge entry.
19
+ *
20
+ * POST /v1/sdk/liveness/create/
21
+ * Auth: x-access-token: <sessionToken>
22
+ * Content-Type: multipart/form-data
23
+ */
24
+ create(params: LivenessCreateRequest): Promise<LivenessCreateResponse>;
25
+ /**
26
+ * Step 3: Submit liveness video recording.
27
+ *
28
+ * POST /v1/sdk/liveness/submit/
29
+ * Auth: x-access-token: <sessionToken>
30
+ * Content-Type: multipart/form-data
31
+ *
32
+ * Note: After submission, the session is revoked (single-use).
33
+ * The backend returns status: "processing" — results come via webhook.
34
+ */
35
+ submit(entryId: number, videoBlob: Blob, snapshotBlob: Blob, autoshotBlob?: Blob, videoDurationSeconds?: number, videoSizeBytes?: number): Promise<LivenessSubmitResponse>;
36
+ /**
37
+ * Poll the public SDK status endpoint for the final verification result.
38
+ * Safe to call after the session has been revoked (no auth required).
39
+ *
40
+ * GET /v1/sdk/liveness/<entryId>/sdk-result/
41
+ */
42
+ pollResult(entryId: number): Promise<{
43
+ id: number;
44
+ status: string;
45
+ failure_reason: string | null;
46
+ completed_at: string | null;
47
+ }>;
48
+ /**
49
+ * Full liveness check — orchestrates the entire flow:
50
+ *
51
+ * 1. Take autoshot selfie from camera
52
+ * 2. Create liveness entry on backend (with challenge actions + selfie + optional doc)
53
+ * 3. Run action detection loop (camera + MediaPipe)
54
+ * 4. Record video and submit to backend
55
+ * 5. Return submit response (status: "processing")
56
+ *
57
+ * @param container - DOM element to mount the liveness UI into
58
+ * @param params - identifier info and optional document image
59
+ * @param actions - override challenge actions (default: BLINK, TURN_LEFT, OPEN_MOUTH)
60
+ */
61
+ startCheck(container: HTMLElement, params: {
62
+ identifier: string;
63
+ identifier_type: string;
64
+ country: string;
65
+ id_file?: File | Blob;
66
+ document?: File | Blob;
67
+ document_front?: File | Blob;
68
+ document_back?: File | Blob;
69
+ identity_check?: number;
70
+ verification_type?: "data_verification" | "document_verification" | "liveness_only";
71
+ }, actions?: ChallengeAction[], existingEntryId?: number): Promise<LivenessSubmitResponse>;
72
+ /**
73
+ * Capture a single sharp frame from a video element as a JPEG Blob.
74
+ * Retries up to maxAttempts times (300 ms apart) if the Laplacian blur
75
+ * score is below threshold, then falls back to the best frame captured.
76
+ */
77
+ private captureFrame;
78
+ /**
79
+ * Laplacian variance blur metric on a centre-cropped region (320×240 max).
80
+ * Higher score = sharper image. Score < 80 is considered blurry.
81
+ */
82
+ private _computeBlurScore;
83
+ private describeAction;
84
+ }
85
+ //# sourceMappingURL=liveness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liveness.d.ts","sourceRoot":"","sources":["../../../src/modules/liveness/liveness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,sBAAsB,CAAC;AAQ9B;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAEpC;;;;;;OAMG;IACG,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAuD5E;;;;;;;;;OASG;IACG,MAAM,CACV,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,IAAI,EACf,YAAY,EAAE,IAAI,EAClB,YAAY,CAAC,EAAE,IAAI,EACnB,oBAAoB,CAAC,EAAE,MAAM,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,sBAAsB,CAAC;IAqBlC;;;;;OAKG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QACzC,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,CAAC;IAIF;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;QACvB,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;QAC7B,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,uBAAuB,GAAG,eAAe,CAAC;KACrF,EACD,OAAO,CAAC,EAAE,eAAe,EAAE,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,sBAAsB,CAAC;IAmQlC;;;;OAIG;YACW,YAAY;IAuC1B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,cAAc;CAUvB"}
@@ -0,0 +1,425 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LivenessModule = void 0;
4
+ const device_1 = require("../../utils/device");
5
+ const CameraManager_1 = require("../../camera/CameraManager");
6
+ const VideoRecorder_1 = require("../../camera/VideoRecorder");
7
+ const FaceDetector_1 = require("../../camera/FaceDetector");
8
+ const ActionDetector_1 = require("../../camera/ActionDetector");
9
+ const LivenessUI_1 = require("./LivenessUI");
10
+ /**
11
+ * Liveness module — 3-step flow aligned with Adhere backend.
12
+ *
13
+ * Flow:
14
+ * Step 1: liveness.create() → POST /v1/sdk/liveness/create/ (multipart)
15
+ * Step 2: [camera runs, user performs challenge actions]
16
+ * Step 3: liveness.submit() → POST /v1/sdk/liveness/submit/ (multipart)
17
+ *
18
+ * After submit, the backend returns status: "processing".
19
+ * Final results are delivered via webhook (liveness.completed event).
20
+ */
21
+ class LivenessModule {
22
+ constructor(http) {
23
+ this.http = http;
24
+ }
25
+ /**
26
+ * Step 1: Create a liveness challenge entry.
27
+ *
28
+ * POST /v1/sdk/liveness/create/
29
+ * Auth: x-access-token: <sessionToken>
30
+ * Content-Type: multipart/form-data
31
+ */
32
+ async create(params) {
33
+ const formData = new FormData();
34
+ formData.append("identifier", params.identifier);
35
+ formData.append("identifier_type", params.identifier_type);
36
+ formData.append("country", params.country);
37
+ formData.append("challenge_actions", JSON.stringify(params.challenge_actions));
38
+ if (params.autoshot_file) {
39
+ formData.append("autoshot", params.autoshot_file, "autoshot.jpg");
40
+ }
41
+ // Handle document upload - support both legacy and new front/back fields
42
+ if (params.document) {
43
+ formData.append("document", params.document, "document.jpg");
44
+ if (params.document_back) {
45
+ formData.append("document_back", params.document_back, "document_back.jpg");
46
+ }
47
+ }
48
+ else if (params.document_front) {
49
+ // Legacy support - map to document for backward compatibility
50
+ formData.append("document", params.document_front, "document_front.jpg");
51
+ if (params.document_back) {
52
+ formData.append("document_back", params.document_back, "document_back.jpg");
53
+ }
54
+ }
55
+ else if (params.id_file) {
56
+ // Legacy support - map to document for backward compatibility
57
+ formData.append("document", params.id_file, "id_document.jpg");
58
+ }
59
+ if (params.identity_check) {
60
+ formData.append("identity_check", String(params.identity_check));
61
+ }
62
+ if (params.snapshot_file) {
63
+ formData.append("snapshot", params.snapshot_file, "snapshot.jpg");
64
+ }
65
+ if (params.device_type) {
66
+ formData.append("device_type", params.device_type);
67
+ }
68
+ if (params.os_name) {
69
+ formData.append("os_name", params.os_name);
70
+ }
71
+ if (params.browser_timezone) {
72
+ formData.append("browser_timezone", params.browser_timezone);
73
+ }
74
+ if (params.fingerprint_id) {
75
+ formData.append("fingerprint_id", params.fingerprint_id);
76
+ }
77
+ return this.http.uploadWithSession("/v1/sdk/liveness/create/", formData);
78
+ }
79
+ /**
80
+ * Step 3: Submit liveness video recording.
81
+ *
82
+ * POST /v1/sdk/liveness/submit/
83
+ * Auth: x-access-token: <sessionToken>
84
+ * Content-Type: multipart/form-data
85
+ *
86
+ * Note: After submission, the session is revoked (single-use).
87
+ * The backend returns status: "processing" — results come via webhook.
88
+ */
89
+ async submit(entryId, videoBlob, snapshotBlob, autoshotBlob, videoDurationSeconds, videoSizeBytes) {
90
+ const formData = new FormData();
91
+ formData.append("entry", String(entryId));
92
+ formData.append("video", videoBlob, "liveness.webm");
93
+ formData.append("snapshot", snapshotBlob, "snapshot.jpg");
94
+ if (autoshotBlob) {
95
+ formData.append("autoshot", autoshotBlob, "autoshot.jpg");
96
+ }
97
+ if (videoDurationSeconds !== undefined) {
98
+ formData.append("video_duration_seconds", String(videoDurationSeconds));
99
+ }
100
+ if (videoSizeBytes !== undefined) {
101
+ formData.append("video_size_bytes", String(videoSizeBytes));
102
+ }
103
+ return this.http.uploadWithSession("/v1/sdk/liveness/submit/", formData);
104
+ }
105
+ /**
106
+ * Poll the public SDK status endpoint for the final verification result.
107
+ * Safe to call after the session has been revoked (no auth required).
108
+ *
109
+ * GET /v1/sdk/liveness/<entryId>/sdk-result/
110
+ */
111
+ async pollResult(entryId) {
112
+ return this.http.publicGet(`/v1/sdk/liveness/${entryId}/sdk-result/`);
113
+ }
114
+ /**
115
+ * Full liveness check — orchestrates the entire flow:
116
+ *
117
+ * 1. Take autoshot selfie from camera
118
+ * 2. Create liveness entry on backend (with challenge actions + selfie + optional doc)
119
+ * 3. Run action detection loop (camera + MediaPipe)
120
+ * 4. Record video and submit to backend
121
+ * 5. Return submit response (status: "processing")
122
+ *
123
+ * @param container - DOM element to mount the liveness UI into
124
+ * @param params - identifier info and optional document image
125
+ * @param actions - override challenge actions (default: BLINK, TURN_LEFT, OPEN_MOUTH)
126
+ */
127
+ async startCheck(container, params, actions, existingEntryId) {
128
+ const camera = new CameraManager_1.CameraManager();
129
+ const recorder = new VideoRecorder_1.VideoRecorder();
130
+ const faceEngine = new FaceDetector_1.FaceDetectorEngine();
131
+ const ui = new LivenessUI_1.LivenessUI();
132
+ // Tracks whether cleanup has already been called (so the finally block
133
+ // doesn't double-destroy when retry takes over).
134
+ let cleanedUp = false;
135
+ const cleanup = () => {
136
+ if (cleanedUp)
137
+ return;
138
+ cleanedUp = true;
139
+ faceEngine.destroy();
140
+ camera.stop();
141
+ ui.unmount();
142
+ };
143
+ try {
144
+ // 1. Open camera (with permission error handling)
145
+ let stream;
146
+ try {
147
+ stream = await camera.open();
148
+ }
149
+ catch (camErr) {
150
+ if (camErr.name === "NotAllowedError" || camErr.name === "PermissionDeniedError") {
151
+ throw new Error("Camera access denied. Please allow camera permission in your browser settings and try again.");
152
+ }
153
+ if (camErr.name === "NotFoundError" || camErr.name === "DevicesNotFoundError") {
154
+ throw new Error("No camera found. Please connect a camera and try again.");
155
+ }
156
+ throw new Error(`Camera error: ${camErr.message || "Unable to access camera"}`);
157
+ }
158
+ // 2. Document capture step — document_verification only, when no id_file pre-supplied
159
+ let capturedDocBlob = params.id_file;
160
+ if (!capturedDocBlob && params.verification_type === "document_verification") {
161
+ capturedDocBlob = await ui.mountDocumentCapture(container, stream);
162
+ }
163
+ // 3. Create a temporary video element to capture autoshot
164
+ const tempVideo = document.createElement("video");
165
+ tempVideo.srcObject = stream;
166
+ tempVideo.muted = true;
167
+ tempVideo.playsInline = true;
168
+ await tempVideo.play();
169
+ // Wait for face to be detected before capturing autoshot
170
+ ui.mount(container, { actions: [], time_limit_seconds: 0, instruction: "Centre your face in the oval" });
171
+ const uiVideo = ui.getVideoElement();
172
+ if (uiVideo) {
173
+ uiVideo.srcObject = stream;
174
+ await uiVideo.play();
175
+ }
176
+ else {
177
+ tempVideo.style.cssText = "width:100%;border-radius:12px;transform:scaleX(-1);";
178
+ container.appendChild(tempVideo);
179
+ }
180
+ await faceEngine.init();
181
+ ui.updateInstruction("Centre your face in the oval");
182
+ // Wait until face is stably centred (5 consecutive detected frames, max 12 s)
183
+ const videoForDetection = uiVideo || tempVideo;
184
+ await new Promise((resolve) => {
185
+ let attempts = 0;
186
+ let stableFrames = 0;
187
+ const STABLE_REQUIRED = 5;
188
+ const maxAttempts = 120; // 12 seconds at 100ms intervals
189
+ const checkFace = () => {
190
+ attempts++;
191
+ if (videoForDetection.readyState >= 2) {
192
+ const detection = faceEngine.detect(videoForDetection, performance.now());
193
+ if (detection.faceDetected) {
194
+ stableFrames++;
195
+ if (stableFrames >= STABLE_REQUIRED) {
196
+ resolve();
197
+ return;
198
+ }
199
+ }
200
+ else {
201
+ stableFrames = 0; // reset on any lost-face frame
202
+ ui.updateInstruction("Move your face into the oval");
203
+ }
204
+ }
205
+ if (attempts >= maxAttempts) {
206
+ resolve(); // proceed anyway after timeout
207
+ return;
208
+ }
209
+ setTimeout(checkFace, 100);
210
+ };
211
+ checkFace();
212
+ });
213
+ ui.updateInstruction("Hold still — capturing your selfie...");
214
+ await new Promise((r) => setTimeout(r, 700)); // wait for camera auto-focus
215
+ // 3. Capture autoshot (selfie) — face stable, camera focused
216
+ const autoshotBlob = await this.captureFrame(videoForDetection);
217
+ tempVideo.pause();
218
+ // Validate autoshot blob is not empty
219
+ if (!autoshotBlob || autoshotBlob.size === 0) {
220
+ throw new Error("Failed to capture autoshot selfie. Please ensure camera is working.");
221
+ }
222
+ // 4. Create liveness entry on backend (skip if retrying with existing entry)
223
+ const challengeActions = actions || ["BLINK", "TURN_LEFT", "OPEN_MOUTH"];
224
+ let entry;
225
+ if (existingEntryId) {
226
+ // Reuse existing entry on retry (D1: prevent duplicate wallet charges)
227
+ entry = { id: existingEntryId, challenge_actions: challengeActions, status: "pending", client_id: "", expires_at: null };
228
+ }
229
+ else {
230
+ const deviceMeta = (0, device_1.collectDeviceMetadata)();
231
+ entry = await this.create({
232
+ identifier: params.identifier,
233
+ identifier_type: params.identifier_type,
234
+ country: params.country,
235
+ challenge_actions: challengeActions,
236
+ autoshot_file: autoshotBlob,
237
+ document: params.document || params.document_front || params.id_file || capturedDocBlob,
238
+ document_back: params.document_back,
239
+ identity_check: params.identity_check,
240
+ device_type: deviceMeta.device_type,
241
+ os_name: deviceMeta.os_name,
242
+ browser_timezone: deviceMeta.browser_timezone,
243
+ fingerprint_id: deviceMeta.fingerprint_id,
244
+ });
245
+ }
246
+ // 5. Mount UI with challenge actions from backend
247
+ const uiChallenge = {
248
+ actions: entry.challenge_actions,
249
+ time_limit_seconds: 45,
250
+ instruction: "Complete each action below to verify it's you",
251
+ };
252
+ // Re-mount UI with challenge actions (face engine already initialized)
253
+ ui.unmount();
254
+ const videoEl = ui.mount(container, uiChallenge);
255
+ videoEl.srcObject = stream;
256
+ await videoEl.play();
257
+ // 7. Start recording
258
+ recorder.start(stream);
259
+ ui.updateInstruction(uiChallenge.instruction);
260
+ // 8. Run detection loop (sequential — one action at a time)
261
+ const actionDetector = new ActionDetector_1.ActionDetector(entry.challenge_actions);
262
+ let lastAction = null;
263
+ const detectionResult = await new Promise((resolve) => {
264
+ let animFrameId;
265
+ const timeout = setTimeout(() => {
266
+ cancelAnimationFrame(animFrameId);
267
+ resolve("timeout");
268
+ }, uiChallenge.time_limit_seconds * 1000);
269
+ const detectLoop = () => {
270
+ if (videoEl.readyState >= 2) {
271
+ const detection = faceEngine.detect(videoEl, performance.now());
272
+ const states = actionDetector.check(detection);
273
+ ui.updateActions(states);
274
+ const current = actionDetector.getCurrentAction();
275
+ if (!detection.faceDetected) {
276
+ ui.updateInstruction("Move your face into the oval");
277
+ }
278
+ else if (current) {
279
+ if (current !== lastAction) {
280
+ lastAction = current;
281
+ ui.updateInstruction(this.describeAction(current));
282
+ }
283
+ }
284
+ else {
285
+ ui.updateInstruction("All actions completed! ✓");
286
+ }
287
+ if (actionDetector.allCompleted()) {
288
+ clearTimeout(timeout);
289
+ cancelAnimationFrame(animFrameId);
290
+ resolve("completed");
291
+ return;
292
+ }
293
+ }
294
+ animFrameId = requestAnimationFrame(detectLoop);
295
+ };
296
+ detectLoop();
297
+ });
298
+ // ── On timeout: show retry screen, clean up, restart ──────────
299
+ if (detectionResult === "timeout") {
300
+ // IMPORTANT: mark cleanedUp=true BEFORE returning the new Promise so
301
+ // the outer `finally` block does NOT call cleanup() immediately —
302
+ // which would destroy the UI before the user sees the timeout screen.
303
+ cleanedUp = true;
304
+ recorder.stop().catch(() => { }); // discard partial recording
305
+ return new Promise((resolveRetry, rejectRetry) => {
306
+ ui.showTimeout(() => {
307
+ // User clicked "Try Again" — now it is safe to destroy this attempt
308
+ faceEngine.destroy();
309
+ camera.stop();
310
+ ui.unmount();
311
+ // Restart liveness with same entry ID to prevent duplicate charges (D1)
312
+ // Pass capturedDocBlob forward so document scan step is not repeated
313
+ this.startCheck(container, { ...params, id_file: capturedDocBlob }, actions, entry.id)
314
+ .then(resolveRetry)
315
+ .catch(rejectRetry);
316
+ });
317
+ // showTimeout waits for the user to click — the promise stays pending.
318
+ });
319
+ }
320
+ // ── On completion: stop recording, submit, show success ────────
321
+ // Wait for user to return to neutral face after last action before
322
+ // capturing snapshot — prevents mid-action pose from skewing DeepFace
323
+ // embeddings (open mouth / head turned = high cosine distance).
324
+ ui.updateInstruction("Almost done — hold still...");
325
+ await new Promise((r) => setTimeout(r, 800));
326
+ // Capture a snapshot frame while the video is still playing
327
+ const snapshotBlob = await this.captureFrame(videoEl);
328
+ // 9. Stop recording
329
+ const { blob: videoBlob, durationSeconds } = await recorder.stop();
330
+ // 10. Submit to backend — always include autoshot so document-flow
331
+ // entries (which bypassed create() via existingEntryId) still get it.
332
+ const submitResult = await this.submit(entry.id, videoBlob, snapshotBlob, autoshotBlob, durationSeconds, videoBlob.size);
333
+ // 11. Show success overlay briefly before the flow advances
334
+ await ui.showResult(submitResult.status === "processing" ? "processing" : "failed", submitResult.id);
335
+ return submitResult;
336
+ }
337
+ finally {
338
+ cleanup();
339
+ }
340
+ }
341
+ /**
342
+ * Capture a single sharp frame from a video element as a JPEG Blob.
343
+ * Retries up to maxAttempts times (300 ms apart) if the Laplacian blur
344
+ * score is below threshold, then falls back to the best frame captured.
345
+ */
346
+ async captureFrame(video, maxAttempts = 4) {
347
+ const MIN_BLUR_SCORE = 80; // Laplacian variance — above this = acceptably sharp
348
+ const RETRY_DELAY_MS = 300;
349
+ let bestBlob = null;
350
+ let bestScore = -1;
351
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
352
+ const canvas = document.createElement("canvas");
353
+ canvas.width = video.videoWidth || 640;
354
+ canvas.height = video.videoHeight || 480;
355
+ const ctx = canvas.getContext("2d");
356
+ if (!ctx)
357
+ throw new Error("Failed to get canvas context");
358
+ ctx.drawImage(video, 0, 0);
359
+ const score = this._computeBlurScore(canvas);
360
+ const blob = await new Promise((res) => canvas.toBlob((b) => res(b), "image/jpeg", 0.92));
361
+ if (blob && score > bestScore) {
362
+ bestBlob = blob;
363
+ bestScore = score;
364
+ }
365
+ if (score >= MIN_BLUR_SCORE)
366
+ break; // sharp enough — stop early
367
+ if (attempt < maxAttempts - 1) {
368
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
369
+ }
370
+ }
371
+ if (bestBlob)
372
+ return bestBlob;
373
+ throw new Error("Failed to capture frame");
374
+ }
375
+ /**
376
+ * Laplacian variance blur metric on a centre-cropped region (320×240 max).
377
+ * Higher score = sharper image. Score < 80 is considered blurry.
378
+ */
379
+ _computeBlurScore(canvas) {
380
+ const ctx = canvas.getContext("2d");
381
+ if (!ctx)
382
+ return 0;
383
+ // Sample a 320×240 centre crop for performance
384
+ const sw = Math.min(canvas.width, 320);
385
+ const sh = Math.min(canvas.height, 240);
386
+ const sx = Math.floor((canvas.width - sw) / 2);
387
+ const sy = Math.floor((canvas.height - sh) / 2);
388
+ const { data, width, height } = ctx.getImageData(sx, sy, sw, sh);
389
+ // Convert to greyscale
390
+ const gray = new Float32Array(width * height);
391
+ for (let i = 0; i < width * height; i++) {
392
+ gray[i] =
393
+ 0.299 * data[i * 4] +
394
+ 0.587 * data[i * 4 + 1] +
395
+ 0.114 * data[i * 4 + 2];
396
+ }
397
+ // Laplacian convolution [0,1,0 / 1,-4,1 / 0,1,0] — sum of squared responses
398
+ let sum = 0;
399
+ let n = 0;
400
+ for (let y = 1; y < height - 1; y++) {
401
+ for (let x = 1; x < width - 1; x++) {
402
+ const l = gray[(y - 1) * width + x] +
403
+ gray[(y + 1) * width + x] +
404
+ gray[y * width + (x - 1)] +
405
+ gray[y * width + (x + 1)] -
406
+ 4 * gray[y * width + x];
407
+ sum += l * l;
408
+ n++;
409
+ }
410
+ }
411
+ return n > 0 ? sum / n : 0;
412
+ }
413
+ describeAction(action) {
414
+ const descriptions = {
415
+ BLINK: "Blink your eyes",
416
+ TURN_LEFT: "Turn your head left",
417
+ TURN_RIGHT: "Turn your head right",
418
+ TURN_HEAD: "Turn your head side to side",
419
+ OPEN_MOUTH: "Open your mouth wide",
420
+ };
421
+ return descriptions[action] || action.replace(/_/g, " ").toLowerCase();
422
+ }
423
+ }
424
+ exports.LivenessModule = LivenessModule;
425
+ //# sourceMappingURL=liveness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liveness.js","sourceRoot":"","sources":["../../../src/modules/liveness/liveness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * LivenessUploader — thin wrapper around the HttpClient's uploadWithSession()
3
+ * that adds:
4
+ * - Progress callbacks (onProgress)
5
+ * - Upload cancellation via AbortController
6
+ * - Automatic retry on transient network failure (configurable)
7
+ *
8
+ * Used internally by LivenessModule.create() and LivenessModule.submit() when
9
+ * the caller needs progress reporting (e.g. for a progress bar during video
10
+ * upload on slow connections).
11
+ */
12
+ import { HttpClient } from "../../client/HttpClient";
13
+ export interface UploadOptions {
14
+ /** Called with a value 0–1 as the upload progresses. */
15
+ onProgress?: (fraction: number) => void;
16
+ /** Optional AbortSignal to cancel the upload mid-flight. */
17
+ signal?: AbortSignal;
18
+ /** Override the default number of retries on transient errors. */
19
+ retries?: number;
20
+ }
21
+ export declare class LivenessUploader {
22
+ private http;
23
+ constructor(http: HttpClient);
24
+ /**
25
+ * Upload a FormData payload to `path` with optional progress and
26
+ * cancellation support.
27
+ *
28
+ * Falls back to the standard `uploadWithSession()` on browsers that do not
29
+ * support XMLHttpRequest upload progress (very rare).
30
+ */
31
+ upload<T>(path: string, formData: FormData, options?: UploadOptions): Promise<T>;
32
+ private uploadWithProgress;
33
+ }
34
+ //# sourceMappingURL=uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../../../src/modules/liveness/uploader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAIrD,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,WAAW,CAAC;IAErB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,gBAAgB;IACf,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAEpC;;;;;;OAMG;IACG,MAAM,CAAC,CAAC,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,CAAC,CAAC;YAcC,kBAAkB;CAkFjC"}