smartcomply-web-sdk 1.0.3 → 1.0.4

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