smartcomply-web-sdk 1.0.75 → 1.0.77

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.
@@ -5210,7 +5210,9 @@ var SmartComplySDK = (() => {
5210
5210
  */
5211
5211
  async create(params) {
5212
5212
  const formData = new FormData();
5213
- if (!params.identity_check) {
5213
+ const hasDocument = !!(params.document || params.document_front || params.id_file);
5214
+ if (params.identity_check || !hasDocument && !params.identifier) {
5215
+ } else {
5214
5216
  const rawIdentifier = params.identifier || `ID-${Date.now()}`;
5215
5217
  const safeIdentifier = rawIdentifier.length > 20 ? rawIdentifier.slice(0, 20) : rawIdentifier;
5216
5218
  formData.append("identifier", safeIdentifier);
@@ -7227,10 +7229,13 @@ var SmartComplySDK = (() => {
7227
7229
  this._uiTimeoutId = null;
7228
7230
  // Session expiry
7229
7231
  this.sessionExpiryTimer = null;
7232
+ this.sessionExpiredTimer = null;
7230
7233
  // Status polling (result screen)
7231
7234
  this._pollStatusTimer = null;
7232
7235
  // Components
7233
7236
  this.docCapture = null;
7237
+ // 12 * 5s = 60s grace
7238
+ this._expiryDeferrals = 0;
7234
7239
  this.options = options;
7235
7240
  this.sdk = new SmartComply({
7236
7241
  apiKey: options.apiKey,
@@ -7291,6 +7296,10 @@ var SmartComplySDK = (() => {
7291
7296
  clearTimeout(this.sessionExpiryTimer);
7292
7297
  this.sessionExpiryTimer = null;
7293
7298
  }
7299
+ if (this.sessionExpiredTimer) {
7300
+ clearTimeout(this.sessionExpiredTimer);
7301
+ this.sessionExpiredTimer = null;
7302
+ }
7294
7303
  if (this._pollStatusTimer) {
7295
7304
  clearTimeout(this._pollStatusTimer);
7296
7305
  this._pollStatusTimer = null;
@@ -7448,6 +7457,10 @@ var SmartComplySDK = (() => {
7448
7457
  () => this._showExpiryWarningWhenSafe(),
7449
7458
  warnMs
7450
7459
  );
7460
+ this.sessionExpiredTimer = setTimeout(
7461
+ () => this._enforceSessionExpiry(),
7462
+ Math.max(expiresMs, 0)
7463
+ );
7451
7464
  }
7452
7465
  this.sdkConfig = await this.sdk.initializeConfig();
7453
7466
  if (this.sdkConfig.theme) {
@@ -7475,6 +7488,17 @@ var SmartComplySDK = (() => {
7475
7488
  }
7476
7489
  this.showErrorStep("Your session is about to expire. Please complete verification quickly or restart.");
7477
7490
  }
7491
+ _enforceSessionExpiry() {
7492
+ if (this.isDestroyed) return;
7493
+ const cameraActiveSteps = ["document_capture", "liveness"];
7494
+ if (cameraActiveSteps.includes(this.currentStep) && this._expiryDeferrals < _SmartComplyFlow.MAX_EXPIRY_DEFERRALS) {
7495
+ this._expiryDeferrals += 1;
7496
+ this.sessionExpiredTimer = setTimeout(() => this._enforceSessionExpiry(), 5e3);
7497
+ return;
7498
+ }
7499
+ this.showErrorStep("Your session has expired. Please restart verification.");
7500
+ this.close();
7501
+ }
7478
7502
  // ─────────────────────────────────────────────────────────────────
7479
7503
  // Step rendering
7480
7504
  // ─────────────────────────────────────────────────────────────────
@@ -7579,7 +7603,8 @@ var SmartComplySDK = (() => {
7579
7603
  // ── Welcome ────────────────────────────────────────────────────
7580
7604
  renderWelcome(container) {
7581
7605
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;";
7582
- const isDocFlow = this.isDocumentFlow();
7606
+ const isLivenessOnly = this.isLivenessOnlyFlow();
7607
+ const isDocFlow = isLivenessOnly ? false : this.isDocumentFlow();
7583
7608
  const topRow = document.createElement("div");
7584
7609
  topRow.style.cssText = "display:flex;flex-direction:column;align-items:center;gap:8px;";
7585
7610
  const iconWrap = document.createElement("div");
@@ -7601,7 +7626,7 @@ var SmartComplySDK = (() => {
7601
7626
  color:${this.theme.primary};
7602
7627
  border:1px solid ${this.theme.primary}30;
7603
7628
  `;
7604
- badge.textContent = isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
7629
+ badge.textContent = isLivenessOnly ? "🤳 Liveness Check" : isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
7605
7630
  topRow.appendChild(badge);
7606
7631
  container.appendChild(topRow);
7607
7632
  const titleWrap = document.createElement("div");
@@ -7619,10 +7644,12 @@ var SmartComplySDK = (() => {
7619
7644
  color:${this.theme.textSecondary};font-size:13px;line-height:1.5;
7620
7645
  margin:0 auto;max-width:300px;text-align:center;
7621
7646
  `;
7622
- 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.";
7647
+ 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.";
7623
7648
  titleWrap.appendChild(desc);
7624
7649
  container.appendChild(titleWrap);
7625
- const steps = isDocFlow ? [
7650
+ const steps = isLivenessOnly ? [
7651
+ { icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
7652
+ ] : isDocFlow ? [
7626
7653
  { icon: "🪪", text: "Choose your document type", sub: "Passport, National ID, Driver License" },
7627
7654
  { icon: "📷", text: "Scan your document", sub: "Upload or capture a photo of your ID" },
7628
7655
  { icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
@@ -7671,7 +7698,14 @@ var SmartComplySDK = (() => {
7671
7698
  container.appendChild(stepsWrap);
7672
7699
  const btn = this.createPrimaryButton("Get Started →");
7673
7700
  btn.style.cssText += "margin-top:2px;";
7674
- btn.addEventListener("click", () => this.showStep("country"));
7701
+ btn.addEventListener("click", () => {
7702
+ if (isLivenessOnly) {
7703
+ this.selectedCountry = this.selectedCountry || "global";
7704
+ this.showStep("liveness");
7705
+ } else {
7706
+ this.showStep("country");
7707
+ }
7708
+ });
7675
7709
  container.appendChild(btn);
7676
7710
  const trust = document.createElement("div");
7677
7711
  trust.style.cssText = `
@@ -8962,6 +8996,20 @@ var SmartComplySDK = (() => {
8962
8996
  const allFields = Object.values(channels).flat().flatMap((ch) => ch.fields || []);
8963
8997
  return allFields.some((f2) => isUploadFieldType(f2.type));
8964
8998
  }
8999
+ /**
9000
+ * True when the SDK Config is set to skip data/document verification
9001
+ * entirely and only run the liveness/selfie check — no country picker,
9002
+ * no ID-type picker, no ID-number entry or document capture. Unlike
9003
+ * isDocumentFlow(), there's no channel-shape fallback to infer this from:
9004
+ * a liveness-only config carries no channels at all, so the only signal
9005
+ * is verification_type itself.
9006
+ */
9007
+ isLivenessOnlyFlow() {
9008
+ const vType = this.sdkConfig?.verification_type;
9009
+ if (!vType) return false;
9010
+ const types = Array.isArray(vType) ? vType : [vType];
9011
+ return types.includes("liveness_only");
9012
+ }
8965
9013
  /**
8966
9014
  * The "go back one step" target for a given step, if that step is
8967
9015
  * navigable — shared by the header back button and the browser
@@ -8987,10 +9035,11 @@ var SmartComplySDK = (() => {
8987
9035
  return backTargets[step];
8988
9036
  }
8989
9037
  updateProgress() {
8990
- const isDocFlow = this.isDocumentFlow();
9038
+ const isLivenessOnly = this.isLivenessOnlyFlow();
9039
+ const isDocFlow = isLivenessOnly ? false : this.isDocumentFlow();
8991
9040
  const countries = Object.keys(this.sdkConfig?.channels || {});
8992
9041
  const singleCountry = countries.length <= 1;
8993
- 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));
9042
+ 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));
8994
9043
  const stage2Steps = ["liveness"];
8995
9044
  const allActionSteps = [...stage1Steps, ...stage2Steps];
8996
9045
  const totalSteps = allActionSteps.length;
@@ -9005,7 +9054,7 @@ var SmartComplySDK = (() => {
9005
9054
  } else if (this.currentStep === "done") {
9006
9055
  this.stepLabel.textContent = "Complete ✓";
9007
9056
  } else if (stage2Steps.includes(this.currentStep)) {
9008
- this.stepLabel.textContent = "Stage 2 of 2: Face Check";
9057
+ this.stepLabel.textContent = isLivenessOnly ? "Face Check" : "Stage 2 of 2: Face Check";
9009
9058
  } else if (stage1Steps.includes(this.currentStep)) {
9010
9059
  this.stepLabel.textContent = isDocFlow ? "Stage 1 of 2: Scan Document" : "Stage 1 of 2: Verify Identity";
9011
9060
  } else {
@@ -9085,6 +9134,13 @@ var SmartComplySDK = (() => {
9085
9134
  }
9086
9135
  };
9087
9136
  _SmartComplyFlow.MAX_RETRIES = 3;
9137
+ // Hard stop once the backend session (SessionToken.expires_at, 30 min)
9138
+ // has actually passed. Deferred the same way as the warning above while
9139
+ // a camera is live so we don't tear down an in-progress scan, but capped
9140
+ // — the earlier warning already gave the user 2 minutes' notice, so
9141
+ // there's no reason to keep deferring past a short grace window the way
9142
+ // the advisory warning does.
9143
+ _SmartComplyFlow.MAX_EXPIRY_DEFERRALS = 12;
9088
9144
  var SmartComplyFlow = _SmartComplyFlow;
9089
9145
 
9090
9146
  // src/index.ts