@neofaceid/web-sdk 1.13.0 → 1.13.2

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.
@@ -2288,13 +2288,30 @@ const registerApplication = async (jwtToken, consumerId, applicationData) => {
2288
2288
  };
2289
2289
  const recordFaceVideo = async (options = {}) => {
2290
2290
  ensureSecureContext();
2291
- const { durationMs = 3e3, mimeType = "video/webm", onProgress } = options;
2291
+ const { durationMs = 3e3, onProgress } = options;
2292
+ const getSupportedMimeType = () => {
2293
+ const types = [
2294
+ "video/mp4;codecs=avc1",
2295
+ // iOS Safari (prioritário quando disponível)
2296
+ "video/mp4",
2297
+ // iOS Safari fallback
2298
+ "video/webm;codecs=vp9",
2299
+ // Chrome/Firefox
2300
+ "video/webm;codecs=vp8",
2301
+ // Chrome/Firefox fallback
2302
+ "video/webm"
2303
+ // Android Chrome
2304
+ ];
2305
+ for (const type of types) {
2306
+ if (MediaRecorder.isTypeSupported(type)) return type;
2307
+ }
2308
+ return "";
2309
+ };
2292
2310
  return new Promise(async (resolve, reject) => {
2293
- let stream = null;
2294
- let mediaRecorder = null;
2311
+ let cameraStream = null;
2295
2312
  const chunks = [];
2296
2313
  try {
2297
- stream = await navigator.mediaDevices.getUserMedia({
2314
+ cameraStream = await navigator.mediaDevices.getUserMedia({
2298
2315
  video: {
2299
2316
  facingMode: "user",
2300
2317
  width: { ideal: 640 },
@@ -2302,38 +2319,33 @@ const recordFaceVideo = async (options = {}) => {
2302
2319
  },
2303
2320
  audio: false
2304
2321
  });
2305
- let selectedMimeType = mimeType;
2306
- const mimeTypes = ["video/webm;codecs=vp9", "video/webm;codecs=vp8", "video/webm", "video/mp4"];
2307
- for (const type of mimeTypes) {
2308
- if (MediaRecorder.isTypeSupported(type)) {
2309
- selectedMimeType = type;
2310
- break;
2311
- }
2312
- }
2313
- mediaRecorder = new MediaRecorder(stream, {
2314
- mimeType: selectedMimeType,
2315
- videoBitsPerSecond: 1e6
2316
- // 1 Mbps
2317
- });
2318
- mediaRecorder.ondataavailable = (event) => {
2322
+ const selectedMimeType = getSupportedMimeType();
2323
+ let recorder;
2324
+ try {
2325
+ recorder = selectedMimeType ? new MediaRecorder(cameraStream, { mimeType: selectedMimeType, videoBitsPerSecond: 1e6 }) : new MediaRecorder(cameraStream, { videoBitsPerSecond: 1e6 });
2326
+ } catch {
2327
+ recorder = new MediaRecorder(cameraStream);
2328
+ }
2329
+ const finalMimeType = recorder.mimeType || selectedMimeType;
2330
+ recorder.ondataavailable = (event) => {
2319
2331
  if (event.data.size > 0) {
2320
2332
  chunks.push(event.data);
2321
2333
  }
2322
2334
  };
2323
- mediaRecorder.onstop = () => {
2324
- if (stream) {
2325
- stream.getTracks().forEach((track) => track.stop());
2335
+ recorder.onstop = () => {
2336
+ if (cameraStream) {
2337
+ cameraStream.getTracks().forEach((track) => track.stop());
2326
2338
  }
2327
- const videoBlob = new Blob(chunks, { type: selectedMimeType });
2339
+ const videoBlob = new Blob(chunks, { type: finalMimeType });
2328
2340
  resolve(videoBlob);
2329
2341
  };
2330
- mediaRecorder.onerror = (event) => {
2331
- if (stream) {
2332
- stream.getTracks().forEach((track) => track.stop());
2342
+ recorder.onerror = (event) => {
2343
+ if (cameraStream) {
2344
+ cameraStream.getTracks().forEach((track) => track.stop());
2333
2345
  }
2334
2346
  reject(new NeoFaceError(`Recording error: ${event}`, ErrorType.CAPTURE_ERROR));
2335
2347
  };
2336
- mediaRecorder.start(100);
2348
+ recorder.start(100);
2337
2349
  if (onProgress) {
2338
2350
  const progressInterval = 100;
2339
2351
  let elapsed = 0;
@@ -2347,13 +2359,13 @@ const recordFaceVideo = async (options = {}) => {
2347
2359
  }, progressInterval);
2348
2360
  }
2349
2361
  setTimeout(() => {
2350
- if (mediaRecorder && mediaRecorder.state === "recording") {
2351
- mediaRecorder.stop();
2362
+ if (recorder && recorder.state === "recording") {
2363
+ recorder.stop();
2352
2364
  }
2353
2365
  }, durationMs);
2354
2366
  } catch (error) {
2355
- if (stream) {
2356
- stream.getTracks().forEach((track) => track.stop());
2367
+ if (cameraStream) {
2368
+ cameraStream.getTracks().forEach((track) => track.stop());
2357
2369
  }
2358
2370
  if (error instanceof Error) {
2359
2371
  if (error.name === "NotAllowedError") {
@@ -5321,14 +5333,15 @@ async function captureFaceSilently() {
5321
5333
  video.style.width = "1px";
5322
5334
  video.style.height = "1px";
5323
5335
  video.style.opacity = "0";
5336
+ video.muted = true;
5337
+ video.playsInline = true;
5324
5338
  video.setAttribute("autoplay", "");
5325
- video.setAttribute("muted", "");
5326
5339
  video.setAttribute("playsinline", "");
5327
5340
  const canvas = document.createElement("canvas");
5328
5341
  canvas.style.display = "none";
5329
5342
  let stream = null;
5330
5343
  let faceDetectionAttempts = 0;
5331
- const MAX_FACE_DETECTION_ATTEMPTS = 10;
5344
+ const MAX_FACE_DETECTION_ATTEMPTS = 30;
5332
5345
  const cleanup = () => {
5333
5346
  if (stream) {
5334
5347
  stream.getTracks().forEach((track) => track.stop());
@@ -5342,22 +5355,27 @@ async function captureFaceSilently() {
5342
5355
  };
5343
5356
  document.body.appendChild(video);
5344
5357
  document.body.appendChild(canvas);
5345
- navigator.mediaDevices.getUserMedia({
5346
- video: {
5347
- width: { ideal: 640 },
5348
- height: { ideal: 480 },
5349
- facingMode: "user"
5350
- },
5351
- audio: false
5352
- }).then((mediaStream) => {
5358
+ const tryGetUserMedia = (withConstraints) => {
5359
+ if (withConstraints) {
5360
+ return navigator.mediaDevices.getUserMedia({
5361
+ video: {
5362
+ width: { ideal: 640 },
5363
+ height: { ideal: 480 },
5364
+ facingMode: "user"
5365
+ },
5366
+ audio: false
5367
+ });
5368
+ }
5369
+ return navigator.mediaDevices.getUserMedia({ video: { facingMode: "user" }, audio: false });
5370
+ };
5371
+ tryGetUserMedia(true).catch(() => tryGetUserMedia(false)).then((mediaStream) => {
5353
5372
  stream = mediaStream;
5354
5373
  video.srcObject = stream;
5355
- video.play();
5356
5374
  const tryCapture = () => {
5357
5375
  if (!video.videoWidth || !video.videoHeight) {
5358
5376
  if (faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {
5359
5377
  faceDetectionAttempts++;
5360
- setTimeout(tryCapture, 100);
5378
+ setTimeout(tryCapture, 150);
5361
5379
  return;
5362
5380
  }
5363
5381
  cleanup();
@@ -5387,7 +5405,7 @@ async function captureFaceSilently() {
5387
5405
  detectFace2().then((hasFace) => {
5388
5406
  if (!hasFace && faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {
5389
5407
  faceDetectionAttempts++;
5390
- setTimeout(tryCapture, 100);
5408
+ setTimeout(tryCapture, 150);
5391
5409
  return;
5392
5410
  }
5393
5411
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
@@ -5405,9 +5423,26 @@ async function captureFaceSilently() {
5405
5423
  );
5406
5424
  });
5407
5425
  };
5408
- video.onloadedmetadata = () => {
5409
- setTimeout(tryCapture, CAMERA_READY_DELAY);
5410
- };
5426
+ const playPromise = video.play();
5427
+ if (playPromise !== void 0) {
5428
+ playPromise.then(() => {
5429
+ if (video.videoWidth && video.videoHeight) {
5430
+ setTimeout(tryCapture, CAMERA_READY_DELAY);
5431
+ } else {
5432
+ video.onloadedmetadata = () => setTimeout(tryCapture, CAMERA_READY_DELAY);
5433
+ setTimeout(() => {
5434
+ if (video.videoWidth && video.videoHeight) {
5435
+ tryCapture();
5436
+ }
5437
+ }, 1500);
5438
+ }
5439
+ }).catch((err) => {
5440
+ cleanup();
5441
+ reject(new NeoFaceError("Erro ao reproduzir câmera: " + err.message, ErrorType.CAMERA_ERROR));
5442
+ });
5443
+ } else {
5444
+ video.onloadedmetadata = () => setTimeout(tryCapture, CAMERA_READY_DELAY);
5445
+ }
5411
5446
  video.onerror = () => {
5412
5447
  cleanup();
5413
5448
  reject(new NeoFaceError("Erro ao acessar câmera", ErrorType.CAMERA_ERROR));
@@ -5481,27 +5516,55 @@ async function attemptLogin(applicationToken, overlay, fastMode = false, isRetry
5481
5516
  video.style.width = "1px";
5482
5517
  video.style.height = "1px";
5483
5518
  video.style.opacity = "0";
5519
+ video.muted = true;
5520
+ video.playsInline = true;
5484
5521
  video.setAttribute("autoplay", "");
5485
- video.setAttribute("muted", "");
5486
5522
  video.setAttribute("playsinline", "");
5487
5523
  document.body.appendChild(video);
5488
5524
  let stream = null;
5489
5525
  try {
5490
- stream = await navigator.mediaDevices.getUserMedia({
5491
- video: {
5492
- width: { ideal: 640 },
5493
- height: { ideal: 480 },
5494
- facingMode: "user"
5495
- },
5496
- audio: false
5497
- });
5526
+ try {
5527
+ stream = await navigator.mediaDevices.getUserMedia({
5528
+ video: {
5529
+ width: { ideal: 640 },
5530
+ height: { ideal: 480 },
5531
+ facingMode: "user"
5532
+ },
5533
+ audio: false
5534
+ });
5535
+ } catch {
5536
+ stream = await navigator.mediaDevices.getUserMedia({
5537
+ video: { facingMode: "user" },
5538
+ audio: false
5539
+ });
5540
+ }
5498
5541
  video.srcObject = stream;
5542
+ await new Promise((resolve) => {
5543
+ if (video.readyState >= 1) {
5544
+ resolve();
5545
+ } else {
5546
+ const handler = () => {
5547
+ video.removeEventListener("loadedmetadata", handler);
5548
+ resolve();
5549
+ };
5550
+ video.addEventListener("loadedmetadata", handler);
5551
+ setTimeout(resolve, 2e3);
5552
+ }
5553
+ });
5499
5554
  await video.play();
5500
5555
  await new Promise((resolve) => {
5501
- if (video.readyState >= 2) {
5502
- resolve(void 0);
5556
+ if (video.videoWidth && video.videoHeight) {
5557
+ resolve();
5503
5558
  } else {
5504
- video.onloadedmetadata = () => resolve(void 0);
5559
+ const checkSize = () => {
5560
+ if (video.videoWidth && video.videoHeight) {
5561
+ resolve();
5562
+ } else {
5563
+ setTimeout(checkSize, 100);
5564
+ }
5565
+ };
5566
+ setTimeout(resolve, 3e3);
5567
+ checkSize();
5505
5568
  }
5506
5569
  });
5507
5570
  overlay.updateStatus("detecting");
@@ -5971,8 +6034,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
5971
6034
  initializeBiometricDetection,
5972
6035
  isAdvancedDetectionAvailable
5973
6036
  }, Symbol.toStringTag, { value: "Module" }));
5974
- const VERSION = "1.13.0";
5975
- const RELEASE_DATE = "2026-02-20";
6037
+ const VERSION = "1.13.2";
6038
+ const RELEASE_DATE = "2026-02-21";
5976
6039
  class OnboardingCaptureModal {
5977
6040
  constructor(options) {
5978
6041
  __publicField(this, "overlay", null);
@@ -6023,7 +6086,7 @@ class OnboardingCaptureModal {
6023
6086
  <div class="neofaceid-modal-body">
6024
6087
  <p class="neofaceid-subtitle">${this.subtitle ?? "Vamos capturar sua face e documento"}</p>
6025
6088
  <div class="neofaceid-camera-container">
6026
- <video class="neofaceid-video" autoplay playsinline></video>
6089
+ <video class="neofaceid-video" autoplay muted playsinline></video>
6027
6090
  <canvas class="neofaceid-canvas"></canvas>
6028
6091
  <div class="neofaceid-overlay">
6029
6092
  <div class="neofaceid-frame"></div>
@@ -6074,12 +6137,20 @@ class OnboardingCaptureModal {
6074
6137
  * Inicia a câmera do usuário com resolução adequada.
6075
6138
  */
6076
6139
  async startCamera() {
6077
- const constraints = {
6078
- video: { facingMode: "user", width: { ideal: 1280 }, height: { ideal: 720 } },
6079
- audio: false
6080
- };
6081
- this.stream = await navigator.mediaDevices.getUserMedia(constraints);
6082
6140
  if (!this.video) throw new Error("Elemento de vídeo não encontrado");
6141
+ this.video.muted = true;
6142
+ this.video.playsInline = true;
6143
+ try {
6144
+ this.stream = await navigator.mediaDevices.getUserMedia({
6145
+ video: { facingMode: "user", width: { ideal: 1280 }, height: { ideal: 720 } },
6146
+ audio: false
6147
+ });
6148
+ } catch {
6149
+ this.stream = await navigator.mediaDevices.getUserMedia({
6150
+ video: { facingMode: "user" },
6151
+ audio: false
6152
+ });
6153
+ }
6083
6154
  this.video.srcObject = this.stream;
6084
6155
  await this.video.play();
6085
6156
  }
@@ -6759,7 +6830,7 @@ class DocumentCaptureModal {
6759
6830
 
6760
6831
  <div class="neoface-doc-body">
6761
6832
  <div class="neoface-doc-camera-container">
6762
- <video class="neoface-doc-video" autoplay playsinline></video>
6833
+ <video class="neoface-doc-video" autoplay muted playsinline></video>
6763
6834
  <canvas class="neoface-doc-canvas"></canvas>
6764
6835
 
6765
6836
  <div class="neoface-doc-guide">
@@ -7011,6 +7082,8 @@ class DocumentCaptureModal {
7011
7082
  this.video = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-video");
7012
7083
  this.canvas = (_b = this.overlay) == null ? void 0 : _b.querySelector(".neoface-doc-canvas");
7013
7084
  if (this.video) {
7085
+ this.video.muted = true;
7086
+ this.video.playsInline = true;
7014
7087
  this.video.srcObject = this.stream;
7015
7088
  await this.video.play();
7016
7089
  }