@incodetech/core 2.0.0-alpha.11 → 2.0.0-alpha.12
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.
- package/dist/{OpenViduLogger-5b7KqNTo.esm.js → OpenViduLogger-CRbRNZA7.esm.js} +1 -1
- package/dist/OpenViduLogger-Dy5P806a.esm.js +3 -0
- package/dist/{warmup-Cijuyvoc.d.ts → StateMachine-pi8byl8C.d.ts} +4 -1
- package/dist/{addEvent-s2Za-pK3.esm.js → addEvent-BGKc_lHF.esm.js} +1 -1
- package/dist/{deepsightLoader-Bn2D0REl.esm.js → deepsightLoader-B36_XZ7r.esm.js} +3 -3
- package/dist/{recordingsRepository-CTjaf-ER.esm.js → deepsightService-BWxcc4OC.esm.js} +2 -33
- package/dist/email.d.ts +1 -1
- package/dist/email.esm.js +3 -3
- package/dist/{endpoints-B0ltwtb5.esm.js → endpoints-D9TGnxRK.esm.js} +336 -21
- package/dist/flow.d.ts +4 -303
- package/dist/flow.esm.js +4 -5
- package/dist/id-CJKLe8HS.esm.js +1818 -0
- package/dist/id.d.ts +6 -0
- package/dist/id.esm.js +8 -0
- package/dist/index-CbF_uI-x.d.ts +618 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.esm.js +7 -4
- package/dist/{lib-CykGFCEr.esm.js → lib-BJoLTN_W.esm.js} +2 -2
- package/dist/phone.d.ts +1 -1
- package/dist/phone.esm.js +3 -3
- package/dist/recordingsRepository-D5MURoVB.esm.js +40 -0
- package/dist/selfie.d.ts +22 -324
- package/dist/selfie.esm.js +34 -23
- package/dist/{permissionServices-BhD0KxsO.esm.js → streamingEvents-B3hNanPl.esm.js} +34 -3
- package/dist/types-BpCrZLU6.d.ts +302 -0
- package/dist/types-DZbrbPgj.d.ts +335 -0
- package/package.json +6 -2
- package/dist/OpenViduLogger-20ZYS-mT.esm.js +0 -3
- package/dist/StateMachine-BqPpBhOz.d.ts +0 -2
- package/dist/getBrowser-CLEzz0Hi.esm.js +0 -8
- package/dist/types-Dif6MQmX.d.ts +0 -5
- /package/dist/{Manager-Cy9-TMC9.d.ts → Manager-BZUZTRPx.d.ts} +0 -0
- /package/dist/{chunk-C_Yo44FK.esm.js → chunk-FbsBJI8u.esm.js} +0 -0
- /package/dist/{xstate.esm-2T5fOCTq.esm.js → xstate.esm-2hDiAXvZ.esm.js} +0 -0
|
@@ -0,0 +1,1818 @@
|
|
|
1
|
+
import { A as isIOS, C as enumerateVideoDevices, E as createManager, M as isSafari, O as isAndroid, S as applyTrackConstraints, T as stopCameraStream, c as DEFAULT_ID_CAPTURE_MODEL_VERSION, h as OpenViduRecordingProvider, j as isIPhone14OrHigher, k as isDesktop, l as DEFAULT_ID_CAPTURE_THRESHOLDS, n as api, t as endpoints, v as StreamCanvasProcessingSession, w as requestCameraAccess, x as IncodeCanvas, y as StreamCanvasCapture } from "./endpoints-D9TGnxRK.esm.js";
|
|
2
|
+
import { n as startRecording, r as stopRecording$1, t as createRecordingSession } from "./recordingsRepository-D5MURoVB.esm.js";
|
|
3
|
+
import { i as getDeviceClass, n as checkPermission, o as getWindowDimensions, r as requestPermission, t as streamingEvents } from "./streamingEvents-B3hNanPl.esm.js";
|
|
4
|
+
import { a as createActor, i as fromPromise, n as assign, r as fromCallback, t as setup } from "./xstate.esm-2hDiAXvZ.esm.js";
|
|
5
|
+
import { t as addEvent } from "./addEvent-BGKc_lHF.esm.js";
|
|
6
|
+
|
|
7
|
+
//#region src/modules/id/idCameraStream.ts
|
|
8
|
+
const BACK_CAMERA_KEYWORDS = [
|
|
9
|
+
"rear",
|
|
10
|
+
"back",
|
|
11
|
+
"rück",
|
|
12
|
+
"arrière",
|
|
13
|
+
"trasera",
|
|
14
|
+
"trás",
|
|
15
|
+
"traseira",
|
|
16
|
+
"posteriore",
|
|
17
|
+
"后面",
|
|
18
|
+
"後面",
|
|
19
|
+
"背面",
|
|
20
|
+
"后置",
|
|
21
|
+
"後置",
|
|
22
|
+
"背置",
|
|
23
|
+
"задней",
|
|
24
|
+
"الخلفية",
|
|
25
|
+
"후",
|
|
26
|
+
"arka",
|
|
27
|
+
"achterzijde",
|
|
28
|
+
"หลัง",
|
|
29
|
+
"baksidan",
|
|
30
|
+
"bagside",
|
|
31
|
+
"sau",
|
|
32
|
+
"bak",
|
|
33
|
+
"tylny",
|
|
34
|
+
"takakamera",
|
|
35
|
+
"belakang",
|
|
36
|
+
"אחורית",
|
|
37
|
+
"πίσω",
|
|
38
|
+
"spate",
|
|
39
|
+
"hátsó",
|
|
40
|
+
"zadní",
|
|
41
|
+
"darrere",
|
|
42
|
+
"zadná",
|
|
43
|
+
"задня",
|
|
44
|
+
"stražnja",
|
|
45
|
+
"बैक"
|
|
46
|
+
];
|
|
47
|
+
function isBackCameraLabel(label) {
|
|
48
|
+
const lowercaseLabel = label.toLowerCase();
|
|
49
|
+
return BACK_CAMERA_KEYWORDS.some((keyword) => lowercaseLabel.includes(keyword));
|
|
50
|
+
}
|
|
51
|
+
function classifyCamera(device, index, totalDevices) {
|
|
52
|
+
let cameraType;
|
|
53
|
+
if (device.label === "") cameraType = totalDevices === 1 || index + 1 <= totalDevices / 2 ? "front" : "back";
|
|
54
|
+
else cameraType = isBackCameraLabel(device.label) ? "back" : "front";
|
|
55
|
+
return {
|
|
56
|
+
deviceId: device.deviceId,
|
|
57
|
+
label: device.label,
|
|
58
|
+
cameraType
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function getCameras() {
|
|
62
|
+
const videoDevices = await enumerateVideoDevices();
|
|
63
|
+
const cameras = videoDevices.map((d, i) => classifyCamera(d, i, videoDevices.length));
|
|
64
|
+
if (cameras.length > 1 && !cameras.some((c) => c.cameraType === "back")) {
|
|
65
|
+
const resolutions = cameras.map((c) => {
|
|
66
|
+
const match = c.label.match(/\b([0-9]+)MP?\b/i);
|
|
67
|
+
return match ? parseInt(match[1], 10) : NaN;
|
|
68
|
+
});
|
|
69
|
+
let backCameraIndex = cameras.length - 1;
|
|
70
|
+
if (!resolutions.some(isNaN)) backCameraIndex = resolutions.lastIndexOf(Math.max(...resolutions));
|
|
71
|
+
cameras[backCameraIndex].cameraType = "back";
|
|
72
|
+
}
|
|
73
|
+
return cameras;
|
|
74
|
+
}
|
|
75
|
+
function selectMainCameraFromStream(track, cameras) {
|
|
76
|
+
const settings = track.getSettings();
|
|
77
|
+
const activeCamera = cameras.find((c) => c.deviceId === settings.deviceId || c.label !== "" && c.label === track.label);
|
|
78
|
+
if (!activeCamera) return void 0;
|
|
79
|
+
if ((settings.facingMode === "environment" || isBackCameraLabel(track.label)) && cameras.length > 1) {
|
|
80
|
+
cameras.forEach((camera) => {
|
|
81
|
+
if (camera.deviceId === activeCamera.deviceId) camera.cameraType = "back";
|
|
82
|
+
else if (!isBackCameraLabel(camera.label)) camera.cameraType = "front";
|
|
83
|
+
});
|
|
84
|
+
return cameras.filter((c) => c.cameraType === "back").sort((a, b) => a.label.localeCompare(b.label))[0];
|
|
85
|
+
}
|
|
86
|
+
if (cameras.length === 1) return activeCamera;
|
|
87
|
+
}
|
|
88
|
+
function getAndroidVideoConstraints(level) {
|
|
89
|
+
const base = {
|
|
90
|
+
resizeMode: "none",
|
|
91
|
+
facingMode: "environment"
|
|
92
|
+
};
|
|
93
|
+
switch (level) {
|
|
94
|
+
case 0: return {
|
|
95
|
+
...base,
|
|
96
|
+
height: { ideal: 720 },
|
|
97
|
+
aspectRatio: { ideal: 19.5 / 9 }
|
|
98
|
+
};
|
|
99
|
+
case 1: return {
|
|
100
|
+
...base,
|
|
101
|
+
width: {
|
|
102
|
+
min: 3200,
|
|
103
|
+
ideal: 3840,
|
|
104
|
+
max: 4096
|
|
105
|
+
},
|
|
106
|
+
height: {
|
|
107
|
+
min: 1800,
|
|
108
|
+
ideal: 2160,
|
|
109
|
+
max: 2400
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
case 2: return {
|
|
113
|
+
...base,
|
|
114
|
+
width: {
|
|
115
|
+
min: 1400,
|
|
116
|
+
ideal: 1920,
|
|
117
|
+
max: 2160
|
|
118
|
+
},
|
|
119
|
+
height: {
|
|
120
|
+
min: 900,
|
|
121
|
+
ideal: 1080,
|
|
122
|
+
max: 1440
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
case 3: return {
|
|
126
|
+
...base,
|
|
127
|
+
width: {
|
|
128
|
+
min: 640,
|
|
129
|
+
ideal: 640,
|
|
130
|
+
max: 800
|
|
131
|
+
},
|
|
132
|
+
height: {
|
|
133
|
+
min: 480,
|
|
134
|
+
ideal: 480,
|
|
135
|
+
max: 600
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
case 4: return {
|
|
139
|
+
...base,
|
|
140
|
+
width: {
|
|
141
|
+
min: 640,
|
|
142
|
+
ideal: 800,
|
|
143
|
+
max: 960
|
|
144
|
+
},
|
|
145
|
+
height: {
|
|
146
|
+
min: 480,
|
|
147
|
+
ideal: 480,
|
|
148
|
+
max: 480
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
default: return {};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function delay(ms) {
|
|
155
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
156
|
+
}
|
|
157
|
+
async function getAndroidBackCameraStream(fallbackLevel = 0) {
|
|
158
|
+
if (fallbackLevel > 4) throw new Error("Failed to get camera after all fallback attempts");
|
|
159
|
+
try {
|
|
160
|
+
const initialStream = await requestCameraAccess({ video: getAndroidVideoConstraints(fallbackLevel) });
|
|
161
|
+
const track = initialStream.getVideoTracks()[0];
|
|
162
|
+
const mainCamera = selectMainCameraFromStream(track, await getCameras());
|
|
163
|
+
stopCameraStream(initialStream);
|
|
164
|
+
if (!mainCamera) throw new Error("Could not identify main camera");
|
|
165
|
+
let idealWidth = 1280;
|
|
166
|
+
let idealHeight = 720;
|
|
167
|
+
if (fallbackLevel > 1) {
|
|
168
|
+
const constraints = getAndroidVideoConstraints(fallbackLevel);
|
|
169
|
+
const width = constraints.width;
|
|
170
|
+
const height = constraints.height;
|
|
171
|
+
idealWidth = width?.ideal ?? 1280;
|
|
172
|
+
idealHeight = height?.ideal ?? 720;
|
|
173
|
+
}
|
|
174
|
+
return await requestCameraAccess({ video: {
|
|
175
|
+
deviceId: { exact: mainCamera.deviceId },
|
|
176
|
+
width: { ideal: idealWidth },
|
|
177
|
+
height: { ideal: idealHeight }
|
|
178
|
+
} });
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const errorName = error instanceof Error ? error.name : "UnknownError";
|
|
181
|
+
const nextLevel = Math.min(fallbackLevel + 1, 4);
|
|
182
|
+
if (errorName === "NotReadableError") {
|
|
183
|
+
await delay(300);
|
|
184
|
+
return getAndroidBackCameraStream(nextLevel);
|
|
185
|
+
}
|
|
186
|
+
if (errorName === "AbortError") {
|
|
187
|
+
await delay(300);
|
|
188
|
+
return getAndroidBackCameraStream(fallbackLevel);
|
|
189
|
+
}
|
|
190
|
+
return getAndroidBackCameraStream(nextLevel);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function applyIOSFocusHack(stream) {
|
|
194
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
195
|
+
try {
|
|
196
|
+
await applyTrackConstraints(videoTrack, { advanced: [{ focusDistance: 1 }] });
|
|
197
|
+
} catch {}
|
|
198
|
+
}
|
|
199
|
+
function getIOSConstraints() {
|
|
200
|
+
return {
|
|
201
|
+
audio: false,
|
|
202
|
+
video: {
|
|
203
|
+
resizeMode: "none",
|
|
204
|
+
facingMode: "environment",
|
|
205
|
+
height: { ideal: isIPhone14OrHigher() ? 1080 : 720 },
|
|
206
|
+
aspectRatio: { ideal: 19.5 / 9 }
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async function getIOSCameraStream() {
|
|
211
|
+
const stream = await requestCameraAccess(getIOSConstraints());
|
|
212
|
+
await applyIOSFocusHack(stream);
|
|
213
|
+
return stream;
|
|
214
|
+
}
|
|
215
|
+
function getDesktopConstraints(options) {
|
|
216
|
+
const { deviceId } = options;
|
|
217
|
+
return {
|
|
218
|
+
audio: isSafari(),
|
|
219
|
+
video: {
|
|
220
|
+
facingMode: "user",
|
|
221
|
+
deviceId: deviceId ? { exact: deviceId } : void 0,
|
|
222
|
+
height: { ideal: 1080 },
|
|
223
|
+
width: { ideal: 1920 }
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
async function getDesktopCameraStream(options) {
|
|
228
|
+
return requestCameraAccess(getDesktopConstraints(options));
|
|
229
|
+
}
|
|
230
|
+
async function getIdCameraStream(deviceId) {
|
|
231
|
+
if (isIOS()) return getIOSCameraStream();
|
|
232
|
+
if (isAndroid()) return getAndroidBackCameraStream(0);
|
|
233
|
+
if (isDesktop()) return getDesktopCameraStream({ deviceId });
|
|
234
|
+
return getDesktopCameraStream({ deviceId });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/modules/id/types.ts
|
|
239
|
+
const ID_ERROR_CODES = {
|
|
240
|
+
UPLOAD_ERROR: "UPLOAD_ERROR",
|
|
241
|
+
CLASSIFICATION_FAILED: "CLASSIFICATION_FAILED",
|
|
242
|
+
LOW_SHARPNESS: "LOW_SHARPNESS",
|
|
243
|
+
GLARE_DETECTED: "GLARE_DETECTED",
|
|
244
|
+
WRONG_DOCUMENT_SIDE: "WRONG_DOCUMENT_SIDE",
|
|
245
|
+
ID_TYPE_UNACCEPTABLE: "ID_TYPE_UNACCEPTABLE",
|
|
246
|
+
READABILITY_ISSUE: "READABILITY_ISSUE",
|
|
247
|
+
RETRY_EXHAUSTED_CONTINUE_TO_BACK: "RETRY_EXHAUSTED_CONTINUE_TO_BACK",
|
|
248
|
+
RETRY_EXHAUSTED_SKIP_BACK: "RETRY_EXHAUSTED_SKIP_BACK",
|
|
249
|
+
NO_MORE_TRIES: "NO_MORE_TRIES",
|
|
250
|
+
UNEXPECTED_ERROR: "UNEXPECTED_ERROR",
|
|
251
|
+
NO_TOKEN: "NO_TOKEN",
|
|
252
|
+
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
253
|
+
USER_CANCELLED: "USER_CANCELLED",
|
|
254
|
+
SERVER: "SERVER_ERROR"
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/modules/id/idCaptureServices.ts
|
|
259
|
+
const SHARPNESS_THRESHOLD = 10;
|
|
260
|
+
const GLARE_THRESHOLD = 10;
|
|
261
|
+
const DEFAULT_ID_CAPTURE_THRESHOLDS$1 = {
|
|
262
|
+
blurThreshold: .2,
|
|
263
|
+
blurChangeThreshold: .2,
|
|
264
|
+
glareThreshold: .3,
|
|
265
|
+
clsThreshold: .98,
|
|
266
|
+
sideThreshold: .8,
|
|
267
|
+
iouThreshold: .8,
|
|
268
|
+
framesAggregationInterval: 3e3,
|
|
269
|
+
minFaceIdQualityScore: .62
|
|
270
|
+
};
|
|
271
|
+
const DEFAULT_ID_CAPTURE_SETTINGS = {
|
|
272
|
+
isFixedMask: false,
|
|
273
|
+
isIPhone14OrHigher: false,
|
|
274
|
+
idType: "",
|
|
275
|
+
blurCheckEnabled: false,
|
|
276
|
+
glareCheckEnabled: false,
|
|
277
|
+
faceQualityCheckEnabled: true,
|
|
278
|
+
iouCheckEnabled: true
|
|
279
|
+
};
|
|
280
|
+
async function initializeIdCapture(provider, config) {
|
|
281
|
+
await provider.initialize({});
|
|
282
|
+
provider.setThresholds({
|
|
283
|
+
...DEFAULT_ID_CAPTURE_THRESHOLDS$1,
|
|
284
|
+
...config.thresholds,
|
|
285
|
+
idDetectedTimeout: config.thresholds?.idDetectedTimeout ?? config.deviceIdleTimeout * 1e3,
|
|
286
|
+
autocaptureTimeout: config.thresholds?.autocaptureTimeout ?? config.autoCaptureTimeout * 1e3
|
|
287
|
+
});
|
|
288
|
+
if (config.settings) provider.setSettings({
|
|
289
|
+
...DEFAULT_ID_CAPTURE_SETTINGS,
|
|
290
|
+
...config.settings
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
stream: await getIdCameraStream(),
|
|
294
|
+
provider
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function stopStream(stream) {
|
|
298
|
+
for (const track of stream.getTracks()) track.stop();
|
|
299
|
+
}
|
|
300
|
+
function validateUploadResponse(response, sessionState) {
|
|
301
|
+
if (response.failReason === "ID_TYPE_UNACCEPTABLE") return {
|
|
302
|
+
error: true,
|
|
303
|
+
message: "ID type is not acceptable",
|
|
304
|
+
messageDescription: "Please use a valid ID type",
|
|
305
|
+
errorKey: ID_ERROR_CODES.ID_TYPE_UNACCEPTABLE
|
|
306
|
+
};
|
|
307
|
+
if (response.failReason === "WRONG_DOCUMENT_SIDE") return {
|
|
308
|
+
error: true,
|
|
309
|
+
message: "Wrong side of document",
|
|
310
|
+
messageDescription: response.side === "back" ? "Please show the back side of your ID" : "Please show the front side of your ID",
|
|
311
|
+
errorKey: ID_ERROR_CODES.WRONG_DOCUMENT_SIDE
|
|
312
|
+
};
|
|
313
|
+
if (!response.classification) return {
|
|
314
|
+
error: true,
|
|
315
|
+
message: "ID classification failed",
|
|
316
|
+
errorKey: ID_ERROR_CODES.CLASSIFICATION_FAILED
|
|
317
|
+
};
|
|
318
|
+
const sharpnessThreshold = getDeviceClass() === "desktop" ? -1 : SHARPNESS_THRESHOLD;
|
|
319
|
+
if (response.sharpness !== void 0 && sharpnessThreshold >= 0 && response.sharpness < sharpnessThreshold) return {
|
|
320
|
+
error: true,
|
|
321
|
+
message: "Image is not sharp enough",
|
|
322
|
+
messageDescription: "Please ensure the image is clear and well-focused",
|
|
323
|
+
errorKey: ID_ERROR_CODES.LOW_SHARPNESS
|
|
324
|
+
};
|
|
325
|
+
if (response.glare !== void 0 && response.glare < GLARE_THRESHOLD && !sessionState?.skipGlareFront && !sessionState?.skipGlareBack) return {
|
|
326
|
+
error: true,
|
|
327
|
+
message: "Glare detected on ID",
|
|
328
|
+
messageDescription: "Please avoid bright reflections on your ID",
|
|
329
|
+
errorKey: ID_ERROR_CODES.GLARE_DETECTED
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
async function getExtraImages(params) {
|
|
333
|
+
const extraImages = params.ageAssurance ? [params.type === "back" ? "croppedBackID" : "croppedFrontID"] : [];
|
|
334
|
+
try {
|
|
335
|
+
const res = await api.post(endpoints.getImages, { images: ["croppedIDFace", ...extraImages] }, { signal: params.signal });
|
|
336
|
+
if (!res.ok) throw new Error(`Failed to get extra images: ${res.status}`);
|
|
337
|
+
return res.data ?? {
|
|
338
|
+
croppedIDFace: "",
|
|
339
|
+
croppedFrontID: "",
|
|
340
|
+
croppedBackID: ""
|
|
341
|
+
};
|
|
342
|
+
} catch {
|
|
343
|
+
return {
|
|
344
|
+
croppedIDFace: "",
|
|
345
|
+
croppedFrontID: "",
|
|
346
|
+
croppedBackID: ""
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const getRealQualityValue = (value) => (1 - value) * 100;
|
|
351
|
+
async function uploadIdImage(params) {
|
|
352
|
+
const { type, image, onProgress, signal, metadata, ageAssurance, glare, sharpness, shouldSkipGlareBack } = params;
|
|
353
|
+
addEvent({
|
|
354
|
+
code: "captureAttemptFinished",
|
|
355
|
+
module: "ID",
|
|
356
|
+
payload: { logs: [] }
|
|
357
|
+
});
|
|
358
|
+
const endpoint = type === "front" ? endpoints.frontId : endpoints.backId;
|
|
359
|
+
const body = {
|
|
360
|
+
base64Image: image,
|
|
361
|
+
metadata,
|
|
362
|
+
clientInfo: { deviceClass: getDeviceClass() }
|
|
363
|
+
};
|
|
364
|
+
const queryParams = { imageType: "id" };
|
|
365
|
+
if (shouldSkipGlareBack && type === "back") queryParams.glare = 0;
|
|
366
|
+
else if (glare !== void 0) queryParams.glare = getRealQualityValue(glare);
|
|
367
|
+
if (sharpness !== void 0) queryParams.sharpness = getRealQualityValue(sharpness);
|
|
368
|
+
try {
|
|
369
|
+
const res = await api.post(endpoint, body, {
|
|
370
|
+
signal,
|
|
371
|
+
query: queryParams,
|
|
372
|
+
onUploadProgress: onProgress
|
|
373
|
+
});
|
|
374
|
+
if (!res.ok) throw new Error(`POST ${endpoint} failed: ${res.status} ${res.statusText}`);
|
|
375
|
+
const response = res.data;
|
|
376
|
+
const extraImages = await getExtraImages({
|
|
377
|
+
type,
|
|
378
|
+
ageAssurance,
|
|
379
|
+
signal
|
|
380
|
+
});
|
|
381
|
+
const fullResponse = {
|
|
382
|
+
...response,
|
|
383
|
+
originalImage: image,
|
|
384
|
+
frontIdImage: type === "front" ? image : void 0,
|
|
385
|
+
backIdImage: extraImages.croppedBackID,
|
|
386
|
+
...extraImages
|
|
387
|
+
};
|
|
388
|
+
onProgress?.(100);
|
|
389
|
+
return fullResponse;
|
|
390
|
+
} catch (error) {
|
|
391
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
392
|
+
throw new Error(`${ID_ERROR_CODES.UPLOAD_ERROR}: ${errorMessage}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function buildStandardResolution(width, height) {
|
|
396
|
+
return height > width ? {
|
|
397
|
+
width: 1080,
|
|
398
|
+
height: 1920
|
|
399
|
+
} : {
|
|
400
|
+
width: 1920,
|
|
401
|
+
height: 1080
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function buildResolutionFromStream(stream) {
|
|
405
|
+
const track = stream.getVideoTracks()[0];
|
|
406
|
+
if (!track) return "1080x1920";
|
|
407
|
+
const settings = track.getSettings();
|
|
408
|
+
const width = settings.width;
|
|
409
|
+
const height = settings.height;
|
|
410
|
+
if (typeof width === "number" && typeof height === "number") {
|
|
411
|
+
const standard = buildStandardResolution(width, height);
|
|
412
|
+
return `${standard.width}x${standard.height}`;
|
|
413
|
+
}
|
|
414
|
+
return "1080x1920";
|
|
415
|
+
}
|
|
416
|
+
async function startRecordingSession(params) {
|
|
417
|
+
if (params.config.enableIdRecording !== true) return;
|
|
418
|
+
if (params.existing) return params.existing;
|
|
419
|
+
const provider = params.config.recording?.capability ?? new OpenViduRecordingProvider();
|
|
420
|
+
const clonedStream = params.stream.clone();
|
|
421
|
+
const hasAudio = clonedStream.getAudioTracks().length > 0;
|
|
422
|
+
const resolution = buildResolutionFromStream(clonedStream);
|
|
423
|
+
const session = await createRecordingSession(params.type);
|
|
424
|
+
const connection = await provider.connect({
|
|
425
|
+
sessionToken: session.token,
|
|
426
|
+
stream: clonedStream,
|
|
427
|
+
events: {
|
|
428
|
+
onSessionConnected: (sessionId) => {
|
|
429
|
+
addEvent({
|
|
430
|
+
code: streamingEvents.strSessionDidConnect,
|
|
431
|
+
payload: {
|
|
432
|
+
message: "Recording session connected",
|
|
433
|
+
sessionId
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
onSessionDisconnected: (sessionId) => {
|
|
438
|
+
addEvent({
|
|
439
|
+
code: streamingEvents.strSessionDidDisconnect,
|
|
440
|
+
payload: {
|
|
441
|
+
message: "Recording session disconnected",
|
|
442
|
+
sessionId
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
},
|
|
446
|
+
onSessionException: (event) => {
|
|
447
|
+
addEvent({
|
|
448
|
+
code: streamingEvents.strSessionDidFailWithError,
|
|
449
|
+
payload: {
|
|
450
|
+
message: "Recording session failed due to an error",
|
|
451
|
+
eventName: event.name,
|
|
452
|
+
type: "OpenViduException",
|
|
453
|
+
errorMessage: event.message,
|
|
454
|
+
sessionId: event.sessionId
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
},
|
|
458
|
+
onPublisherCreated: (p) => {
|
|
459
|
+
addEvent({
|
|
460
|
+
code: streamingEvents.strStreamPublisherCreated,
|
|
461
|
+
payload: {
|
|
462
|
+
message: "Recording publisher created",
|
|
463
|
+
sessionId: p.sessionId,
|
|
464
|
+
streamId: p.streamId
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
onPublisherError: (p) => {
|
|
469
|
+
addEvent({
|
|
470
|
+
code: streamingEvents.strStreamPublisherDidFailWithError,
|
|
471
|
+
payload: {
|
|
472
|
+
message: "Recording publisher failed due to an error",
|
|
473
|
+
sessionId: p.sessionId,
|
|
474
|
+
streamId: p.streamId,
|
|
475
|
+
error: { message: p.message ?? "Unknown error" }
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
await startRecording({
|
|
482
|
+
videoRecordingId: session.videoRecordingId,
|
|
483
|
+
type: params.type,
|
|
484
|
+
resolution,
|
|
485
|
+
hasAudio
|
|
486
|
+
});
|
|
487
|
+
addEvent({
|
|
488
|
+
code: streamingEvents.strStreamVideoCaptureStart,
|
|
489
|
+
payload: {
|
|
490
|
+
message: "Recording capture started",
|
|
491
|
+
resolution,
|
|
492
|
+
videoRecordingId: session.videoRecordingId,
|
|
493
|
+
sessionId: session.sessionId,
|
|
494
|
+
streamId: connection.publisher.getStreamId()
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
return {
|
|
498
|
+
token: session.token,
|
|
499
|
+
sessionId: session.sessionId,
|
|
500
|
+
videoRecordingId: session.videoRecordingId,
|
|
501
|
+
connection,
|
|
502
|
+
resolution,
|
|
503
|
+
hasAudio
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function stopRecording(session) {
|
|
507
|
+
(async () => {
|
|
508
|
+
try {
|
|
509
|
+
addEvent({
|
|
510
|
+
code: streamingEvents.strStreamVideoCaptureStop,
|
|
511
|
+
payload: {
|
|
512
|
+
message: "Recording capture stopped",
|
|
513
|
+
videoRecordingId: session.videoRecordingId,
|
|
514
|
+
sessionId: session.sessionId,
|
|
515
|
+
streamId: session.connection.publisher.getStreamId()
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
await stopRecording$1(session.videoRecordingId);
|
|
519
|
+
addEvent({
|
|
520
|
+
code: streamingEvents.strStreamPublisherDestroyed,
|
|
521
|
+
payload: {
|
|
522
|
+
message: "Recording publisher destroyed",
|
|
523
|
+
sessionId: session.sessionId,
|
|
524
|
+
streamId: session.connection.publisher.getStreamId()
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
} finally {
|
|
528
|
+
await session.connection.disconnect();
|
|
529
|
+
addEvent({
|
|
530
|
+
code: streamingEvents.strSessionDidDisconnect,
|
|
531
|
+
payload: {
|
|
532
|
+
message: "Recording session disconnected",
|
|
533
|
+
sessionId: session.sessionId
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
})();
|
|
538
|
+
}
|
|
539
|
+
async function processId(isSecondId = false, queueName = "", signal) {
|
|
540
|
+
const endpoint = isSecondId ? endpoints.processSecondId : endpoints.processId;
|
|
541
|
+
const url = queueName ? `${endpoint}?queueName=${queueName}` : endpoint;
|
|
542
|
+
return { isDocumentExpired: (await api.post(url, {}, { signal })).data?.isDocumentExpired ?? false };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/modules/id/idCaptureStateMachine.ts
|
|
547
|
+
function getIdErrorCodeFromUnknown(error) {
|
|
548
|
+
if (error instanceof Error) {
|
|
549
|
+
const message = error.message;
|
|
550
|
+
for (const [, value] of Object.entries(ID_ERROR_CODES)) if (message.includes(value)) return value;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const _idCaptureMachine = setup({
|
|
554
|
+
types: {
|
|
555
|
+
context: {},
|
|
556
|
+
events: {},
|
|
557
|
+
input: {}
|
|
558
|
+
},
|
|
559
|
+
actors: {
|
|
560
|
+
checkPermission: fromPromise(async () => {
|
|
561
|
+
return checkPermission();
|
|
562
|
+
}),
|
|
563
|
+
requestPermission: fromPromise(async () => {
|
|
564
|
+
return requestPermission();
|
|
565
|
+
}),
|
|
566
|
+
initializeCamera: fromPromise(async ({ input }) => {
|
|
567
|
+
return initializeIdCapture(input.provider, input.config);
|
|
568
|
+
}),
|
|
569
|
+
runDetection: fromCallback(({ input, sendBack }) => {
|
|
570
|
+
if (!input.frameCapturer || !input.provider) {
|
|
571
|
+
sendBack({
|
|
572
|
+
type: "DETECTION_UPDATE",
|
|
573
|
+
status: "error"
|
|
574
|
+
});
|
|
575
|
+
return () => {};
|
|
576
|
+
}
|
|
577
|
+
const provider = input.provider;
|
|
578
|
+
let session;
|
|
579
|
+
let currentCanvas = null;
|
|
580
|
+
let bestCanvas = null;
|
|
581
|
+
let storedQualityElements = {};
|
|
582
|
+
let isCapturing = false;
|
|
583
|
+
let notificationTimeout = null;
|
|
584
|
+
let currentNotificationStatus = null;
|
|
585
|
+
const NOTIFICATION_DURATION_MS = 500;
|
|
586
|
+
const clearNotificationTimeout = () => {
|
|
587
|
+
if (notificationTimeout) {
|
|
588
|
+
clearTimeout(notificationTimeout);
|
|
589
|
+
notificationTimeout = null;
|
|
590
|
+
currentNotificationStatus = null;
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
const sendNotification = (status) => {
|
|
594
|
+
if (notificationTimeout && currentNotificationStatus !== status) return;
|
|
595
|
+
if (notificationTimeout && currentNotificationStatus === status) clearTimeout(notificationTimeout);
|
|
596
|
+
currentNotificationStatus = status;
|
|
597
|
+
sendBack({
|
|
598
|
+
type: "DETECTION_UPDATE",
|
|
599
|
+
status
|
|
600
|
+
});
|
|
601
|
+
notificationTimeout = setTimeout(() => {
|
|
602
|
+
notificationTimeout = null;
|
|
603
|
+
currentNotificationStatus = null;
|
|
604
|
+
sendBack({
|
|
605
|
+
type: "DETECTION_UPDATE",
|
|
606
|
+
status: "detecting"
|
|
607
|
+
});
|
|
608
|
+
}, NOTIFICATION_DURATION_MS);
|
|
609
|
+
};
|
|
610
|
+
const canvas = input.frameCapturer.getLatestCanvas();
|
|
611
|
+
const windowDimensions = getWindowDimensions(canvas?.width() ?? 1280, canvas?.height() ?? 720);
|
|
612
|
+
const DEFAULT_GEOMETRY = {
|
|
613
|
+
areaDown: 25e3,
|
|
614
|
+
areaUp: 55e3,
|
|
615
|
+
areaIOSPassportUp: 3e4,
|
|
616
|
+
areaIOSPassportDown: 2e4,
|
|
617
|
+
widthIOSUp: 160,
|
|
618
|
+
widthIOSDown: 85,
|
|
619
|
+
widthDown: 110,
|
|
620
|
+
widthUp: 205
|
|
621
|
+
};
|
|
622
|
+
if (input.config.geometry) provider.setGeometry({
|
|
623
|
+
...input.config.geometry,
|
|
624
|
+
windowOuterWidth: windowDimensions.outerWidth,
|
|
625
|
+
windowOuterHeight: windowDimensions.outerHeight,
|
|
626
|
+
windowInnerWidth: windowDimensions.innerWidth,
|
|
627
|
+
windowInnerHeight: windowDimensions.innerHeight
|
|
628
|
+
});
|
|
629
|
+
else provider.setGeometry({
|
|
630
|
+
...DEFAULT_GEOMETRY,
|
|
631
|
+
windowOuterWidth: windowDimensions.outerWidth,
|
|
632
|
+
windowOuterHeight: windowDimensions.outerHeight,
|
|
633
|
+
windowInnerWidth: windowDimensions.innerWidth,
|
|
634
|
+
windowInnerHeight: windowDimensions.innerHeight
|
|
635
|
+
});
|
|
636
|
+
const idType = input.currentMode === "back" ? "BackId" : input.currentMode === "passport" || input.currentMode === "front" && input.config.onlyFront ? "Passport" : "FrontId";
|
|
637
|
+
provider.setSettings({
|
|
638
|
+
isFixedMask: input.config.settings?.isFixedMask ?? false,
|
|
639
|
+
isIPhone14OrHigher: input.config.settings?.isIPhone14OrHigher ?? false,
|
|
640
|
+
idType,
|
|
641
|
+
blurCheckEnabled: input.config.settings?.blurCheckEnabled ?? false,
|
|
642
|
+
glareCheckEnabled: input.config.settings?.glareCheckEnabled ?? false,
|
|
643
|
+
faceQualityCheckEnabled: input.config.settings?.faceQualityCheckEnabled ?? true,
|
|
644
|
+
iouCheckEnabled: input.config.settings?.iouCheckEnabled ?? true
|
|
645
|
+
});
|
|
646
|
+
const thresholds = {
|
|
647
|
+
...DEFAULT_ID_CAPTURE_THRESHOLDS,
|
|
648
|
+
...input.config.thresholds,
|
|
649
|
+
idDetectedTimeout: (input.config.deviceIdleTimeout ?? 10) * 1e3,
|
|
650
|
+
autocaptureTimeout: (input.config.autoCaptureTimeout ?? 5) * 1e3
|
|
651
|
+
};
|
|
652
|
+
provider.setThresholds(thresholds);
|
|
653
|
+
const modelType = input.config.modelVersion ?? DEFAULT_ID_CAPTURE_MODEL_VERSION;
|
|
654
|
+
provider.setModelType(modelType);
|
|
655
|
+
provider.setCallbacks({
|
|
656
|
+
onFarAway: () => {
|
|
657
|
+
if (!isCapturing) sendNotification("farAway");
|
|
658
|
+
},
|
|
659
|
+
onDetectionStarted: () => {},
|
|
660
|
+
onMaskChange: (_show, _mask, _top, orientation) => {
|
|
661
|
+
sendBack({
|
|
662
|
+
type: "ORIENTATION_CHANGE",
|
|
663
|
+
orientation
|
|
664
|
+
});
|
|
665
|
+
},
|
|
666
|
+
onBlur: () => {
|
|
667
|
+
if (!isCapturing) sendNotification("blur");
|
|
668
|
+
},
|
|
669
|
+
onGlare: () => {
|
|
670
|
+
if (!isCapturing) sendNotification("glare");
|
|
671
|
+
},
|
|
672
|
+
onIdNotDetected: () => {
|
|
673
|
+
if (!isCapturing) sendBack({
|
|
674
|
+
type: "DETECTION_UPDATE",
|
|
675
|
+
status: "idNotDetected"
|
|
676
|
+
});
|
|
677
|
+
},
|
|
678
|
+
onSwitchToManualCapture: () => {
|
|
679
|
+
isCapturing = false;
|
|
680
|
+
sendBack({
|
|
681
|
+
type: "DETECTION_UPDATE",
|
|
682
|
+
status: "manualCapture"
|
|
683
|
+
});
|
|
684
|
+
},
|
|
685
|
+
onCapturing: () => {
|
|
686
|
+
clearNotificationTimeout();
|
|
687
|
+
isCapturing = true;
|
|
688
|
+
sendBack({
|
|
689
|
+
type: "DETECTION_UPDATE",
|
|
690
|
+
status: "capturing"
|
|
691
|
+
});
|
|
692
|
+
},
|
|
693
|
+
onBestFrame: (blur, glare, orientation) => {
|
|
694
|
+
if (currentCanvas) {
|
|
695
|
+
bestCanvas = currentCanvas.clone();
|
|
696
|
+
storedQualityElements = {
|
|
697
|
+
glare,
|
|
698
|
+
sharpness: blur
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
if (orientation === "horizontal" || orientation === "vertical") sendBack({
|
|
702
|
+
type: "ORIENTATION_CHANGE",
|
|
703
|
+
orientation
|
|
704
|
+
});
|
|
705
|
+
},
|
|
706
|
+
onCapture: () => {
|
|
707
|
+
isCapturing = false;
|
|
708
|
+
if (bestCanvas) sendBack({
|
|
709
|
+
type: "DETECTION_SUCCESS",
|
|
710
|
+
canvas: bestCanvas,
|
|
711
|
+
qualityElements: storedQualityElements
|
|
712
|
+
});
|
|
713
|
+
else if (currentCanvas) sendBack({
|
|
714
|
+
type: "DETECTION_SUCCESS",
|
|
715
|
+
canvas: currentCanvas.clone(),
|
|
716
|
+
qualityElements: {}
|
|
717
|
+
});
|
|
718
|
+
},
|
|
719
|
+
onIdTypeChange: (idType$1) => {
|
|
720
|
+
sendBack({
|
|
721
|
+
type: "ID_TYPE_CHANGE",
|
|
722
|
+
idType: idType$1
|
|
723
|
+
});
|
|
724
|
+
},
|
|
725
|
+
onIdSideChange: (side) => {
|
|
726
|
+
if (isCapturing) return;
|
|
727
|
+
sendBack({
|
|
728
|
+
type: "ID_SIDE_CHANGE",
|
|
729
|
+
side
|
|
730
|
+
});
|
|
731
|
+
},
|
|
732
|
+
onCapturingCounterValueChange: (value) => {
|
|
733
|
+
sendBack({
|
|
734
|
+
type: "COUNTER_VALUE_CHANGE",
|
|
735
|
+
value
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
sendBack({
|
|
740
|
+
type: "DETECTION_UPDATE",
|
|
741
|
+
status: "detecting"
|
|
742
|
+
});
|
|
743
|
+
session = new StreamCanvasProcessingSession({
|
|
744
|
+
capturer: input.frameCapturer,
|
|
745
|
+
provider: {
|
|
746
|
+
processFrame: async (frame) => {
|
|
747
|
+
currentCanvas = IncodeCanvas.fromImageData(frame);
|
|
748
|
+
sendBack({
|
|
749
|
+
type: "DETECTION_FRAME",
|
|
750
|
+
frame
|
|
751
|
+
});
|
|
752
|
+
await provider.processFrame(frame);
|
|
753
|
+
},
|
|
754
|
+
reset: () => {
|
|
755
|
+
if (notificationTimeout) {
|
|
756
|
+
clearTimeout(notificationTimeout);
|
|
757
|
+
notificationTimeout = null;
|
|
758
|
+
currentNotificationStatus = null;
|
|
759
|
+
}
|
|
760
|
+
currentCanvas = null;
|
|
761
|
+
bestCanvas = null;
|
|
762
|
+
storedQualityElements = {};
|
|
763
|
+
isCapturing = false;
|
|
764
|
+
provider.reset();
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
onFrame: (frame) => sendBack({
|
|
768
|
+
type: "DETECTION_FRAME",
|
|
769
|
+
frame
|
|
770
|
+
})
|
|
771
|
+
});
|
|
772
|
+
sendBack({
|
|
773
|
+
type: "DETECTION_RESET_READY",
|
|
774
|
+
reset: () => {
|
|
775
|
+
provider.reset();
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
return () => {
|
|
779
|
+
clearNotificationTimeout();
|
|
780
|
+
session?.dispose();
|
|
781
|
+
};
|
|
782
|
+
}),
|
|
783
|
+
uploadIdImage: fromPromise(async ({ input, signal }) => {
|
|
784
|
+
const image = input.canvas.getBase64Image();
|
|
785
|
+
if (!image) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
|
|
786
|
+
return uploadIdImage({
|
|
787
|
+
image,
|
|
788
|
+
type: input.type,
|
|
789
|
+
sendBase64: true,
|
|
790
|
+
glare: input.qualityElements?.glare,
|
|
791
|
+
sharpness: input.qualityElements?.sharpness,
|
|
792
|
+
ageAssurance: false,
|
|
793
|
+
signal
|
|
794
|
+
});
|
|
795
|
+
}),
|
|
796
|
+
processId: fromPromise(async ({ input, signal }) => {
|
|
797
|
+
return processId(input.isSecondId, "", signal);
|
|
798
|
+
}),
|
|
799
|
+
startRecording: fromPromise(async ({ input }) => {
|
|
800
|
+
if (!input.stream) return;
|
|
801
|
+
const type = input.currentMode === "back" ? "backId" : "frontId";
|
|
802
|
+
return startRecordingSession({
|
|
803
|
+
config: input.config,
|
|
804
|
+
stream: input.stream,
|
|
805
|
+
existing: input.existing,
|
|
806
|
+
type
|
|
807
|
+
});
|
|
808
|
+
}),
|
|
809
|
+
checkMotionSensor: fromCallback(({ input, sendBack }) => {
|
|
810
|
+
if (!input.motionProvider) {
|
|
811
|
+
sendBack({
|
|
812
|
+
type: "MOTION_STATUS",
|
|
813
|
+
status: "UNCLEAR"
|
|
814
|
+
});
|
|
815
|
+
return () => {};
|
|
816
|
+
}
|
|
817
|
+
const interval = setInterval(() => {
|
|
818
|
+
sendBack({
|
|
819
|
+
type: "MOTION_STATUS",
|
|
820
|
+
status: input.motionProvider.check()
|
|
821
|
+
});
|
|
822
|
+
}, 500);
|
|
823
|
+
return () => clearInterval(interval);
|
|
824
|
+
})
|
|
825
|
+
},
|
|
826
|
+
actions: {
|
|
827
|
+
stopMediaStream: assign(({ context }) => {
|
|
828
|
+
context.frameCapturer?.dispose();
|
|
829
|
+
if (context.stream) stopStream(context.stream);
|
|
830
|
+
return {
|
|
831
|
+
stream: void 0,
|
|
832
|
+
frameCapturer: void 0
|
|
833
|
+
};
|
|
834
|
+
}),
|
|
835
|
+
disposeProvider: ({ context }) => {
|
|
836
|
+
context.provider?.dispose?.();
|
|
837
|
+
},
|
|
838
|
+
resetForBackCapture: assign(({ context }) => {
|
|
839
|
+
context.frameCapturer?.dispose();
|
|
840
|
+
if (context.stream) stopStream(context.stream);
|
|
841
|
+
context.provider?.reset();
|
|
842
|
+
return {
|
|
843
|
+
stream: void 0,
|
|
844
|
+
frameCapturer: void 0,
|
|
845
|
+
detectionStatus: "idle",
|
|
846
|
+
counterValue: 0,
|
|
847
|
+
orientation: void 0,
|
|
848
|
+
resetDetection: void 0,
|
|
849
|
+
idType: void 0,
|
|
850
|
+
qualityElements: void 0,
|
|
851
|
+
debugFrame: void 0,
|
|
852
|
+
frameRect: void 0,
|
|
853
|
+
previewImageUrl: void 0,
|
|
854
|
+
uploadProgress: void 0,
|
|
855
|
+
manualCaptureTriggered: false
|
|
856
|
+
};
|
|
857
|
+
}),
|
|
858
|
+
prepareForBackCapture: assign(({ context }) => {
|
|
859
|
+
context.provider?.reset();
|
|
860
|
+
return {
|
|
861
|
+
detectionStatus: "idle",
|
|
862
|
+
counterValue: 0,
|
|
863
|
+
orientation: void 0,
|
|
864
|
+
resetDetection: void 0,
|
|
865
|
+
idType: void 0,
|
|
866
|
+
qualityElements: void 0,
|
|
867
|
+
debugFrame: void 0,
|
|
868
|
+
frameRect: void 0,
|
|
869
|
+
previewImageUrl: void 0,
|
|
870
|
+
uploadProgress: void 0,
|
|
871
|
+
manualCaptureTriggered: false
|
|
872
|
+
};
|
|
873
|
+
}),
|
|
874
|
+
setStreamAndCapturer: assign({
|
|
875
|
+
stream: ({ event }) => {
|
|
876
|
+
if ("output" in event) return event.output.stream;
|
|
877
|
+
},
|
|
878
|
+
provider: ({ event }) => {
|
|
879
|
+
if ("output" in event) return event.output.provider;
|
|
880
|
+
},
|
|
881
|
+
frameCapturer: ({ event }) => {
|
|
882
|
+
if ("output" in event) {
|
|
883
|
+
const output = event.output;
|
|
884
|
+
if (output.stream) return new StreamCanvasCapture(output.stream);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}),
|
|
888
|
+
trackTutorialId: () => {
|
|
889
|
+
addEvent({
|
|
890
|
+
code: "tutorialVideoStarted",
|
|
891
|
+
payload: { tutorialFrontID: true }
|
|
892
|
+
});
|
|
893
|
+
},
|
|
894
|
+
trackContinue: () => {},
|
|
895
|
+
resetContext: assign(({ context }) => {
|
|
896
|
+
context.provider?.reset();
|
|
897
|
+
return {
|
|
898
|
+
stream: void 0,
|
|
899
|
+
provider: context.provider,
|
|
900
|
+
frameCapturer: void 0,
|
|
901
|
+
error: void 0,
|
|
902
|
+
detectionStatus: "idle",
|
|
903
|
+
counterValue: 0,
|
|
904
|
+
orientation: void 0,
|
|
905
|
+
capturedImages: {},
|
|
906
|
+
uploadResponse: void 0,
|
|
907
|
+
recordingSession: void 0,
|
|
908
|
+
attemptsRemaining: context.config.captureAttempts,
|
|
909
|
+
uploadError: void 0,
|
|
910
|
+
permissionResult: void 0,
|
|
911
|
+
resetDetection: void 0,
|
|
912
|
+
idType: void 0,
|
|
913
|
+
qualityElements: void 0,
|
|
914
|
+
previewImageUrl: void 0,
|
|
915
|
+
uploadProgress: void 0,
|
|
916
|
+
currentMode: context.config.onlyBack ? "back" : !context.config.enableId && context.config.enablePassport ? "passport" : "front",
|
|
917
|
+
selectedDocumentType: void 0,
|
|
918
|
+
manualCaptureTriggered: false
|
|
919
|
+
};
|
|
920
|
+
}),
|
|
921
|
+
resetDetection: ({ context }) => {
|
|
922
|
+
context.resetDetection?.();
|
|
923
|
+
},
|
|
924
|
+
captureImage: assign({ qualityElements: ({ event }) => {
|
|
925
|
+
if ("qualityElements" in event) return event.qualityElements;
|
|
926
|
+
} }),
|
|
927
|
+
storeCapturedCanvasInProvider: ({ context, event }) => {
|
|
928
|
+
let canvas = null;
|
|
929
|
+
if ("canvas" in event && event.canvas) canvas = event.canvas;
|
|
930
|
+
else canvas = context.frameCapturer?.getLatestCanvas() ?? null;
|
|
931
|
+
if (!canvas || !context.provider) return;
|
|
932
|
+
const canvasWidth = canvas.width();
|
|
933
|
+
const canvasHeight = canvas.height();
|
|
934
|
+
if (!canvasWidth || !canvasHeight) return;
|
|
935
|
+
const originalCanvas = canvas;
|
|
936
|
+
let frameRect;
|
|
937
|
+
if (context.detectionArea) {
|
|
938
|
+
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 1280;
|
|
939
|
+
const viewportHeight = typeof window !== "undefined" ? window.innerHeight : 720;
|
|
940
|
+
const scaleX = viewportWidth / canvasWidth;
|
|
941
|
+
const scaleY = viewportHeight / canvasHeight;
|
|
942
|
+
const scale = Math.max(scaleX, scaleY);
|
|
943
|
+
const displayedWidth = canvasWidth * scale;
|
|
944
|
+
const displayedHeight = canvasHeight * scale;
|
|
945
|
+
const offsetX = (viewportWidth - displayedWidth) / 2;
|
|
946
|
+
const offsetY = (viewportHeight - displayedHeight) / 2;
|
|
947
|
+
frameRect = {
|
|
948
|
+
x: (context.detectionArea.x - offsetX) / scale,
|
|
949
|
+
y: (context.detectionArea.y - offsetY) / scale,
|
|
950
|
+
w: context.detectionArea.width / scale,
|
|
951
|
+
h: context.detectionArea.height / scale
|
|
952
|
+
};
|
|
953
|
+
} else if (context.frameRect) {
|
|
954
|
+
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 1280;
|
|
955
|
+
const viewportHeight = typeof window !== "undefined" ? window.innerHeight : 720;
|
|
956
|
+
const scaleX = viewportWidth / canvasWidth;
|
|
957
|
+
const scaleY = viewportHeight / canvasHeight;
|
|
958
|
+
const scale = Math.max(scaleX, scaleY);
|
|
959
|
+
const displayedWidth = canvasWidth * scale;
|
|
960
|
+
const displayedHeight = canvasHeight * scale;
|
|
961
|
+
const offsetX = (viewportWidth - displayedWidth) / 2;
|
|
962
|
+
const offsetY = (viewportHeight - displayedHeight) / 2;
|
|
963
|
+
frameRect = {
|
|
964
|
+
x: (context.frameRect.x - offsetX) / scale,
|
|
965
|
+
y: (context.frameRect.y - offsetY) / scale,
|
|
966
|
+
w: context.frameRect.w / scale,
|
|
967
|
+
h: context.frameRect.h / scale
|
|
968
|
+
};
|
|
969
|
+
} else {
|
|
970
|
+
const quadValue = (context.provider.getLastProcessResult?.())?.quad;
|
|
971
|
+
const hasQuad = !!quadValue;
|
|
972
|
+
const quadSize = quadValue?.size ? quadValue.size() : quadValue?.length ?? 0;
|
|
973
|
+
if (hasQuad && quadSize >= 4 && quadValue.get) {
|
|
974
|
+
const p0 = quadValue.get(0);
|
|
975
|
+
const p1 = quadValue.get(1);
|
|
976
|
+
const p2 = quadValue.get(2);
|
|
977
|
+
const p3 = quadValue.get(3);
|
|
978
|
+
const minX = Math.min(p0.x, p1.x, p2.x, p3.x);
|
|
979
|
+
const maxX = Math.max(p0.x, p1.x, p2.x, p3.x);
|
|
980
|
+
const minY = Math.min(p0.y, p1.y, p2.y, p3.y);
|
|
981
|
+
const maxY = Math.max(p0.y, p1.y, p2.y, p3.y);
|
|
982
|
+
frameRect = {
|
|
983
|
+
x: minX,
|
|
984
|
+
y: minY,
|
|
985
|
+
w: maxX - minX,
|
|
986
|
+
h: maxY - minY
|
|
987
|
+
};
|
|
988
|
+
} else {
|
|
989
|
+
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 1280;
|
|
990
|
+
const viewportHeight = typeof window !== "undefined" ? window.innerHeight : 720;
|
|
991
|
+
const frameViewportWidth = Math.min(387, viewportWidth * .9);
|
|
992
|
+
const frameViewportHeight = frameViewportWidth / (35 / 22);
|
|
993
|
+
const frameViewportX = (viewportWidth - frameViewportWidth) / 2;
|
|
994
|
+
const frameViewportY = (viewportHeight - frameViewportHeight) / 2;
|
|
995
|
+
frameRect = {
|
|
996
|
+
x: canvasWidth * frameViewportX / viewportWidth,
|
|
997
|
+
y: canvasHeight * frameViewportY / viewportHeight,
|
|
998
|
+
w: canvasWidth * frameViewportWidth / viewportWidth,
|
|
999
|
+
h: canvasHeight * frameViewportHeight / viewportHeight
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
const transformedCanvas = context.provider.transformPerspective(originalCanvas, frameRect);
|
|
1004
|
+
context.provider.setCapturedCanvases(originalCanvas, transformedCanvas);
|
|
1005
|
+
},
|
|
1006
|
+
captureLatestFrame: ({ context }) => {
|
|
1007
|
+
context.frameCapturer?.getLatestCanvas();
|
|
1008
|
+
},
|
|
1009
|
+
clearUploadFailure: assign({
|
|
1010
|
+
uploadError: () => void 0,
|
|
1011
|
+
detectionStatus: () => "idle",
|
|
1012
|
+
previewImageUrl: ({ context }) => {
|
|
1013
|
+
if (context.previewImageUrl) URL.revokeObjectURL(context.previewImageUrl);
|
|
1014
|
+
},
|
|
1015
|
+
uploadProgress: () => void 0
|
|
1016
|
+
}),
|
|
1017
|
+
decrementAttemptsRemaining: assign(({ context }) => ({ attemptsRemaining: context.attemptsRemaining - 1 })),
|
|
1018
|
+
setUploadErrorFromUploadValidation: assign({ uploadError: ({ context }) => {
|
|
1019
|
+
if (!context.uploadResponse) return ID_ERROR_CODES.SERVER;
|
|
1020
|
+
return validateUploadResponse(context.uploadResponse, {
|
|
1021
|
+
skipGlareFront: context.uploadResponse.skipGlareFront,
|
|
1022
|
+
skipGlareBack: context.uploadResponse.skipGlareBack
|
|
1023
|
+
})?.errorKey ?? ID_ERROR_CODES.SERVER;
|
|
1024
|
+
} }),
|
|
1025
|
+
stopMediaRecording: ({ context }) => {
|
|
1026
|
+
if (context.recordingSession) stopRecording(context.recordingSession);
|
|
1027
|
+
},
|
|
1028
|
+
clearRecordingSession: assign({ recordingSession: () => void 0 }),
|
|
1029
|
+
setSelectedDocument: assign({ selectedDocumentType: ({ event }) => {
|
|
1030
|
+
if ("documentType" in event) return event.documentType;
|
|
1031
|
+
} }),
|
|
1032
|
+
setCurrentMode: assign({ currentMode: ({ event, context }) => {
|
|
1033
|
+
if ("documentType" in event) {
|
|
1034
|
+
if (event.documentType === "passport") return "passport";
|
|
1035
|
+
return "front";
|
|
1036
|
+
}
|
|
1037
|
+
if (event.type === "FRONT_COMPLETE") return "back";
|
|
1038
|
+
return context.currentMode;
|
|
1039
|
+
} }),
|
|
1040
|
+
storeCapturedImage: assign({
|
|
1041
|
+
capturedImages: ({ context, event }) => {
|
|
1042
|
+
if (!context.currentMode) return context.capturedImages;
|
|
1043
|
+
const transformedImage = (context.provider?.getCapturedCanvas())?.getBase64Image(1, true);
|
|
1044
|
+
let fallbackImage = "";
|
|
1045
|
+
if ("output" in event) fallbackImage = event.output.originalImage ?? "";
|
|
1046
|
+
const imageData = {
|
|
1047
|
+
imageBase64: transformedImage ?? fallbackImage,
|
|
1048
|
+
blob: new Blob(),
|
|
1049
|
+
url: "",
|
|
1050
|
+
metadata: ""
|
|
1051
|
+
};
|
|
1052
|
+
if (context.currentMode === "front" || context.currentMode === "passport") return {
|
|
1053
|
+
...context.capturedImages,
|
|
1054
|
+
front: imageData
|
|
1055
|
+
};
|
|
1056
|
+
return {
|
|
1057
|
+
...context.capturedImages,
|
|
1058
|
+
back: imageData
|
|
1059
|
+
};
|
|
1060
|
+
},
|
|
1061
|
+
previewImageUrl: ({ context, event }) => {
|
|
1062
|
+
const transformedCanvas = context.provider?.getCapturedCanvas();
|
|
1063
|
+
if (transformedCanvas) {
|
|
1064
|
+
transformedCanvas.updateBlob();
|
|
1065
|
+
const blobData = transformedCanvas.getBlobData();
|
|
1066
|
+
if (blobData?.url) return blobData.url;
|
|
1067
|
+
}
|
|
1068
|
+
if ("canvas" in event && event.canvas) {
|
|
1069
|
+
const canvas = event.canvas;
|
|
1070
|
+
canvas.updateBlob();
|
|
1071
|
+
const blobData = canvas.getBlobData();
|
|
1072
|
+
if (blobData?.url) return blobData.url;
|
|
1073
|
+
}
|
|
1074
|
+
return context.previewImageUrl;
|
|
1075
|
+
}
|
|
1076
|
+
}),
|
|
1077
|
+
setDetectionStatus: assign({ detectionStatus: ({ event, context }) => {
|
|
1078
|
+
if (event.type === "DETECTION_UPDATE") {
|
|
1079
|
+
const newStatus = event.status;
|
|
1080
|
+
const currentStatus = context.detectionStatus;
|
|
1081
|
+
if ((newStatus === "blur" || newStatus === "glare") && (currentStatus === "wrongSide" || currentStatus === "farAway")) return currentStatus;
|
|
1082
|
+
if ((newStatus === "wrongSide" || newStatus === "farAway") && (currentStatus === "blur" || currentStatus === "glare")) return newStatus;
|
|
1083
|
+
return newStatus;
|
|
1084
|
+
}
|
|
1085
|
+
return "idle";
|
|
1086
|
+
} }),
|
|
1087
|
+
setCounterValue: assign({ counterValue: ({ event }) => {
|
|
1088
|
+
if ("value" in event) return event.value;
|
|
1089
|
+
return 0;
|
|
1090
|
+
} }),
|
|
1091
|
+
setIdType: assign({ idType: ({ event }) => {
|
|
1092
|
+
if ("idType" in event) return event.idType;
|
|
1093
|
+
} }),
|
|
1094
|
+
setOrientation: assign({ orientation: ({ event }) => {
|
|
1095
|
+
if ("orientation" in event) return event.orientation;
|
|
1096
|
+
} }),
|
|
1097
|
+
setFrameRect: assign({ frameRect: ({ event }) => {
|
|
1098
|
+
if ("frameRect" in event) return event.frameRect;
|
|
1099
|
+
} }),
|
|
1100
|
+
setDetectionArea: assign({ detectionArea: ({ event }) => {
|
|
1101
|
+
if ("detectionArea" in event) return event.detectionArea;
|
|
1102
|
+
} }),
|
|
1103
|
+
setMotionStatus: assign({ motionStatus: ({ event }) => {
|
|
1104
|
+
if ("status" in event && event.type === "MOTION_STATUS") return event.status;
|
|
1105
|
+
} })
|
|
1106
|
+
},
|
|
1107
|
+
guards: {
|
|
1108
|
+
hasShowTutorial: ({ context }) => context.config.showTutorial,
|
|
1109
|
+
hasShowDocumentChooser: ({ context }) => context.config.showDocumentChooserScreen ?? false,
|
|
1110
|
+
isPermissionGranted: ({ event }) => {
|
|
1111
|
+
if ("output" in event) return event.output === "granted";
|
|
1112
|
+
return false;
|
|
1113
|
+
},
|
|
1114
|
+
isPermissionDeniedError: ({ event }) => {
|
|
1115
|
+
if ("error" in event) {
|
|
1116
|
+
const error = event.error;
|
|
1117
|
+
return error?.name === "NotAllowedError" || error?.name === "PermissionDeniedError";
|
|
1118
|
+
}
|
|
1119
|
+
return false;
|
|
1120
|
+
},
|
|
1121
|
+
hasStream: ({ context }) => context.stream !== void 0,
|
|
1122
|
+
hasAttemptsRemaining: ({ context }) => context.attemptsRemaining > 0,
|
|
1123
|
+
hasCapturedImage: ({ context }) => {
|
|
1124
|
+
return context.provider?.getCapturedCanvas() !== null;
|
|
1125
|
+
},
|
|
1126
|
+
hasUploadValidationError: ({ context }) => {
|
|
1127
|
+
if (!context.uploadResponse) return false;
|
|
1128
|
+
return validateUploadResponse(context.uploadResponse, {
|
|
1129
|
+
skipGlareFront: context.uploadResponse.skipGlareFront,
|
|
1130
|
+
skipGlareBack: context.uploadResponse.skipGlareBack
|
|
1131
|
+
}) !== void 0;
|
|
1132
|
+
},
|
|
1133
|
+
isFrontMode: ({ context }) => context.currentMode === "front" || context.currentMode === "passport",
|
|
1134
|
+
isOnlyFront: ({ context }) => context.config.onlyFront,
|
|
1135
|
+
shouldContinueToBack: ({ context }) => {
|
|
1136
|
+
if (context.currentMode === "passport") return false;
|
|
1137
|
+
if (context.currentMode !== "front") return false;
|
|
1138
|
+
if (context.config.onlyFront) return false;
|
|
1139
|
+
if (context.config.onlyBack) return false;
|
|
1140
|
+
if (!context.config.enableId && context.config.enablePassport) return false;
|
|
1141
|
+
if (context.uploadResponse?.skipBackIdCapture) return false;
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}).createMachine({
|
|
1146
|
+
id: "idCapture",
|
|
1147
|
+
initial: "idle",
|
|
1148
|
+
context: ({ input }) => {
|
|
1149
|
+
const currentMode = input.config.onlyBack ? "back" : !input.config.enableId && input.config.enablePassport ? "passport" : "front";
|
|
1150
|
+
return {
|
|
1151
|
+
config: input.config,
|
|
1152
|
+
currentMode,
|
|
1153
|
+
selectedDocumentType: void 0,
|
|
1154
|
+
stream: void 0,
|
|
1155
|
+
provider: input.provider,
|
|
1156
|
+
frameCapturer: void 0,
|
|
1157
|
+
error: void 0,
|
|
1158
|
+
detectionStatus: "idle",
|
|
1159
|
+
counterValue: 0,
|
|
1160
|
+
orientation: void 0,
|
|
1161
|
+
capturedImages: {},
|
|
1162
|
+
uploadResponse: void 0,
|
|
1163
|
+
recordingSession: void 0,
|
|
1164
|
+
attemptsRemaining: input.config.captureAttempts,
|
|
1165
|
+
uploadError: void 0,
|
|
1166
|
+
permissionResult: void 0,
|
|
1167
|
+
resetDetection: void 0,
|
|
1168
|
+
idType: void 0,
|
|
1169
|
+
qualityElements: void 0,
|
|
1170
|
+
debugFrame: void 0,
|
|
1171
|
+
frameRect: void 0,
|
|
1172
|
+
detectionArea: void 0,
|
|
1173
|
+
previewImageUrl: void 0,
|
|
1174
|
+
uploadProgress: void 0,
|
|
1175
|
+
motionStatus: void 0,
|
|
1176
|
+
manualCaptureTriggered: false
|
|
1177
|
+
};
|
|
1178
|
+
},
|
|
1179
|
+
on: {
|
|
1180
|
+
QUIT: { target: "#idCapture.closed" },
|
|
1181
|
+
UPDATE_DETECTION_AREA: { actions: "setDetectionArea" }
|
|
1182
|
+
},
|
|
1183
|
+
states: {
|
|
1184
|
+
idle: { on: { LOAD: [
|
|
1185
|
+
{
|
|
1186
|
+
target: "chooser",
|
|
1187
|
+
guard: "hasShowDocumentChooser"
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
target: "tutorial",
|
|
1191
|
+
guard: "hasShowTutorial"
|
|
1192
|
+
},
|
|
1193
|
+
{ target: "loading" }
|
|
1194
|
+
] } },
|
|
1195
|
+
chooser: { on: { SELECT_DOCUMENT: [{
|
|
1196
|
+
target: "tutorial",
|
|
1197
|
+
guard: "hasShowTutorial",
|
|
1198
|
+
actions: ["setSelectedDocument", "setCurrentMode"]
|
|
1199
|
+
}, {
|
|
1200
|
+
target: "loading",
|
|
1201
|
+
actions: ["setSelectedDocument", "setCurrentMode"]
|
|
1202
|
+
}] } },
|
|
1203
|
+
loading: { invoke: {
|
|
1204
|
+
id: "checkPermissionLoading",
|
|
1205
|
+
src: "checkPermission",
|
|
1206
|
+
onDone: [{
|
|
1207
|
+
target: "capture",
|
|
1208
|
+
guard: "isPermissionGranted",
|
|
1209
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1210
|
+
}, {
|
|
1211
|
+
target: "permissions",
|
|
1212
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1213
|
+
}],
|
|
1214
|
+
onError: {
|
|
1215
|
+
target: "permissions",
|
|
1216
|
+
actions: assign({ permissionResult: () => "prompt" })
|
|
1217
|
+
}
|
|
1218
|
+
} },
|
|
1219
|
+
tutorial: {
|
|
1220
|
+
initial: "checkingPermission",
|
|
1221
|
+
entry: "trackTutorialId",
|
|
1222
|
+
states: {
|
|
1223
|
+
checkingPermission: {
|
|
1224
|
+
invoke: {
|
|
1225
|
+
id: "checkPermissionTutorial",
|
|
1226
|
+
src: "checkPermission",
|
|
1227
|
+
onDone: [{
|
|
1228
|
+
target: "initializingCamera",
|
|
1229
|
+
guard: "isPermissionGranted",
|
|
1230
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1231
|
+
}, {
|
|
1232
|
+
target: "ready",
|
|
1233
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1234
|
+
}]
|
|
1235
|
+
},
|
|
1236
|
+
on: { NEXT_STEP: {
|
|
1237
|
+
target: "initializingCamera",
|
|
1238
|
+
actions: "trackContinue"
|
|
1239
|
+
} }
|
|
1240
|
+
},
|
|
1241
|
+
initializingCamera: {
|
|
1242
|
+
initial: "booting",
|
|
1243
|
+
invoke: {
|
|
1244
|
+
id: "tutorialInitCamera",
|
|
1245
|
+
src: "initializeCamera",
|
|
1246
|
+
input: ({ context }) => {
|
|
1247
|
+
if (!context.provider) throw new Error("Provider is required");
|
|
1248
|
+
return {
|
|
1249
|
+
provider: context.provider,
|
|
1250
|
+
config: context.config
|
|
1251
|
+
};
|
|
1252
|
+
},
|
|
1253
|
+
onDone: { actions: "setStreamAndCapturer" },
|
|
1254
|
+
onError: [{
|
|
1255
|
+
target: "ready",
|
|
1256
|
+
guard: "isPermissionDeniedError",
|
|
1257
|
+
actions: assign({ permissionResult: () => "denied" })
|
|
1258
|
+
}, {
|
|
1259
|
+
target: "ready",
|
|
1260
|
+
actions: assign({ error: ({ event }) => String(event.error) })
|
|
1261
|
+
}]
|
|
1262
|
+
},
|
|
1263
|
+
states: {
|
|
1264
|
+
booting: {
|
|
1265
|
+
always: [{
|
|
1266
|
+
target: "#tutorialCameraReady",
|
|
1267
|
+
guard: "hasStream"
|
|
1268
|
+
}],
|
|
1269
|
+
on: { NEXT_STEP: {
|
|
1270
|
+
target: "waitingForStream",
|
|
1271
|
+
actions: "trackContinue"
|
|
1272
|
+
} }
|
|
1273
|
+
},
|
|
1274
|
+
waitingForStream: { always: [{
|
|
1275
|
+
target: "#idCapture.capture",
|
|
1276
|
+
guard: "hasStream"
|
|
1277
|
+
}] }
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
cameraReady: {
|
|
1281
|
+
id: "tutorialCameraReady",
|
|
1282
|
+
on: { NEXT_STEP: {
|
|
1283
|
+
target: "#idCapture.capture",
|
|
1284
|
+
actions: "trackContinue"
|
|
1285
|
+
} }
|
|
1286
|
+
},
|
|
1287
|
+
ready: { on: { NEXT_STEP: {
|
|
1288
|
+
target: "waitingForPermission",
|
|
1289
|
+
actions: "trackContinue"
|
|
1290
|
+
} } },
|
|
1291
|
+
waitingForPermission: { invoke: {
|
|
1292
|
+
id: "checkPermissionWaiting",
|
|
1293
|
+
src: "checkPermission",
|
|
1294
|
+
onDone: [{
|
|
1295
|
+
target: "#idCapture.capture",
|
|
1296
|
+
guard: "isPermissionGranted",
|
|
1297
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1298
|
+
}, {
|
|
1299
|
+
target: "#idCapture.permissions",
|
|
1300
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1301
|
+
}]
|
|
1302
|
+
} }
|
|
1303
|
+
}
|
|
1304
|
+
},
|
|
1305
|
+
permissions: {
|
|
1306
|
+
initial: "idle",
|
|
1307
|
+
states: {
|
|
1308
|
+
idle: {
|
|
1309
|
+
invoke: {
|
|
1310
|
+
id: "checkPermissionIdle",
|
|
1311
|
+
src: "checkPermission",
|
|
1312
|
+
onDone: [
|
|
1313
|
+
{
|
|
1314
|
+
target: "#idCapture.capture",
|
|
1315
|
+
guard: "isPermissionGranted",
|
|
1316
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
target: "denied",
|
|
1320
|
+
guard: ({ event }) => event.output === "denied",
|
|
1321
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1322
|
+
},
|
|
1323
|
+
{
|
|
1324
|
+
target: "waitingForUser",
|
|
1325
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1326
|
+
}
|
|
1327
|
+
],
|
|
1328
|
+
onError: {
|
|
1329
|
+
target: "waitingForUser",
|
|
1330
|
+
actions: assign({ permissionResult: () => "prompt" })
|
|
1331
|
+
}
|
|
1332
|
+
},
|
|
1333
|
+
on: {
|
|
1334
|
+
REQUEST_PERMISSION: "requesting",
|
|
1335
|
+
GO_TO_LEARN_MORE: "learnMore"
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
waitingForUser: { on: {
|
|
1339
|
+
REQUEST_PERMISSION: "requesting",
|
|
1340
|
+
GO_TO_LEARN_MORE: "learnMore"
|
|
1341
|
+
} },
|
|
1342
|
+
learnMore: { on: {
|
|
1343
|
+
BACK: "idle",
|
|
1344
|
+
REQUEST_PERMISSION: "requesting"
|
|
1345
|
+
} },
|
|
1346
|
+
requesting: { invoke: {
|
|
1347
|
+
id: "requestPermission",
|
|
1348
|
+
src: "requestPermission",
|
|
1349
|
+
onDone: [
|
|
1350
|
+
{
|
|
1351
|
+
target: "#idCapture.capture",
|
|
1352
|
+
guard: "isPermissionGranted",
|
|
1353
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
target: "denied",
|
|
1357
|
+
guard: ({ event }) => event.output === "denied",
|
|
1358
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
target: "idle",
|
|
1362
|
+
actions: assign({ permissionResult: ({ event }) => event.output })
|
|
1363
|
+
}
|
|
1364
|
+
],
|
|
1365
|
+
onError: { target: "denied" }
|
|
1366
|
+
} },
|
|
1367
|
+
denied: { entry: assign({ permissionResult: () => "refresh" }) }
|
|
1368
|
+
}
|
|
1369
|
+
},
|
|
1370
|
+
capture: {
|
|
1371
|
+
initial: "checkingStream",
|
|
1372
|
+
exit: ["stopMediaRecording", "clearRecordingSession"],
|
|
1373
|
+
on: { SET_FRAME_RECT: { actions: "setFrameRect" } },
|
|
1374
|
+
states: {
|
|
1375
|
+
checkingStream: { always: [{
|
|
1376
|
+
target: "detecting",
|
|
1377
|
+
guard: "hasStream"
|
|
1378
|
+
}, { target: "initializing" }] },
|
|
1379
|
+
initializing: { invoke: {
|
|
1380
|
+
id: "initializeCamera",
|
|
1381
|
+
src: "initializeCamera",
|
|
1382
|
+
input: ({ context }) => {
|
|
1383
|
+
if (!context.provider) throw new Error("Provider is required");
|
|
1384
|
+
return {
|
|
1385
|
+
provider: context.provider,
|
|
1386
|
+
config: context.config
|
|
1387
|
+
};
|
|
1388
|
+
},
|
|
1389
|
+
onDone: {
|
|
1390
|
+
target: "detecting",
|
|
1391
|
+
actions: "setStreamAndCapturer"
|
|
1392
|
+
},
|
|
1393
|
+
onError: [{
|
|
1394
|
+
target: "#idCapture.permissions",
|
|
1395
|
+
guard: "isPermissionDeniedError",
|
|
1396
|
+
actions: assign({ permissionResult: () => "denied" })
|
|
1397
|
+
}, {
|
|
1398
|
+
target: "#idCapture.error",
|
|
1399
|
+
actions: assign({ error: ({ event }) => String(event.error) })
|
|
1400
|
+
}]
|
|
1401
|
+
} },
|
|
1402
|
+
detecting: {
|
|
1403
|
+
always: [{
|
|
1404
|
+
target: "manualCaptureWaiting",
|
|
1405
|
+
guard: ({ context }) => context.manualCaptureTriggered,
|
|
1406
|
+
actions: assign({ detectionStatus: () => "manualCapture" })
|
|
1407
|
+
}],
|
|
1408
|
+
entry: [assign({ detectionStatus: () => "detecting" })],
|
|
1409
|
+
invoke: [{
|
|
1410
|
+
id: "startRecording",
|
|
1411
|
+
src: "startRecording",
|
|
1412
|
+
input: ({ context }) => ({
|
|
1413
|
+
config: context.config,
|
|
1414
|
+
stream: context.stream,
|
|
1415
|
+
existing: context.recordingSession,
|
|
1416
|
+
currentMode: context.currentMode
|
|
1417
|
+
}),
|
|
1418
|
+
onDone: { actions: assign({ recordingSession: ({ context, event }) => {
|
|
1419
|
+
return event.output ?? context.recordingSession;
|
|
1420
|
+
} }) },
|
|
1421
|
+
onError: { actions: () => void 0 }
|
|
1422
|
+
}, {
|
|
1423
|
+
id: "runDetection",
|
|
1424
|
+
src: "runDetection",
|
|
1425
|
+
input: ({ context }) => ({
|
|
1426
|
+
frameCapturer: context.frameCapturer,
|
|
1427
|
+
provider: context.provider,
|
|
1428
|
+
config: context.config,
|
|
1429
|
+
currentMode: context.currentMode,
|
|
1430
|
+
detectionArea: context.detectionArea ?? context.config.detectionArea
|
|
1431
|
+
})
|
|
1432
|
+
}],
|
|
1433
|
+
on: {
|
|
1434
|
+
DETECTION_UPDATE: { actions: "setDetectionStatus" },
|
|
1435
|
+
DETECTION_FRAME: { actions: assign({ debugFrame: ({ event }) => event.frame }) },
|
|
1436
|
+
DETECTION_RESET_READY: { actions: assign({ resetDetection: ({ event }) => event.reset }) },
|
|
1437
|
+
DETECTION_SUCCESS: {
|
|
1438
|
+
target: "capturing",
|
|
1439
|
+
actions: assign({ qualityElements: ({ event }) => event.qualityElements })
|
|
1440
|
+
},
|
|
1441
|
+
MANUAL_CAPTURE: { target: "capturingManual" },
|
|
1442
|
+
SWITCH_TO_MANUAL_CAPTURE: {
|
|
1443
|
+
target: "manualCaptureWaiting",
|
|
1444
|
+
actions: assign({
|
|
1445
|
+
detectionStatus: () => "manualCapture",
|
|
1446
|
+
manualCaptureTriggered: () => true
|
|
1447
|
+
})
|
|
1448
|
+
},
|
|
1449
|
+
COUNTER_VALUE_CHANGE: { actions: "setCounterValue" },
|
|
1450
|
+
ID_TYPE_CHANGE: { actions: "setIdType" },
|
|
1451
|
+
ID_SIDE_CHANGE: { actions: assign({ detectionStatus: ({ event, context }) => {
|
|
1452
|
+
const detectedSide = event.side?.toLowerCase() || "";
|
|
1453
|
+
const currentMode = context.currentMode;
|
|
1454
|
+
if (detectedSide === "wrong") return "wrongSide";
|
|
1455
|
+
const isBackDetected = detectedSide.includes("back") && !detectedSide.includes("front");
|
|
1456
|
+
const isFrontDetected = detectedSide.includes("front") && !detectedSide.includes("back");
|
|
1457
|
+
if (currentMode === "front" && isBackDetected || currentMode === "back" && isFrontDetected) return "wrongSide";
|
|
1458
|
+
if (currentMode === "front" && isFrontDetected || currentMode === "back" && isBackDetected || currentMode === "passport") return "detecting";
|
|
1459
|
+
return "detecting";
|
|
1460
|
+
} }) },
|
|
1461
|
+
ORIENTATION_CHANGE: { actions: "setOrientation" }
|
|
1462
|
+
}
|
|
1463
|
+
},
|
|
1464
|
+
manualCaptureWaiting: { on: { MANUAL_CAPTURE: { target: "capturingManual" } } },
|
|
1465
|
+
capturing: {
|
|
1466
|
+
entry: [
|
|
1467
|
+
"captureImage",
|
|
1468
|
+
"storeCapturedCanvasInProvider",
|
|
1469
|
+
"storeCapturedImage"
|
|
1470
|
+
],
|
|
1471
|
+
always: [{
|
|
1472
|
+
target: "uploading",
|
|
1473
|
+
guard: "hasCapturedImage"
|
|
1474
|
+
}, {
|
|
1475
|
+
target: "uploadError",
|
|
1476
|
+
actions: assign(({ context }) => ({
|
|
1477
|
+
uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
|
|
1478
|
+
attemptsRemaining: context.attemptsRemaining - 1
|
|
1479
|
+
}))
|
|
1480
|
+
}]
|
|
1481
|
+
},
|
|
1482
|
+
capturingManual: {
|
|
1483
|
+
entry: [
|
|
1484
|
+
"captureLatestFrame",
|
|
1485
|
+
"storeCapturedCanvasInProvider",
|
|
1486
|
+
"storeCapturedImage"
|
|
1487
|
+
],
|
|
1488
|
+
always: [{
|
|
1489
|
+
target: "uploading",
|
|
1490
|
+
guard: "hasCapturedImage"
|
|
1491
|
+
}, {
|
|
1492
|
+
target: "uploadError",
|
|
1493
|
+
actions: assign(({ context }) => ({
|
|
1494
|
+
uploadError: ID_ERROR_CODES.UPLOAD_ERROR,
|
|
1495
|
+
attemptsRemaining: context.attemptsRemaining - 1
|
|
1496
|
+
}))
|
|
1497
|
+
}]
|
|
1498
|
+
},
|
|
1499
|
+
uploading: {
|
|
1500
|
+
entry: assign({ uploadProgress: () => 0 }),
|
|
1501
|
+
invoke: {
|
|
1502
|
+
id: "uploadIdImage",
|
|
1503
|
+
src: "uploadIdImage",
|
|
1504
|
+
input: ({ context }) => {
|
|
1505
|
+
const canvas = context.provider?.getOriginalCapturedCanvas();
|
|
1506
|
+
if (!canvas) throw new Error(ID_ERROR_CODES.UPLOAD_ERROR);
|
|
1507
|
+
return {
|
|
1508
|
+
canvas,
|
|
1509
|
+
type: context.currentMode === "back" ? "back" : "front",
|
|
1510
|
+
qualityElements: context.qualityElements
|
|
1511
|
+
};
|
|
1512
|
+
},
|
|
1513
|
+
onDone: {
|
|
1514
|
+
target: "validatingUpload",
|
|
1515
|
+
actions: [assign({
|
|
1516
|
+
uploadResponse: ({ event }) => event.output,
|
|
1517
|
+
uploadProgress: () => 100
|
|
1518
|
+
}), "storeCapturedImage"]
|
|
1519
|
+
},
|
|
1520
|
+
onError: {
|
|
1521
|
+
target: "uploadError",
|
|
1522
|
+
actions: assign(({ context, event }) => ({
|
|
1523
|
+
uploadError: getIdErrorCodeFromUnknown(event.error) ?? ID_ERROR_CODES.UPLOAD_ERROR,
|
|
1524
|
+
attemptsRemaining: context.attemptsRemaining - 1
|
|
1525
|
+
}))
|
|
1526
|
+
}
|
|
1527
|
+
},
|
|
1528
|
+
on: { UPLOAD_PROGRESS: { actions: assign({ uploadProgress: ({ event }) => event.progress }) } }
|
|
1529
|
+
},
|
|
1530
|
+
validatingUpload: { always: [{
|
|
1531
|
+
target: "uploadError",
|
|
1532
|
+
guard: "hasUploadValidationError",
|
|
1533
|
+
actions: ["setUploadErrorFromUploadValidation", "decrementAttemptsRemaining"]
|
|
1534
|
+
}, { target: "success" }] },
|
|
1535
|
+
uploadError: { on: { CONTINUE_FROM_ERROR: [
|
|
1536
|
+
{
|
|
1537
|
+
target: "detecting",
|
|
1538
|
+
guard: "hasAttemptsRemaining",
|
|
1539
|
+
actions: [
|
|
1540
|
+
"resetDetection",
|
|
1541
|
+
"clearUploadFailure",
|
|
1542
|
+
assign({ manualCaptureTriggered: () => false })
|
|
1543
|
+
]
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
target: "#idCapture.frontFinished",
|
|
1547
|
+
guard: "shouldContinueToBack"
|
|
1548
|
+
},
|
|
1549
|
+
{ target: "#idCapture.finished" }
|
|
1550
|
+
] } },
|
|
1551
|
+
success: { on: { NEXT_STEP: [{
|
|
1552
|
+
target: "#idCapture.frontFinished",
|
|
1553
|
+
guard: "shouldContinueToBack"
|
|
1554
|
+
}, { target: "#idCapture.processing" }] } }
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
frontFinished: {
|
|
1558
|
+
entry: ["stopMediaRecording", "prepareForBackCapture"],
|
|
1559
|
+
on: { CONTINUE_TO_BACK: {
|
|
1560
|
+
target: "capture",
|
|
1561
|
+
actions: assign({ currentMode: () => "back" })
|
|
1562
|
+
} }
|
|
1563
|
+
},
|
|
1564
|
+
processing: {
|
|
1565
|
+
entry: "stopMediaStream",
|
|
1566
|
+
invoke: {
|
|
1567
|
+
id: "processId",
|
|
1568
|
+
src: "processId",
|
|
1569
|
+
input: ({ context }) => ({ isSecondId: context.config.isSecondId ?? false }),
|
|
1570
|
+
onDone: [{
|
|
1571
|
+
target: "expired",
|
|
1572
|
+
guard: ({ event }) => event.output.isDocumentExpired
|
|
1573
|
+
}, { target: "finished" }],
|
|
1574
|
+
onError: { target: "finished" }
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
expired: { on: { RETRY_CAPTURE: [
|
|
1578
|
+
{
|
|
1579
|
+
target: "chooser",
|
|
1580
|
+
guard: "hasShowDocumentChooser",
|
|
1581
|
+
actions: ["resetContext"]
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
target: "tutorial",
|
|
1585
|
+
guard: "hasShowTutorial",
|
|
1586
|
+
actions: "resetContext"
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
target: "loading",
|
|
1590
|
+
actions: "resetContext"
|
|
1591
|
+
}
|
|
1592
|
+
] } },
|
|
1593
|
+
finished: {
|
|
1594
|
+
entry: [
|
|
1595
|
+
"stopMediaRecording",
|
|
1596
|
+
"stopMediaStream",
|
|
1597
|
+
"disposeProvider"
|
|
1598
|
+
],
|
|
1599
|
+
type: "final",
|
|
1600
|
+
on: { RESET: {
|
|
1601
|
+
target: "idle",
|
|
1602
|
+
actions: "resetContext"
|
|
1603
|
+
} }
|
|
1604
|
+
},
|
|
1605
|
+
closed: {
|
|
1606
|
+
entry: ["stopMediaStream", "disposeProvider"],
|
|
1607
|
+
type: "final"
|
|
1608
|
+
},
|
|
1609
|
+
error: {
|
|
1610
|
+
entry: ["stopMediaStream", "disposeProvider"],
|
|
1611
|
+
on: { RESET: {
|
|
1612
|
+
target: "idle",
|
|
1613
|
+
actions: "resetContext"
|
|
1614
|
+
} }
|
|
1615
|
+
},
|
|
1616
|
+
manualIdUpload: { on: { QUIT: { target: "closed" } } },
|
|
1617
|
+
digitalIdUpload: { on: { QUIT: { target: "closed" } } }
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
const idCaptureMachine = _idCaptureMachine;
|
|
1621
|
+
|
|
1622
|
+
//#endregion
|
|
1623
|
+
//#region src/modules/id/idCaptureActor.ts
|
|
1624
|
+
function createIdCaptureActor(options) {
|
|
1625
|
+
return createActor(idCaptureMachine, { input: {
|
|
1626
|
+
config: options.config,
|
|
1627
|
+
provider: options.provider
|
|
1628
|
+
} }).start();
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
//#endregion
|
|
1632
|
+
//#region src/modules/id/idCaptureManager.ts
|
|
1633
|
+
function getPermissionStatus(snapshot) {
|
|
1634
|
+
if (!snapshot.matches("permissions")) return;
|
|
1635
|
+
if (snapshot.matches({ permissions: "idle" })) return "idle";
|
|
1636
|
+
if (snapshot.matches({ permissions: "waitingForUser" })) return "idle";
|
|
1637
|
+
if (snapshot.matches({ permissions: "learnMore" })) return "learnMore";
|
|
1638
|
+
if (snapshot.matches({ permissions: "requesting" })) return "requesting";
|
|
1639
|
+
if (snapshot.matches({ permissions: "denied" })) return "denied";
|
|
1640
|
+
}
|
|
1641
|
+
function getCaptureStatus(snapshot) {
|
|
1642
|
+
const matches = {
|
|
1643
|
+
initializing: snapshot.matches({ capture: "initializing" }),
|
|
1644
|
+
detecting: snapshot.matches({ capture: "detecting" }),
|
|
1645
|
+
manualCaptureWaiting: snapshot.matches({ capture: "manualCaptureWaiting" }),
|
|
1646
|
+
capturing: snapshot.matches({ capture: "capturing" }),
|
|
1647
|
+
capturingManual: snapshot.matches({ capture: "capturingManual" }),
|
|
1648
|
+
uploading: snapshot.matches({ capture: "uploading" }),
|
|
1649
|
+
uploadError: snapshot.matches({ capture: "uploadError" }),
|
|
1650
|
+
success: snapshot.matches({ capture: "success" })
|
|
1651
|
+
};
|
|
1652
|
+
if (matches.initializing) return "initializing";
|
|
1653
|
+
if (matches.detecting || matches.manualCaptureWaiting) return "detecting";
|
|
1654
|
+
if (matches.capturing || matches.capturingManual) return "capturing";
|
|
1655
|
+
if (matches.uploading) return "uploading";
|
|
1656
|
+
if (matches.uploadError) return "uploadError";
|
|
1657
|
+
if (matches.success) return "success";
|
|
1658
|
+
}
|
|
1659
|
+
function getErrorMessage(errorCode) {
|
|
1660
|
+
if (!errorCode) return void 0;
|
|
1661
|
+
return {
|
|
1662
|
+
UPLOAD_ERROR: "Upload failed",
|
|
1663
|
+
CLASSIFICATION_FAILED: "ID classification failed",
|
|
1664
|
+
LOW_SHARPNESS: "Image is not sharp enough",
|
|
1665
|
+
GLARE_DETECTED: "Glare detected on ID",
|
|
1666
|
+
WRONG_DOCUMENT_SIDE: "Wrong side of document",
|
|
1667
|
+
ID_TYPE_UNACCEPTABLE: "ID type is not acceptable",
|
|
1668
|
+
READABILITY_ISSUE: "ID readability issue",
|
|
1669
|
+
RETRY_EXHAUSTED_CONTINUE_TO_BACK: "Retry exhausted",
|
|
1670
|
+
RETRY_EXHAUSTED_SKIP_BACK: "Retry exhausted",
|
|
1671
|
+
NO_MORE_TRIES: "No more tries remaining",
|
|
1672
|
+
UNEXPECTED_ERROR: "An unexpected error occurred",
|
|
1673
|
+
NO_TOKEN: "No token available",
|
|
1674
|
+
PERMISSION_DENIED: "Permission denied",
|
|
1675
|
+
USER_CANCELLED: "User cancelled",
|
|
1676
|
+
SERVER_ERROR: "Server error"
|
|
1677
|
+
}[errorCode];
|
|
1678
|
+
}
|
|
1679
|
+
function getErrorDescription(errorCode, uploadResponse) {
|
|
1680
|
+
if (!errorCode) return void 0;
|
|
1681
|
+
return {
|
|
1682
|
+
UPLOAD_ERROR: "Please try again",
|
|
1683
|
+
CLASSIFICATION_FAILED: "Please ensure your ID is clearly visible",
|
|
1684
|
+
LOW_SHARPNESS: "Please ensure the image is clear and well-focused",
|
|
1685
|
+
GLARE_DETECTED: "Please avoid bright reflections on your ID",
|
|
1686
|
+
WRONG_DOCUMENT_SIDE: uploadResponse?.side === "back" ? "Please show the back side of your ID" : "Please show the front side of your ID",
|
|
1687
|
+
ID_TYPE_UNACCEPTABLE: "Please use a valid ID type",
|
|
1688
|
+
READABILITY_ISSUE: "Please ensure all text is clearly visible",
|
|
1689
|
+
RETRY_EXHAUSTED_CONTINUE_TO_BACK: "Continuing to back side capture",
|
|
1690
|
+
RETRY_EXHAUSTED_SKIP_BACK: "Skipping back side capture",
|
|
1691
|
+
NO_MORE_TRIES: "Maximum attempts reached",
|
|
1692
|
+
UNEXPECTED_ERROR: "Please try again later",
|
|
1693
|
+
NO_TOKEN: "Session expired",
|
|
1694
|
+
PERMISSION_DENIED: "Camera permission is required",
|
|
1695
|
+
USER_CANCELLED: "Capture was cancelled",
|
|
1696
|
+
SERVER_ERROR: "Please try again later"
|
|
1697
|
+
}[errorCode];
|
|
1698
|
+
}
|
|
1699
|
+
function mapState(snapshot) {
|
|
1700
|
+
const { context } = snapshot;
|
|
1701
|
+
if (snapshot.matches("idle")) return { status: "idle" };
|
|
1702
|
+
if (snapshot.matches("chooser")) return { status: "chooser" };
|
|
1703
|
+
if (snapshot.matches("loading")) return { status: "loading" };
|
|
1704
|
+
if (snapshot.matches("tutorial")) return {
|
|
1705
|
+
status: "tutorial",
|
|
1706
|
+
selectedDocumentType: context.selectedDocumentType
|
|
1707
|
+
};
|
|
1708
|
+
if (snapshot.matches("closed")) return { status: "closed" };
|
|
1709
|
+
if (snapshot.matches("permissions")) {
|
|
1710
|
+
const permissionStatus = getPermissionStatus(snapshot);
|
|
1711
|
+
if (permissionStatus === void 0) return {
|
|
1712
|
+
status: "permissions",
|
|
1713
|
+
permissionStatus: "idle"
|
|
1714
|
+
};
|
|
1715
|
+
return {
|
|
1716
|
+
status: "permissions",
|
|
1717
|
+
permissionStatus
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
if (snapshot.matches("capture")) {
|
|
1721
|
+
const captureStatus = getCaptureStatus(snapshot);
|
|
1722
|
+
const needsBackCapture = context.currentMode === "front" && !context.config.onlyFront && !context.config.onlyBack;
|
|
1723
|
+
return {
|
|
1724
|
+
status: "capture",
|
|
1725
|
+
captureStatus: captureStatus ?? "initializing",
|
|
1726
|
+
stream: context.stream,
|
|
1727
|
+
detectionStatus: context.detectionStatus,
|
|
1728
|
+
debugFrame: void 0,
|
|
1729
|
+
attemptsRemaining: context.attemptsRemaining,
|
|
1730
|
+
uploadError: context.uploadError,
|
|
1731
|
+
currentMode: context.currentMode,
|
|
1732
|
+
counterValue: context.counterValue,
|
|
1733
|
+
orientation: context.orientation,
|
|
1734
|
+
idType: context.idType,
|
|
1735
|
+
previewImageUrl: context.previewImageUrl,
|
|
1736
|
+
uploadProgress: context.uploadProgress ?? 0,
|
|
1737
|
+
uploadErrorMessage: context.uploadError ? getErrorMessage(context.uploadError) : void 0,
|
|
1738
|
+
uploadErrorDescription: context.uploadError ? getErrorDescription(context.uploadError, context.uploadResponse) : void 0,
|
|
1739
|
+
needsBackCapture,
|
|
1740
|
+
showCaptureButtonInAuto: context.config.showCaptureButtonInAuto ?? false,
|
|
1741
|
+
canRetry: context.attemptsRemaining > 0
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
if (snapshot.matches("frontFinished")) return { status: "frontFinished" };
|
|
1745
|
+
if (snapshot.matches("processing")) return { status: "processing" };
|
|
1746
|
+
if (snapshot.matches("expired")) return { status: "expired" };
|
|
1747
|
+
if (snapshot.matches("finished")) return { status: "finished" };
|
|
1748
|
+
if (snapshot.matches("error")) return {
|
|
1749
|
+
status: "error",
|
|
1750
|
+
error: context.error ?? "Unknown error"
|
|
1751
|
+
};
|
|
1752
|
+
return { status: "idle" };
|
|
1753
|
+
}
|
|
1754
|
+
function createApi({ actor }) {
|
|
1755
|
+
return {
|
|
1756
|
+
load() {
|
|
1757
|
+
actor.send({ type: "LOAD" });
|
|
1758
|
+
},
|
|
1759
|
+
selectDocument(documentType) {
|
|
1760
|
+
actor.send({
|
|
1761
|
+
type: "SELECT_DOCUMENT",
|
|
1762
|
+
documentType
|
|
1763
|
+
});
|
|
1764
|
+
},
|
|
1765
|
+
nextStep() {
|
|
1766
|
+
actor.send({ type: "NEXT_STEP" });
|
|
1767
|
+
},
|
|
1768
|
+
requestPermission() {
|
|
1769
|
+
actor.send({ type: "REQUEST_PERMISSION" });
|
|
1770
|
+
},
|
|
1771
|
+
goToLearnMore() {
|
|
1772
|
+
actor.send({ type: "GO_TO_LEARN_MORE" });
|
|
1773
|
+
},
|
|
1774
|
+
back() {
|
|
1775
|
+
actor.send({ type: "BACK" });
|
|
1776
|
+
},
|
|
1777
|
+
close() {
|
|
1778
|
+
actor.send({ type: "QUIT" });
|
|
1779
|
+
},
|
|
1780
|
+
reset() {
|
|
1781
|
+
actor.send({ type: "RESET" });
|
|
1782
|
+
},
|
|
1783
|
+
retryCapture() {
|
|
1784
|
+
actor.send({ type: "RETRY_CAPTURE" });
|
|
1785
|
+
},
|
|
1786
|
+
continueFromError() {
|
|
1787
|
+
actor.send({ type: "CONTINUE_FROM_ERROR" });
|
|
1788
|
+
},
|
|
1789
|
+
capture() {
|
|
1790
|
+
actor.send({ type: "MANUAL_CAPTURE" });
|
|
1791
|
+
},
|
|
1792
|
+
switchToManualCapture() {
|
|
1793
|
+
actor.send({ type: "SWITCH_TO_MANUAL_CAPTURE" });
|
|
1794
|
+
},
|
|
1795
|
+
continueToBack() {
|
|
1796
|
+
actor.send({ type: "CONTINUE_TO_BACK" });
|
|
1797
|
+
},
|
|
1798
|
+
skipBack() {
|
|
1799
|
+
actor.send({ type: "SKIP_BACK" });
|
|
1800
|
+
},
|
|
1801
|
+
updateDetectionArea(detectionArea) {
|
|
1802
|
+
actor.send({
|
|
1803
|
+
type: "UPDATE_DETECTION_AREA",
|
|
1804
|
+
detectionArea
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
function createIdCaptureManager(options) {
|
|
1810
|
+
return createManager({
|
|
1811
|
+
actor: createIdCaptureActor(options),
|
|
1812
|
+
mapState,
|
|
1813
|
+
createApi
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
//#endregion
|
|
1818
|
+
export { processId as a, stopStream as c, ID_ERROR_CODES as d, initializeIdCapture as i, uploadIdImage as l, createIdCaptureActor as n, startRecordingSession as o, idCaptureMachine as r, stopRecording as s, createIdCaptureManager as t, validateUploadResponse as u };
|