smartcomply-web-sdk 1.0.99 β 1.0.101
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/client/Smartcomply.d.ts +1 -1
- package/dist/client/Smartcomply.d.ts.map +1 -1
- package/dist/client/Smartcomply.js +1 -1
- package/dist/client/Smartcomply.js.map +1 -1
- package/dist/esm/client/Smartcomply.d.ts +1 -1
- package/dist/esm/client/Smartcomply.d.ts.map +1 -1
- package/dist/esm/client/Smartcomply.js +1 -1
- package/dist/esm/client/Smartcomply.js.map +1 -1
- package/dist/esm/flow/DocumentCapture.d.ts +3 -19
- package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/esm/flow/DocumentCapture.js +22 -170
- package/dist/esm/flow/DocumentCapture.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts +0 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +67 -94
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/types/onboarding.d.ts +2 -0
- package/dist/esm/types/onboarding.d.ts.map +1 -1
- package/dist/esm/utils/channels.d.ts +15 -0
- package/dist/esm/utils/channels.d.ts.map +1 -1
- package/dist/esm/utils/channels.js +41 -0
- package/dist/esm/utils/channels.js.map +1 -1
- package/dist/esm/utils/constants.d.ts +9 -8
- package/dist/esm/utils/constants.d.ts.map +1 -1
- package/dist/esm/utils/constants.js +10 -9
- package/dist/esm/utils/constants.js.map +1 -1
- package/dist/esm/utils/documentRules.d.ts +1 -2
- package/dist/esm/utils/documentRules.d.ts.map +1 -1
- package/dist/esm/utils/documentRules.js +1 -2
- package/dist/esm/utils/documentRules.js.map +1 -1
- package/dist/flow/DocumentCapture.d.ts +3 -19
- package/dist/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/flow/DocumentCapture.js +22 -170
- package/dist/flow/DocumentCapture.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts +0 -1
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +65 -92
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/smartcomply.browser.js +94 -256
- package/dist/smartcomply.browser.js.map +3 -3
- package/dist/types/onboarding.d.ts +2 -0
- package/dist/types/onboarding.d.ts.map +1 -1
- package/dist/utils/channels.d.ts +15 -0
- package/dist/utils/channels.d.ts.map +1 -1
- package/dist/utils/channels.js +44 -0
- package/dist/utils/channels.js.map +1 -1
- package/dist/utils/constants.d.ts +9 -8
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +10 -9
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/documentRules.d.ts +1 -2
- package/dist/utils/documentRules.d.ts.map +1 -1
- package/dist/utils/documentRules.js +1 -2
- package/dist/utils/documentRules.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { SmartComply } from "../client/Smartcomply";
|
|
2
2
|
import { getTheme, TYPE, RADIUS } from "./theme";
|
|
3
3
|
import { DocumentCapture, DocumentCaptureCancelledError } from "./DocumentCapture";
|
|
4
|
-
import { isUploadFieldType } from "../utils/channels";
|
|
5
|
-
import { isPdf } from "../utils/documentFiles";
|
|
4
|
+
import { countryCodeFor, countryLabel, filterCountries, isUploadFieldType } from "../utils/channels";
|
|
6
5
|
import { collectDeviceMetadata } from "../utils/device";
|
|
7
6
|
import { requiresBackSide } from "../utils/documentRules";
|
|
8
|
-
import { DEFAULT_CHALLENGE_ACTIONS } from "../utils/constants";
|
|
7
|
+
import { DEFAULT_CHALLENGE_ACTIONS, MAX_DOCUMENT_BYTES } from "../utils/constants";
|
|
9
8
|
import { icon } from "./icons";
|
|
10
9
|
import { renderSpinner, renderInlineSpinner } from "./spinner";
|
|
11
10
|
import { SDKError } from "../errors/SDKError";
|
|
@@ -53,15 +52,9 @@ const STEP_ORDER = [
|
|
|
53
52
|
"liveness",
|
|
54
53
|
"done",
|
|
55
54
|
];
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
ghana: "π¬π Ghana",
|
|
60
|
-
kenya: "π°πͺ Kenya",
|
|
61
|
-
south_africa: "πΏπ¦ South Africa",
|
|
62
|
-
united_states: "πΊπΈ United States",
|
|
63
|
-
united_kingdom: "π¬π§ United Kingdom",
|
|
64
|
-
};
|
|
55
|
+
// From this many countries up, the country screen shows a search box: a
|
|
56
|
+
// setup can offer passports from every supported country (188).
|
|
57
|
+
const COUNTRY_SEARCH_MIN = 7;
|
|
65
58
|
/**
|
|
66
59
|
* Whether a SPECIFIC channel is a document-scan item, vs. a plain
|
|
67
60
|
* number-entry one. channels[country] is a flat list that can mix
|
|
@@ -304,24 +297,6 @@ function _parseOcrDate(raw) {
|
|
|
304
297
|
const d = new Date(s);
|
|
305
298
|
return isNaN(d.getTime()) ? null : d;
|
|
306
299
|
}
|
|
307
|
-
// ββ W12: Image compression before document upload ββββββββββββββββββββββββββββ
|
|
308
|
-
async function _compressImage(blob, maxWidth = 1600, quality = 0.85) {
|
|
309
|
-
try {
|
|
310
|
-
const bmp = await createImageBitmap(blob);
|
|
311
|
-
const scale = Math.min(1, maxWidth / bmp.width);
|
|
312
|
-
const w = Math.round(bmp.width * scale);
|
|
313
|
-
const h = Math.round(bmp.height * scale);
|
|
314
|
-
const canvas = document.createElement("canvas");
|
|
315
|
-
canvas.width = w;
|
|
316
|
-
canvas.height = h;
|
|
317
|
-
canvas.getContext("2d").drawImage(bmp, 0, 0, w, h);
|
|
318
|
-
bmp.close();
|
|
319
|
-
return await new Promise((res, rej) => canvas.toBlob(b => b ? res(b) : rej(new Error("Compression failed")), "image/jpeg", quality));
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
322
|
-
return blob; // fallback to original on any error
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
300
|
// ββ Document type code β human label βββββββββββββββββββββββββββββββββββββββββ
|
|
326
301
|
function _formatDocType(code) {
|
|
327
302
|
const map = {
|
|
@@ -1164,15 +1139,47 @@ export class SmartComplyFlow {
|
|
|
1164
1139
|
subtitle.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin-top:-8px;`;
|
|
1165
1140
|
subtitle.textContent = "Choose the country that issued your ID";
|
|
1166
1141
|
container.appendChild(subtitle);
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1142
|
+
const list = document.createElement("div");
|
|
1143
|
+
list.setAttribute("role", "list");
|
|
1144
|
+
list.style.cssText = "display:flex;flex-direction:column;gap:10px;max-height:min(52vh,420px);overflow-y:auto;padding:2px;";
|
|
1145
|
+
const renderList = (query) => {
|
|
1146
|
+
list.innerHTML = "";
|
|
1147
|
+
const matches = filterCountries(channels, query);
|
|
1148
|
+
if (matches.length === 0) {
|
|
1149
|
+
const empty = document.createElement("div");
|
|
1150
|
+
empty.setAttribute("role", "status");
|
|
1151
|
+
empty.style.cssText = `color:${this.theme.textSecondary};font-size:13px;line-height:1.6;padding:8px 2px;`;
|
|
1152
|
+
empty.textContent = `No country matches "${query.trim()}". Check the spelling or try another name.`;
|
|
1153
|
+
list.appendChild(empty);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
for (const country of matches) {
|
|
1157
|
+
const count = channels[country].data_verification.length
|
|
1158
|
+
+ channels[country].document_verification.length;
|
|
1159
|
+
const card = this.createOptionCard(countryLabel(channels, country), `${count} ID type${count > 1 ? "s" : ""} available`, () => {
|
|
1160
|
+
this.selectedCountry = country;
|
|
1161
|
+
this.showStep("id_type");
|
|
1162
|
+
});
|
|
1163
|
+
card.setAttribute("role", "listitem");
|
|
1164
|
+
list.appendChild(card);
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
if (countries.length >= COUNTRY_SEARCH_MIN) {
|
|
1168
|
+
const search = document.createElement("input");
|
|
1169
|
+
search.type = "search";
|
|
1170
|
+
search.placeholder = "Search countries";
|
|
1171
|
+
search.setAttribute("aria-label", "Search countries");
|
|
1172
|
+
search.autocomplete = "off";
|
|
1173
|
+
search.style.cssText = `
|
|
1174
|
+
width:100%;box-sizing:border-box;padding:11px 14px;border-radius:${RADIUS.md}px;
|
|
1175
|
+
background:${this.theme.cardBg};color:${this.theme.text};
|
|
1176
|
+
border:1.5px solid ${this.theme.border};font-size:14px;outline-offset:2px;
|
|
1177
|
+
`;
|
|
1178
|
+
search.addEventListener("input", () => renderList(search.value));
|
|
1179
|
+
container.appendChild(search);
|
|
1175
1180
|
}
|
|
1181
|
+
renderList("");
|
|
1182
|
+
container.appendChild(list);
|
|
1176
1183
|
}
|
|
1177
1184
|
// ββ ID Type Select ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
1178
1185
|
renderIdTypeSelect(container) {
|
|
@@ -1760,8 +1767,15 @@ export class SmartComplyFlow {
|
|
|
1760
1767
|
faceWrap.appendChild(faceImg);
|
|
1761
1768
|
container.appendChild(faceWrap);
|
|
1762
1769
|
}
|
|
1763
|
-
//
|
|
1764
|
-
if (this.
|
|
1770
|
+
// Expired or expiring-soon notice
|
|
1771
|
+
if (this._ocrIsExpired) {
|
|
1772
|
+
const expired = document.createElement("div");
|
|
1773
|
+
expired.setAttribute("role", "alert");
|
|
1774
|
+
expired.style.cssText = `width:100%;padding:10px 14px;border-radius:10px;background:${this.theme.errorBg};border:1px solid ${this.theme.error}40;color:${this.theme.error};font-size:12px;text-align:left;line-height:1.5;`;
|
|
1775
|
+
expired.textContent = "β οΈ This document has expired. You can continue, but a check with an expired document won't be approved. Use a valid document if you have one.";
|
|
1776
|
+
container.appendChild(expired);
|
|
1777
|
+
}
|
|
1778
|
+
else if (this._ocrExpirySoon) {
|
|
1765
1779
|
const warn = document.createElement("div");
|
|
1766
1780
|
warn.style.cssText = `width:100%;padding:10px 14px;border-radius:10px;background:${this.theme.warning}18;border:1px solid ${this.theme.warning}40;color:${this.theme.warning};font-size:12px;text-align:left;line-height:1.5;`;
|
|
1767
1781
|
warn.textContent = "β οΈ Your document expires soon. Some services may not accept it, but you can still continue.";
|
|
@@ -1864,12 +1878,10 @@ export class SmartComplyFlow {
|
|
|
1864
1878
|
polls++;
|
|
1865
1879
|
try {
|
|
1866
1880
|
const result = await this.sdk.liveness.pollResult(entryId);
|
|
1867
|
-
//
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
return;
|
|
1872
|
-
}
|
|
1881
|
+
// An expired document does not stop the flow: liveness and the face
|
|
1882
|
+
// match still run so the client gets the full result, and the backend
|
|
1883
|
+
// fails the check at the end with "Document has expired." The confirm
|
|
1884
|
+
// screen tells the user (see _ocrIsExpired).
|
|
1873
1885
|
// W8: OCR quality fail β offer retake
|
|
1874
1886
|
if (result.ocr_quality_failed === true) {
|
|
1875
1887
|
container.innerHTML = "";
|
|
@@ -1945,32 +1957,6 @@ export class SmartComplyFlow {
|
|
|
1945
1957
|
// First poll after 3s β give OCR submission time to start
|
|
1946
1958
|
this._ocrPollTimer = setTimeout(poll, 3000);
|
|
1947
1959
|
}
|
|
1948
|
-
_renderExpiredDocument(container) {
|
|
1949
|
-
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
|
|
1950
|
-
const icon = document.createElement("div");
|
|
1951
|
-
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
1952
|
-
icon.textContent = "π
";
|
|
1953
|
-
container.appendChild(icon);
|
|
1954
|
-
const t = document.createElement("h2");
|
|
1955
|
-
t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
|
|
1956
|
-
t.textContent = "Document Expired";
|
|
1957
|
-
container.appendChild(t);
|
|
1958
|
-
const d = document.createElement("p");
|
|
1959
|
-
d.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin:0;max-width:280px;line-height:1.6;`;
|
|
1960
|
-
d.textContent = "The document you submitted has expired. Please use a valid, current ID.";
|
|
1961
|
-
container.appendChild(d);
|
|
1962
|
-
const retryBtn = this.createPrimaryButton("Try a different document");
|
|
1963
|
-
retryBtn.addEventListener("click", () => {
|
|
1964
|
-
this.livenessEntryId = null;
|
|
1965
|
-
this.documentBlob = null;
|
|
1966
|
-
this.documentBackBlob = null;
|
|
1967
|
-
this.showStep("document_capture");
|
|
1968
|
-
});
|
|
1969
|
-
container.appendChild(retryBtn);
|
|
1970
|
-
const endBtn = this.createSecondaryButton("End session");
|
|
1971
|
-
endBtn.addEventListener("click", () => { this.options.onError?.(new Error("Document expired")); this.close(); });
|
|
1972
|
-
container.appendChild(endBtn);
|
|
1973
|
-
}
|
|
1974
1960
|
_renderOcrQualityFail(container, reason) {
|
|
1975
1961
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
|
|
1976
1962
|
const icon = document.createElement("div");
|
|
@@ -2158,15 +2144,11 @@ export class SmartComplyFlow {
|
|
|
2158
2144
|
* Process document capture result with front/back support
|
|
2159
2145
|
*/
|
|
2160
2146
|
async processDocumentCaptureResult(result, captureContainer, container) {
|
|
2161
|
-
//
|
|
2162
|
-
//
|
|
2163
|
-
//
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
this.documentBlob = isPdf(result.front) ? result.front : await _compressImage(result.front, 1600, 0.85);
|
|
2167
|
-
this.documentBackBlob = !result.back
|
|
2168
|
-
? null
|
|
2169
|
-
: isPdf(result.back) ? result.back : await _compressImage(result.back, 1600, 0.85);
|
|
2147
|
+
// Uploaded exactly as captured or picked, photo or PDF: re-encoding a
|
|
2148
|
+
// small passport photo as JPEG blurred its machine-readable lines and the
|
|
2149
|
+
// OCR rejected a clear Rwandan passport as "country: unknown".
|
|
2150
|
+
this.documentBlob = result.front;
|
|
2151
|
+
this.documentBackBlob = result.back || null;
|
|
2170
2152
|
this._documentViaCamera = result.viaCamera;
|
|
2171
2153
|
// Process the front image β pass the already-compressed blob
|
|
2172
2154
|
await this.processDocumentBlob(this.documentBlob, captureContainer, container);
|
|
@@ -2179,12 +2161,11 @@ export class SmartComplyFlow {
|
|
|
2179
2161
|
let retryCount = 0;
|
|
2180
2162
|
const attemptProcessing = async () => {
|
|
2181
2163
|
try {
|
|
2182
|
-
// Validate blob β compression already done in processDocumentCaptureResult (P2)
|
|
2183
2164
|
if (!blob || blob.size === 0) {
|
|
2184
2165
|
throw new Error("Failed to capture document. Please try again.");
|
|
2185
2166
|
}
|
|
2186
|
-
if (blob.size >
|
|
2187
|
-
throw new Error(
|
|
2167
|
+
if (blob.size > MAX_DOCUMENT_BYTES) {
|
|
2168
|
+
throw new Error(`Document is too large. Please use a file smaller than ${Math.round(MAX_DOCUMENT_BYTES / (1024 * 1024))}MB.`);
|
|
2188
2169
|
}
|
|
2189
2170
|
this.documentBlob = blob;
|
|
2190
2171
|
// Show scanning state
|
|
@@ -2203,11 +2184,7 @@ export class SmartComplyFlow {
|
|
|
2203
2184
|
|| this.idNumber
|
|
2204
2185
|
|| this.selectedIdType
|
|
2205
2186
|
|| `DOC-${Date.now()}`;
|
|
2206
|
-
const
|
|
2207
|
-
nigeria: "NG", ghana: "GH", kenya: "KE",
|
|
2208
|
-
south_africa: "ZA", united_states: "US", united_kingdom: "GB", global: "US",
|
|
2209
|
-
};
|
|
2210
|
-
const country = COUNTRY_CODES[this.selectedCountry] || this.selectedCountry.toUpperCase().slice(0, 2);
|
|
2187
|
+
const country = countryCodeFor(this.sdkConfig?.channels || {}, this.selectedCountry);
|
|
2211
2188
|
const deviceMeta = collectDeviceMetadata();
|
|
2212
2189
|
let createResult;
|
|
2213
2190
|
try {
|
|
@@ -2397,11 +2374,7 @@ export class SmartComplyFlow {
|
|
|
2397
2374
|
? ""
|
|
2398
2375
|
: (this.idNumber || Object.values(this.collectedFields)[0] || this.selectedIdType || `DOC-${Date.now()}`);
|
|
2399
2376
|
const identifierType = isDataVerification ? "" : this.selectedIdType;
|
|
2400
|
-
const
|
|
2401
|
-
nigeria: "NG", ghana: "GH", kenya: "KE",
|
|
2402
|
-
south_africa: "ZA", united_states: "US", united_kingdom: "GB", global: "US",
|
|
2403
|
-
};
|
|
2404
|
-
const country = COUNTRY_CODES[this.selectedCountry] || this.selectedCountry.toUpperCase().slice(0, 2);
|
|
2377
|
+
const country = countryCodeFor(this.sdkConfig?.channels || {}, this.selectedCountry);
|
|
2405
2378
|
// Start liveness check (this handles camera, detection, recording, submission)
|
|
2406
2379
|
// If livenessEntryId exists (document flow), reuse it instead of creating a new one
|
|
2407
2380
|
this.sdk.liveness
|