smartcomply-web-sdk 1.0.76 → 1.0.78
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/camera/FaceDetector.d.ts.map +1 -1
- package/dist/camera/FaceDetector.js +42 -17
- package/dist/camera/FaceDetector.js.map +1 -1
- package/dist/esm/camera/FaceDetector.d.ts.map +1 -1
- package/dist/esm/camera/FaceDetector.js +42 -17
- package/dist/esm/camera/FaceDetector.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts +9 -0
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +83 -20
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/esm/modules/liveness/liveness.js +29 -2
- package/dist/esm/modules/liveness/liveness.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts +9 -0
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +83 -20
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/modules/liveness/liveness.js +29 -2
- package/dist/modules/liveness/liveness.js.map +1 -1
- package/dist/smartcomply.browser.js +102 -33
- package/dist/smartcomply.browser.js.map +3 -3
- package/package.json +1 -1
|
@@ -4531,35 +4531,69 @@ var SmartComplySDK = (() => {
|
|
|
4531
4531
|
}, nh.POSE_CONNECTIONS = Dc;
|
|
4532
4532
|
|
|
4533
4533
|
// src/camera/FaceDetector.ts
|
|
4534
|
-
var
|
|
4535
|
-
var
|
|
4536
|
-
var
|
|
4537
|
-
var
|
|
4534
|
+
var MEDIAPIPE_VERSION = "0.10.32";
|
|
4535
|
+
var WASM_CDN = `https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${MEDIAPIPE_VERSION}/wasm`;
|
|
4536
|
+
var MODEL_URL = "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task";
|
|
4537
|
+
var _visionPromise = null;
|
|
4538
|
+
var _modelBufferPromise = null;
|
|
4539
|
+
var INIT_TIMEOUT_MS = 2e4;
|
|
4540
|
+
function withTimeout(promise, ms2, label) {
|
|
4541
|
+
return new Promise((resolve, reject) => {
|
|
4542
|
+
const timer = setTimeout(
|
|
4543
|
+
() => reject(new Error(`${label} timed out. Please check your connection and try again.`)),
|
|
4544
|
+
ms2
|
|
4545
|
+
);
|
|
4546
|
+
promise.then(
|
|
4547
|
+
(value) => {
|
|
4548
|
+
clearTimeout(timer);
|
|
4549
|
+
resolve(value);
|
|
4550
|
+
},
|
|
4551
|
+
(err) => {
|
|
4552
|
+
clearTimeout(timer);
|
|
4553
|
+
reject(err);
|
|
4554
|
+
}
|
|
4555
|
+
);
|
|
4556
|
+
});
|
|
4557
|
+
}
|
|
4538
4558
|
var FaceDetectorEngine = class {
|
|
4539
4559
|
constructor() {
|
|
4540
4560
|
this.landmarker = null;
|
|
4541
4561
|
}
|
|
4542
4562
|
async init() {
|
|
4543
|
-
if (!
|
|
4544
|
-
|
|
4563
|
+
if (!_visionPromise) {
|
|
4564
|
+
_visionPromise = na.forVisionTasks(WASM_CDN);
|
|
4565
|
+
_visionPromise.catch(() => {
|
|
4566
|
+
_visionPromise = null;
|
|
4567
|
+
});
|
|
4545
4568
|
}
|
|
4546
|
-
if (!
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
}
|
|
4551
|
-
|
|
4569
|
+
if (!_modelBufferPromise) {
|
|
4570
|
+
_modelBufferPromise = fetch(MODEL_URL).then((res) => {
|
|
4571
|
+
if (!res.ok) throw new Error(`Failed to download face model (${res.status})`);
|
|
4572
|
+
return res.arrayBuffer();
|
|
4573
|
+
});
|
|
4574
|
+
_modelBufferPromise.catch(() => {
|
|
4575
|
+
_modelBufferPromise = null;
|
|
4576
|
+
});
|
|
4552
4577
|
}
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4578
|
+
const [vision, modelBuffer] = await withTimeout(
|
|
4579
|
+
Promise.all([_visionPromise, _modelBufferPromise]),
|
|
4580
|
+
INIT_TIMEOUT_MS,
|
|
4581
|
+
"Face detection setup"
|
|
4582
|
+
);
|
|
4583
|
+
this.landmarker = await withTimeout(
|
|
4584
|
+
Ic.createFromOptions(vision, {
|
|
4585
|
+
baseOptions: {
|
|
4586
|
+
// Pass a copy — MediaPipe takes ownership of the buffer
|
|
4587
|
+
modelAssetBuffer: new Uint8Array(modelBuffer.slice(0))
|
|
4588
|
+
},
|
|
4589
|
+
runningMode: "VIDEO",
|
|
4590
|
+
outputFaceBlendshapes: true,
|
|
4591
|
+
outputFacialTransformationMatrixes: true,
|
|
4592
|
+
numFaces: 1
|
|
4593
|
+
}),
|
|
4594
|
+
INIT_TIMEOUT_MS,
|
|
4595
|
+
"Face detection setup"
|
|
4596
|
+
);
|
|
4563
4597
|
}
|
|
4564
4598
|
detect(video, timestamp) {
|
|
4565
4599
|
if (!this.landmarker) {
|
|
@@ -5210,7 +5244,9 @@ var SmartComplySDK = (() => {
|
|
|
5210
5244
|
*/
|
|
5211
5245
|
async create(params) {
|
|
5212
5246
|
const formData = new FormData();
|
|
5213
|
-
|
|
5247
|
+
const hasDocument = !!(params.document || params.document_front || params.id_file);
|
|
5248
|
+
if (params.identity_check || !hasDocument && !params.identifier) {
|
|
5249
|
+
} else {
|
|
5214
5250
|
const rawIdentifier = params.identifier || `ID-${Date.now()}`;
|
|
5215
5251
|
const safeIdentifier = rawIdentifier.length > 20 ? rawIdentifier.slice(0, 20) : rawIdentifier;
|
|
5216
5252
|
formData.append("identifier", safeIdentifier);
|
|
@@ -5360,6 +5396,9 @@ var SmartComplySDK = (() => {
|
|
|
5360
5396
|
interrupted.catch(() => {
|
|
5361
5397
|
});
|
|
5362
5398
|
try {
|
|
5399
|
+
const faceInitPromise = faceEngine.init();
|
|
5400
|
+
faceInitPromise.catch(() => {
|
|
5401
|
+
});
|
|
5363
5402
|
let stream;
|
|
5364
5403
|
if (params.prewarmedStream && params.prewarmedStream.active) {
|
|
5365
5404
|
stream = params.prewarmedStream;
|
|
@@ -5385,7 +5424,7 @@ var SmartComplySDK = (() => {
|
|
|
5385
5424
|
tempVideo.style.cssText = "width:100%;border-radius:12px;transform:scaleX(-1);";
|
|
5386
5425
|
container.appendChild(tempVideo);
|
|
5387
5426
|
}
|
|
5388
|
-
await
|
|
5427
|
+
await faceInitPromise;
|
|
5389
5428
|
ui2.updateInstruction("Fit your face inside the oval outline");
|
|
5390
5429
|
const videoForDetection = uiVideo || tempVideo;
|
|
5391
5430
|
await new Promise((resolve) => {
|
|
@@ -6982,7 +7021,12 @@ var SmartComplySDK = (() => {
|
|
|
6982
7021
|
"failed after all retries",
|
|
6983
7022
|
"camera access is not supported",
|
|
6984
7023
|
"camera access failed",
|
|
6985
|
-
"recording failed unexpectedly"
|
|
7024
|
+
"recording failed unexpectedly",
|
|
7025
|
+
// FaceDetectorEngine.init() failures (FaceDetector.ts) — a slow/blocked
|
|
7026
|
+
// CDN or model download is a connectivity issue, not a failed face scan,
|
|
7027
|
+
// and must not burn one of the user's limited liveness retry attempts.
|
|
7028
|
+
"face detection setup timed out",
|
|
7029
|
+
"failed to download face model"
|
|
6986
7030
|
];
|
|
6987
7031
|
function _isInfrastructureError(err) {
|
|
6988
7032
|
const msg = String(err?.message || "").toLowerCase();
|
|
@@ -7601,7 +7645,8 @@ var SmartComplySDK = (() => {
|
|
|
7601
7645
|
// ── Welcome ────────────────────────────────────────────────────
|
|
7602
7646
|
renderWelcome(container) {
|
|
7603
7647
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;";
|
|
7604
|
-
const
|
|
7648
|
+
const isLivenessOnly = this.isLivenessOnlyFlow();
|
|
7649
|
+
const isDocFlow = isLivenessOnly ? false : this.isDocumentFlow();
|
|
7605
7650
|
const topRow = document.createElement("div");
|
|
7606
7651
|
topRow.style.cssText = "display:flex;flex-direction:column;align-items:center;gap:8px;";
|
|
7607
7652
|
const iconWrap = document.createElement("div");
|
|
@@ -7623,7 +7668,7 @@ var SmartComplySDK = (() => {
|
|
|
7623
7668
|
color:${this.theme.primary};
|
|
7624
7669
|
border:1px solid ${this.theme.primary}30;
|
|
7625
7670
|
`;
|
|
7626
|
-
badge.textContent = isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
|
|
7671
|
+
badge.textContent = isLivenessOnly ? "🤳 Liveness Check" : isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
|
|
7627
7672
|
topRow.appendChild(badge);
|
|
7628
7673
|
container.appendChild(topRow);
|
|
7629
7674
|
const titleWrap = document.createElement("div");
|
|
@@ -7641,10 +7686,12 @@ var SmartComplySDK = (() => {
|
|
|
7641
7686
|
color:${this.theme.textSecondary};font-size:13px;line-height:1.5;
|
|
7642
7687
|
margin:0 auto;max-width:300px;text-align:center;
|
|
7643
7688
|
`;
|
|
7644
|
-
desc.textContent = isDocFlow ? "You'll need your document and camera access. This takes about 2 minutes." : "You'll need your ID number and camera access. This takes about 2 minutes.";
|
|
7689
|
+
desc.textContent = isLivenessOnly ? "You'll just need camera access. This takes about 30 seconds." : isDocFlow ? "You'll need your document and camera access. This takes about 2 minutes." : "You'll need your ID number and camera access. This takes about 2 minutes.";
|
|
7645
7690
|
titleWrap.appendChild(desc);
|
|
7646
7691
|
container.appendChild(titleWrap);
|
|
7647
|
-
const steps =
|
|
7692
|
+
const steps = isLivenessOnly ? [
|
|
7693
|
+
{ icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
|
|
7694
|
+
] : isDocFlow ? [
|
|
7648
7695
|
{ icon: "🪪", text: "Choose your document type", sub: "Passport, National ID, Driver License" },
|
|
7649
7696
|
{ icon: "📷", text: "Scan your document", sub: "Upload or capture a photo of your ID" },
|
|
7650
7697
|
{ icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
|
|
@@ -7693,7 +7740,14 @@ var SmartComplySDK = (() => {
|
|
|
7693
7740
|
container.appendChild(stepsWrap);
|
|
7694
7741
|
const btn = this.createPrimaryButton("Get Started →");
|
|
7695
7742
|
btn.style.cssText += "margin-top:2px;";
|
|
7696
|
-
btn.addEventListener("click", () =>
|
|
7743
|
+
btn.addEventListener("click", () => {
|
|
7744
|
+
if (isLivenessOnly) {
|
|
7745
|
+
this.selectedCountry = this.selectedCountry || "global";
|
|
7746
|
+
this.showStep("liveness");
|
|
7747
|
+
} else {
|
|
7748
|
+
this.showStep("country");
|
|
7749
|
+
}
|
|
7750
|
+
});
|
|
7697
7751
|
container.appendChild(btn);
|
|
7698
7752
|
const trust = document.createElement("div");
|
|
7699
7753
|
trust.style.cssText = `
|
|
@@ -8984,6 +9038,20 @@ var SmartComplySDK = (() => {
|
|
|
8984
9038
|
const allFields = Object.values(channels).flat().flatMap((ch) => ch.fields || []);
|
|
8985
9039
|
return allFields.some((f2) => isUploadFieldType(f2.type));
|
|
8986
9040
|
}
|
|
9041
|
+
/**
|
|
9042
|
+
* True when the SDK Config is set to skip data/document verification
|
|
9043
|
+
* entirely and only run the liveness/selfie check — no country picker,
|
|
9044
|
+
* no ID-type picker, no ID-number entry or document capture. Unlike
|
|
9045
|
+
* isDocumentFlow(), there's no channel-shape fallback to infer this from:
|
|
9046
|
+
* a liveness-only config carries no channels at all, so the only signal
|
|
9047
|
+
* is verification_type itself.
|
|
9048
|
+
*/
|
|
9049
|
+
isLivenessOnlyFlow() {
|
|
9050
|
+
const vType = this.sdkConfig?.verification_type;
|
|
9051
|
+
if (!vType) return false;
|
|
9052
|
+
const types = Array.isArray(vType) ? vType : [vType];
|
|
9053
|
+
return types.includes("liveness_only");
|
|
9054
|
+
}
|
|
8987
9055
|
/**
|
|
8988
9056
|
* The "go back one step" target for a given step, if that step is
|
|
8989
9057
|
* navigable — shared by the header back button and the browser
|
|
@@ -9009,10 +9077,11 @@ var SmartComplySDK = (() => {
|
|
|
9009
9077
|
return backTargets[step];
|
|
9010
9078
|
}
|
|
9011
9079
|
updateProgress() {
|
|
9012
|
-
const
|
|
9080
|
+
const isLivenessOnly = this.isLivenessOnlyFlow();
|
|
9081
|
+
const isDocFlow = isLivenessOnly ? false : this.isDocumentFlow();
|
|
9013
9082
|
const countries = Object.keys(this.sdkConfig?.channels || {});
|
|
9014
9083
|
const singleCountry = countries.length <= 1;
|
|
9015
|
-
const stage1Steps = isDocFlow ? ["country", "id_type", "document_capture", "ocr_gate", "document_confirm"].filter((s2) => !(s2 === "country" && singleCountry)) : ["country", "id_type", "id_input", "confirm_identity"].filter((s2) => !(s2 === "country" && singleCountry));
|
|
9084
|
+
const stage1Steps = isLivenessOnly ? [] : isDocFlow ? ["country", "id_type", "document_capture", "ocr_gate", "document_confirm"].filter((s2) => !(s2 === "country" && singleCountry)) : ["country", "id_type", "id_input", "confirm_identity"].filter((s2) => !(s2 === "country" && singleCountry));
|
|
9016
9085
|
const stage2Steps = ["liveness"];
|
|
9017
9086
|
const allActionSteps = [...stage1Steps, ...stage2Steps];
|
|
9018
9087
|
const totalSteps = allActionSteps.length;
|
|
@@ -9027,7 +9096,7 @@ var SmartComplySDK = (() => {
|
|
|
9027
9096
|
} else if (this.currentStep === "done") {
|
|
9028
9097
|
this.stepLabel.textContent = "Complete ✓";
|
|
9029
9098
|
} else if (stage2Steps.includes(this.currentStep)) {
|
|
9030
|
-
this.stepLabel.textContent = "Stage 2 of 2: Face Check";
|
|
9099
|
+
this.stepLabel.textContent = isLivenessOnly ? "Face Check" : "Stage 2 of 2: Face Check";
|
|
9031
9100
|
} else if (stage1Steps.includes(this.currentStep)) {
|
|
9032
9101
|
this.stepLabel.textContent = isDocFlow ? "Stage 1 of 2: Scan Document" : "Stage 1 of 2: Verify Identity";
|
|
9033
9102
|
} else {
|