easyproctor-hml 2.7.14 → 3.0.0

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/README.md CHANGED
@@ -263,6 +263,12 @@ const {
263
263
  token: "...",
264
264
  });
265
265
  ```
266
+
267
+ ## Release Note V 3.0.0
268
+ - Envio do vídeo por partes durante o exame para o proctoring REALTIME
269
+ - Fix: correção do bug do import pacote fix-webm-duration
270
+ - Possibilidade de check automático no check devices através do parametro 'auto'
271
+
266
272
  ## Release Note V 2.7.0
267
273
  - Implementação da comunicação com a API para o proctoring do tipo realtime
268
274
  - Envio do vídeo durante o exame
package/esm/index.js CHANGED
@@ -9340,6 +9340,9 @@ var FaceDetection = class extends BaseDetection {
9340
9340
  if (this.emmitedFaceAlert) {
9341
9341
  this.handleOk("face_stop", "face_detection_on_stream");
9342
9342
  }
9343
+ this.numFacesSent = -1;
9344
+ this.emmitedPositionAlert = false;
9345
+ this.emmitedFaceAlert = false;
9343
9346
  }
9344
9347
  // displayVideoDetections(result: { detections: any; }) {
9345
9348
  // // console.log(result);
@@ -12699,7 +12702,8 @@ var getDefaultProctoringOptions = {
12699
12702
  useSpyScan: false,
12700
12703
  useExternalCamera: false,
12701
12704
  useChallenge: false,
12702
- screenRecorderOptions: { width: 1280, height: 720 }
12705
+ screenRecorderOptions: { width: 1280, height: 720 },
12706
+ auto: false
12703
12707
  };
12704
12708
 
12705
12709
  // src/proctoring/options/ProctoringVideoOptions.ts
@@ -12905,6 +12909,9 @@ function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCa
12905
12909
  console.log("stopRecording Recorder n\xE3o est\xE1 em estado recording");
12906
12910
  resolve();
12907
12911
  }
12912
+ stream.getTracks().forEach((el) => {
12913
+ el.stop();
12914
+ });
12908
12915
  });
12909
12916
  }
12910
12917
  function pauseRecording() {
@@ -13262,56 +13269,6 @@ var ObjectDetection = class extends BaseDetection {
13262
13269
  }
13263
13270
  };
13264
13271
 
13265
- // src/new-flow/recorders/VolumeMeter.ts
13266
- var VolumeMeter = class {
13267
- constructor(stream) {
13268
- this.volume = null;
13269
- this.animationFrameId = null;
13270
- this.stream = stream;
13271
- }
13272
- async start(options = {}) {
13273
- return new Promise((resolve, reject) => {
13274
- try {
13275
- this.audioContext = new AudioContext();
13276
- this.analyser = this.audioContext.createAnalyser();
13277
- this.microphone = this.audioContext.createMediaStreamSource(this.stream);
13278
- this.analyser.smoothingTimeConstant = 0.8;
13279
- this.analyser.fftSize = 1024;
13280
- this.microphone.connect(this.analyser);
13281
- const processAudio = () => {
13282
- const array = new Uint8Array(this.analyser.frequencyBinCount);
13283
- this.analyser.getByteFrequencyData(array);
13284
- const arraySum = array.reduce((a3, value) => a3 + value, 0);
13285
- const average = arraySum / array.length;
13286
- this.setVolume(average);
13287
- options.setVolume && options.setVolume(average);
13288
- this.animationFrameId = requestAnimationFrame(processAudio);
13289
- };
13290
- this.animationFrameId = requestAnimationFrame(processAudio);
13291
- resolve(true);
13292
- } catch (error) {
13293
- this.stop();
13294
- reject(`Error: ${error}`);
13295
- }
13296
- });
13297
- }
13298
- stop() {
13299
- var _a2, _b, _c2;
13300
- if (this.animationFrameId !== null) {
13301
- cancelAnimationFrame(this.animationFrameId);
13302
- }
13303
- (_a2 = this.audioContext) == null ? void 0 : _a2.close();
13304
- (_b = this.microphone) == null ? void 0 : _b.disconnect();
13305
- (_c2 = this.analyser) == null ? void 0 : _c2.disconnect();
13306
- }
13307
- getVolume() {
13308
- return this.volume;
13309
- }
13310
- setVolume(value) {
13311
- this.volume = value;
13312
- }
13313
- };
13314
-
13315
13272
  // src/new-flow/chunk/ChunkStorageService.ts
13316
13273
  var _ChunkStorageService = class _ChunkStorageService {
13317
13274
  constructor() {
@@ -13328,13 +13285,7 @@ var _ChunkStorageService = class _ChunkStorageService {
13328
13285
  _ChunkStorageService.DB_VERSION
13329
13286
  );
13330
13287
  request.onerror = () => {
13331
- var _a2, _b;
13332
- console.error("IndexedDB error:", request.error);
13333
- reject(
13334
- new Error(
13335
- `N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks: ${(_a2 = request.error) == null ? void 0 : _a2.name} - ${(_b = request.error) == null ? void 0 : _b.message}`
13336
- )
13337
- );
13288
+ reject(new Error("N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks."));
13338
13289
  };
13339
13290
  request.onupgradeneeded = () => {
13340
13291
  const db = request.result;
@@ -13557,8 +13508,8 @@ var _ChunkStorageService = class _ChunkStorageService {
13557
13508
  }
13558
13509
  };
13559
13510
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
13560
- /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
13561
- _ChunkStorageService.DB_VERSION = 2;
13511
+ /** v2: índices uploaded numéricos; v3: campo arrayBuffer em vez de blob */
13512
+ _ChunkStorageService.DB_VERSION = 3;
13562
13513
  _ChunkStorageService.STORE_NAME = "chunks";
13563
13514
  var ChunkStorageService = _ChunkStorageService;
13564
13515
 
@@ -13741,8 +13692,9 @@ var BackgroundUploadService = class _BackgroundUploadService {
13741
13692
  let virtualStart = this.totalBytesPurged;
13742
13693
  const chunksWithMeta = allChunks.map((c3) => {
13743
13694
  const start = virtualStart;
13744
- const end = start + c3.blob.size - 1;
13745
- virtualStart += c3.blob.size;
13695
+ const byteLength = c3.arrayBuffer.byteLength;
13696
+ const end = start + byteLength - 1;
13697
+ virtualStart += byteLength;
13746
13698
  return { chunk: c3, start, end };
13747
13699
  });
13748
13700
  let combinedBlobParts = [];
@@ -13752,8 +13704,8 @@ var BackgroundUploadService = class _BackgroundUploadService {
13752
13704
  for (const meta of chunksWithMeta) {
13753
13705
  if (this.currentOffset > meta.end) continue;
13754
13706
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
13755
- const chunkSlice = meta.chunk.blob.slice(sliceStart);
13756
- combinedBlobParts.push(chunkSlice);
13707
+ const sliceBuf = meta.chunk.arrayBuffer.slice(sliceStart);
13708
+ combinedBlobParts.push(new Blob([sliceBuf]));
13757
13709
  lastProcessedChunkId = meta.chunk.id;
13758
13710
  finalChunkIndex = meta.chunk.chunkIndex;
13759
13711
  }
@@ -13787,7 +13739,10 @@ var BackgroundUploadService = class _BackgroundUploadService {
13787
13739
  }
13788
13740
  if (this.config.cleanAfterUpload) {
13789
13741
  const chunksToClear = chunksWithMeta.filter((meta) => meta.chunk.uploaded === 1 || meta.chunk.uploaded === 0 && meta.end < this.currentOffset);
13790
- const sizePurged = chunksToClear.reduce((acc, meta) => acc + meta.chunk.blob.size, 0);
13742
+ const sizePurged = chunksToClear.reduce(
13743
+ (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
13744
+ 0
13745
+ );
13791
13746
  await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13792
13747
  if (sizePurged > 0) {
13793
13748
  this.totalBytesPurged += sizePurged;
@@ -13991,7 +13946,6 @@ var _CameraRecorder = class _CameraRecorder {
13991
13946
  this.currentRetries = 0;
13992
13947
  this.packageCount = 0;
13993
13948
  this.failedUploads = 0;
13994
- this.noiseWait = 20;
13995
13949
  this.options = options;
13996
13950
  this.videoOptions = videoOptions;
13997
13951
  this.backend = backend;
@@ -14413,10 +14367,11 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14413
14367
  const savePromise = (async () => {
14414
14368
  var _a2;
14415
14369
  try {
14370
+ const arrayBuffer = await blob.arrayBuffer();
14416
14371
  await this.chunkStorage.saveChunk({
14417
14372
  proctoringId: this.proctoringId,
14418
14373
  chunkIndex: this.chunkIndex,
14419
- blob,
14374
+ arrayBuffer,
14420
14375
  timestamp: Date.now(),
14421
14376
  uploaded: 0,
14422
14377
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
@@ -14571,35 +14526,36 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14571
14526
  const settings = this.cameraStream.getVideoTracks()[0].getSettings();
14572
14527
  const settingsAudio = this.cameraStream.getAudioTracks()[0].getSettings();
14573
14528
  if (this.options.proctoringType == "VIDEO" || this.options.proctoringType == "REALTIME" || this.options.proctoringType == "IMAGE" && ((_a2 = this.paramsConfig.imageBehaviourParameters) == null ? void 0 : _a2.saveVideo)) {
14529
+ let isUploaded = false;
14574
14530
  if (this.isChunkEnabled) {
14575
- const isStable = await this.checkInternetStability();
14576
- if (isStable) {
14577
- } else {
14578
- if (this.backend && this.backendToken && this.proctoringId) {
14579
- const fileName = `EP_${this.proctoringId}_camera_0.webm`;
14580
- const objectName = `${this.proctoringId}/${fileName}`;
14581
- const isUploaded = await this.backend.checkUpload(this.backendToken, objectName, "video/webm");
14582
- if (isUploaded) {
14583
- this.chunkStorage && await this.chunkStorage.clearAllChunks(session.id);
14584
- return;
14585
- }
14586
- }
14531
+ if (this.backend && this.backendToken && this.proctoringId) {
14532
+ const fileName = `EP_${this.proctoringId}_camera_0.webm`;
14533
+ const objectName = `${this.proctoringId}/${fileName}`;
14534
+ isUploaded = await this.backend.checkUpload(this.backendToken, objectName, "video/webm");
14535
+ this.chunkStorage && await this.chunkStorage.clearAllChunks(session.id);
14587
14536
  }
14588
14537
  }
14589
- const rawBlob = new Blob(this.blobs, {
14590
- type: ((_b = this.recorderOptions) == null ? void 0 : _b.mimeType) || "video/webm"
14591
- });
14592
- const fixedBlob = await fixWebmDuration(rawBlob, this.duration);
14593
- session.addRecording({
14594
- device: `Audio
14538
+ if (!isUploaded) {
14539
+ const rawBlob = new Blob(this.blobs, {
14540
+ type: ((_b = this.recorderOptions) == null ? void 0 : _b.mimeType) || "video/webm"
14541
+ });
14542
+ let finalBlob = rawBlob;
14543
+ if (typeof fixWebmDuration === "function") {
14544
+ finalBlob = await fixWebmDuration(rawBlob, this.duration);
14545
+ } else {
14546
+ console.warn("fixWebmDuration n\xE3o dispon\xEDvel");
14547
+ }
14548
+ session.addRecording({
14549
+ device: `Audio
14595
14550
  Sample Rate: ${settingsAudio.sampleRate}
14596
14551
  Sample Size: ${settingsAudio.sampleSize}
14597
14552
 
14598
14553
  Video:
14599
14554
  ${JSON.stringify(this.recorderOptions)}`,
14600
- arrayBuffer: await fixedBlob.arrayBuffer(),
14601
- origin: "Camera" /* Camera */
14602
- });
14555
+ arrayBuffer: await finalBlob.arrayBuffer(),
14556
+ origin: "Camera" /* Camera */
14557
+ });
14558
+ }
14603
14559
  }
14604
14560
  }
14605
14561
  async getFile(file, name, type) {
@@ -14612,51 +14568,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14612
14568
  });
14613
14569
  });
14614
14570
  }
14615
- /**
14616
- * Verifica se a internet está estável para realizar o upload do vídeo na íntegra.
14617
- */
14618
- async checkInternetStability() {
14619
- var _a2;
14620
- if (!navigator.onLine) return false;
14621
- try {
14622
- const controller = new AbortController();
14623
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
14624
- const baseUrl = (_a2 = this.backend) == null ? void 0 : _a2.getBaseUrl();
14625
- if (!baseUrl) return true;
14626
- const response = await fetch(`${baseUrl}/Client/health`, {
14627
- method: "GET",
14628
- signal: controller.signal
14629
- });
14630
- clearTimeout(timeoutId);
14631
- return response.status < 500;
14632
- } catch (e3) {
14633
- console.warn("[CameraRecorder] Internet inst\xE1vel ou lenta detectada para upload integral.");
14634
- return false;
14635
- }
14636
- }
14637
- onNoiseDetected() {
14638
- var _a2, _b, _c2;
14639
- if (this.options.proctoringType === "REALTIME") return;
14640
- if (!this.volumeMeter && this.cameraStream) {
14641
- this.volumeMeter = new VolumeMeter(this.cameraStream);
14642
- this.volumeMeter.start().catch((e3) => {
14643
- console.log(e3);
14644
- this.volumeMeter = void 0;
14645
- });
14646
- }
14647
- const volume = (_b = (_a2 = this.volumeMeter) == null ? void 0 : _a2.getVolume()) != null ? _b : 0;
14648
- if (volume >= (((_c2 = this.paramsConfig.audioBehaviourParameters) == null ? void 0 : _c2.noiseLimit) || 40)) {
14649
- if (this.noiseWait >= 20) {
14650
- this.options.onRealtimeAlertsCallback({
14651
- status: "ALERT",
14652
- description: "Barulho detectado",
14653
- type: "audio_detection_on_stream"
14654
- });
14655
- this.noiseWait = 0;
14656
- }
14657
- }
14658
- this.noiseWait++;
14659
- }
14660
14571
  };
14661
14572
  // ========================
14662
14573
  // Chunk & Lifecycle
@@ -14667,10 +14578,62 @@ _CameraRecorder.CHUNK_TIMESLICE_MS = 6e4;
14667
14578
  _CameraRecorder.LS_SESSION_KEY = "ep_proctoring_session";
14668
14579
  var CameraRecorder = _CameraRecorder;
14669
14580
 
14581
+ // src/new-flow/recorders/VolumeMeter.ts
14582
+ var VolumeMeter = class {
14583
+ constructor(stream) {
14584
+ this.volume = null;
14585
+ this.animationFrameId = null;
14586
+ this.stream = stream;
14587
+ }
14588
+ async start(options = {}) {
14589
+ return new Promise((resolve, reject) => {
14590
+ try {
14591
+ this.audioContext = new AudioContext();
14592
+ this.analyser = this.audioContext.createAnalyser();
14593
+ this.microphone = this.audioContext.createMediaStreamSource(this.stream);
14594
+ this.analyser.smoothingTimeConstant = 0.8;
14595
+ this.analyser.fftSize = 1024;
14596
+ this.microphone.connect(this.analyser);
14597
+ const processAudio = () => {
14598
+ const array = new Uint8Array(this.analyser.frequencyBinCount);
14599
+ this.analyser.getByteFrequencyData(array);
14600
+ const arraySum = array.reduce((a3, value) => a3 + value, 0);
14601
+ const average = arraySum / array.length;
14602
+ this.setVolume(average);
14603
+ options.setVolume && options.setVolume(average);
14604
+ this.animationFrameId = requestAnimationFrame(processAudio);
14605
+ };
14606
+ this.animationFrameId = requestAnimationFrame(processAudio);
14607
+ resolve(true);
14608
+ } catch (error) {
14609
+ this.stop();
14610
+ reject(`Error: ${error}`);
14611
+ }
14612
+ });
14613
+ }
14614
+ stop() {
14615
+ var _a2, _b, _c2;
14616
+ if (this.animationFrameId !== null) {
14617
+ cancelAnimationFrame(this.animationFrameId);
14618
+ }
14619
+ (_a2 = this.audioContext) == null ? void 0 : _a2.close();
14620
+ (_b = this.microphone) == null ? void 0 : _b.disconnect();
14621
+ (_c2 = this.analyser) == null ? void 0 : _c2.disconnect();
14622
+ }
14623
+ getVolume() {
14624
+ return this.volume;
14625
+ }
14626
+ setVolume(value) {
14627
+ this.volume = value;
14628
+ }
14629
+ };
14630
+
14670
14631
  // src/new-flow/checkers/DeviceCheckerUI.ts
14671
14632
  var DeviceCheckerUI = class {
14672
14633
  constructor(options = getDefaultProctoringOptions, _videoOptions) {
14673
14634
  this.videoOptions = { width: 1080, height: 720, minWidth: 0, minHeight: 0 };
14635
+ this.autoConfirmTimer = null;
14636
+ this.autoConfirmSeconds = 5;
14674
14637
  this.options = {
14675
14638
  ...getDefaultProctoringOptions,
14676
14639
  ...options,
@@ -15436,9 +15399,53 @@ var DeviceCheckerUI = class {
15436
15399
  }));
15437
15400
  }
15438
15401
  closeModal() {
15402
+ this.stopAutoConfirm();
15439
15403
  const checkDevices = document.querySelector("#checkDevices");
15440
15404
  checkDevices == null ? void 0 : checkDevices.remove();
15441
15405
  }
15406
+ verifyAutoConfirm(status) {
15407
+ var _a2;
15408
+ if (!((_a2 = this.options) == null ? void 0 : _a2.auto)) return;
15409
+ let isAllGreen = status.allowedResolution && status.allowedPositionFace && status.allowedAmbient && status.allowedMicrophone;
15410
+ if (this.options.useSpyScan) {
15411
+ isAllGreen = isAllGreen && status.allowedSpyScan === true;
15412
+ }
15413
+ if (isAllGreen) {
15414
+ if (!this.autoConfirmTimer) {
15415
+ this.startAutoConfirm();
15416
+ }
15417
+ } else {
15418
+ if (this.autoConfirmTimer) {
15419
+ this.stopAutoConfirm();
15420
+ }
15421
+ }
15422
+ }
15423
+ startAutoConfirm() {
15424
+ this.autoConfirmSeconds = 5;
15425
+ const btn = document.getElementById("confirmBtn");
15426
+ if (btn && !btn.disabled) {
15427
+ btn.innerText = `Continuando em ${this.autoConfirmSeconds}s...`;
15428
+ this.autoConfirmTimer = setInterval(() => {
15429
+ this.autoConfirmSeconds--;
15430
+ if (this.autoConfirmSeconds <= 0) {
15431
+ this.stopAutoConfirm();
15432
+ btn.click();
15433
+ } else {
15434
+ btn.innerText = `Continuando em ${this.autoConfirmSeconds}s...`;
15435
+ }
15436
+ }, 1e3);
15437
+ }
15438
+ }
15439
+ stopAutoConfirm() {
15440
+ if (this.autoConfirmTimer) {
15441
+ clearInterval(this.autoConfirmTimer);
15442
+ this.autoConfirmTimer = null;
15443
+ }
15444
+ const btn = document.getElementById("confirmBtn");
15445
+ if (btn) {
15446
+ btn.innerText = "Continuar";
15447
+ }
15448
+ }
15442
15449
  modalActions(closeCheckDevices) {
15443
15450
  const cancelBtn = document.getElementById("cancelBtn");
15444
15451
  const confirmBtn = document.getElementById("confirmBtn");
@@ -15805,6 +15812,7 @@ var _DeviceCheckerService = class _DeviceCheckerService {
15805
15812
  }
15806
15813
  }
15807
15814
  onUpdateCallback() {
15815
+ var _a2;
15808
15816
  if (typeof this.onUpdateCb === "function") {
15809
15817
  this.onUpdateCb({
15810
15818
  allowedResolution: this.allowedResolution,
@@ -15815,6 +15823,15 @@ var _DeviceCheckerService = class _DeviceCheckerService {
15815
15823
  faceDetectionAlerts: this.faceDetectionAlerts
15816
15824
  });
15817
15825
  }
15826
+ if (((_a2 = this.options) == null ? void 0 : _a2.auto) && this.DeviceCheckerUI) {
15827
+ this.DeviceCheckerUI.verifyAutoConfirm({
15828
+ allowedResolution: this.allowedResolution,
15829
+ allowedPositionFace: this.allowedPositionFace,
15830
+ allowedAmbient: this.allowedAmbient,
15831
+ allowedMicrophone: this.allowedMicrophone,
15832
+ allowedSpyScan: this.allowedSpyScan
15833
+ });
15834
+ }
15818
15835
  }
15819
15836
  async checkDevices(options = getDefaultProctoringOptions, _videoOptions = getDefaultProctoringVideoOptions) {
15820
15837
  var _a2;
@@ -16021,16 +16038,19 @@ var _DeviceCheckerService = class _DeviceCheckerService {
16021
16038
  }).finally(() => {
16022
16039
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
16023
16040
  this.DeviceCheckerUI && this.allowedSpyScan != null && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
16041
+ this.onUpdateCallback();
16024
16042
  });
16025
16043
  } else {
16026
16044
  this.allowedSpyScan = false;
16027
16045
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
16028
16046
  this.DeviceCheckerUI && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
16047
+ this.onUpdateCallback();
16029
16048
  }
16030
16049
  } catch (error) {
16031
16050
  this.allowedSpyScan = false;
16032
16051
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
16033
16052
  this.DeviceCheckerUI && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
16053
+ this.onUpdateCallback();
16034
16054
  console.log(error);
16035
16055
  }
16036
16056
  }
@@ -16350,8 +16370,8 @@ var CapturePhoto = class {
16350
16370
  }
16351
16371
  };
16352
16372
 
16353
- // src/extension/extension.ts
16354
- var Extension = class {
16373
+ // src/extension/extensionEasyProctor.ts
16374
+ var ExtensionEasyProctor = class {
16355
16375
  constructor() {
16356
16376
  this.hasExtension = false;
16357
16377
  this.tryes = 0;
@@ -16396,6 +16416,89 @@ var Extension = class {
16396
16416
  }
16397
16417
  };
16398
16418
 
16419
+ // src/extension/extensionEasyCatcher.ts
16420
+ var ExtensionEasyCatcher = class {
16421
+ constructor(options) {
16422
+ this.hasExtension = false;
16423
+ this.tryes = 0;
16424
+ this.responseStart = false;
16425
+ this.options = options || {};
16426
+ }
16427
+ /**
16428
+ * Verifica se a extensão está instalada e ativa.
16429
+ * Retorna o número da versão se encontrada, ou lança erro após timeout.
16430
+ */
16431
+ checkExtensionInstalled(timeoutMs = 2e3) {
16432
+ return new Promise((resolve, reject) => {
16433
+ let handled = false;
16434
+ const handler = (event) => {
16435
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "version") {
16436
+ handled = true;
16437
+ window.removeEventListener("message", handler);
16438
+ resolve(event.data.message);
16439
+ }
16440
+ };
16441
+ window.addEventListener("message", handler);
16442
+ window.postMessage({
16443
+ type: "easycatcher",
16444
+ func: "verifyExtensionEasycatcher"
16445
+ }, "*");
16446
+ setTimeout(() => {
16447
+ if (!handled) {
16448
+ window.removeEventListener("message", handler);
16449
+ reject(new Error("Extens\xE3o n\xE3o detectada ou n\xE3o respondeu."));
16450
+ }
16451
+ }, timeoutMs);
16452
+ });
16453
+ }
16454
+ /**
16455
+ * Solicita o JSON da sessão atual capturado pela extensão.
16456
+ */
16457
+ getSessionData(timeoutMs = 5e3) {
16458
+ return new Promise((resolve, reject) => {
16459
+ let handled = false;
16460
+ const handler = (event) => {
16461
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "data_response") {
16462
+ handled = true;
16463
+ window.removeEventListener("message", handler);
16464
+ resolve(event.data.payload);
16465
+ }
16466
+ };
16467
+ window.addEventListener("message", handler);
16468
+ window.postMessage({
16469
+ type: "easycatcher",
16470
+ func: "getDataExtensionEasycatcher"
16471
+ }, "*");
16472
+ setTimeout(() => {
16473
+ if (!handled) {
16474
+ window.removeEventListener("message", handler);
16475
+ reject(new Error("Timeout ao aguardar dados da extens\xE3o."));
16476
+ }
16477
+ }, timeoutMs);
16478
+ });
16479
+ }
16480
+ start() {
16481
+ return new Promise((resolve, reject) => {
16482
+ let handled = false;
16483
+ const handler = (event) => {
16484
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "started_confirmed") {
16485
+ handled = true;
16486
+ window.removeEventListener("message", handler);
16487
+ resolve(true);
16488
+ }
16489
+ };
16490
+ window.addEventListener("message", handler);
16491
+ window.postMessage({ type: "easycatcher", func: "startExtensionEasycatcher" }, "*");
16492
+ setTimeout(() => {
16493
+ if (!handled) {
16494
+ window.removeEventListener("message", handler);
16495
+ reject(new Error("Timeout: Extens\xE3o n\xE3o confirmou o in\xEDcio."));
16496
+ }
16497
+ }, 3e3);
16498
+ });
16499
+ }
16500
+ };
16501
+
16399
16502
  // src/modules/onChangeDevices.ts
16400
16503
  var onChangeDevices = class {
16401
16504
  constructor(repositoryDevices, proctoringId2, sessionOptions, allRecorders) {
@@ -23571,7 +23674,10 @@ var Proctoring = class {
23571
23674
  if (this.context.token === void 0) {
23572
23675
  throw TOKEN_MISSING;
23573
23676
  }
23574
- this.extension = new Extension();
23677
+ if (options.useChallenge) {
23678
+ this.extensionEasycatcher = new ExtensionEasyCatcher();
23679
+ }
23680
+ this.extension = new ExtensionEasyProctor();
23575
23681
  this.extension.addEventListener();
23576
23682
  const baseURL = this.backend.selectBaseUrl(this.context.type);
23577
23683
  const devices = await enumarateDevices();
@@ -23639,20 +23745,6 @@ var Proctoring = class {
23639
23745
  } catch (error) {
23640
23746
  throw SAFE_BROWSER_API_NOT_FOUND;
23641
23747
  }
23642
- this.allRecorders.cameraRecorder.onVisibilityRestored = () => {
23643
- console.log("[Proctoring] Usu\xE1rio retornou ao browser.");
23644
- this.onVisibilityRestoredCallback();
23645
- };
23646
- if (this.sessionOptions.proctoringType === "REALTIME" && !isSafeBrowser()) {
23647
- try {
23648
- await BackgroundUploadService.recoverPendingUploads(
23649
- this.backend,
23650
- this.context.token
23651
- );
23652
- } catch (e3) {
23653
- console.warn("[Proctoring] Erro ao recuperar chunks de sess\xE3o anterior:", e3);
23654
- }
23655
- }
23656
23748
  try {
23657
23749
  console.log("Starting recorders");
23658
23750
  await this.recorder.startAll();
@@ -23733,7 +23825,9 @@ Error: ${error}`
23733
23825
  this.appChecker && await this.appChecker.disconnectWebSocket();
23734
23826
  await this.recorder.saveAllOnSession();
23735
23827
  await this.sendPendingRealtimeAlerts();
23828
+ trackers.registerError(this.proctoringId, `finish this.repository.save starting`);
23736
23829
  await this.repository.save(this.proctoringSession);
23830
+ trackers.registerError(this.proctoringId, `finish this.repository.save finished`);
23737
23831
  let uploader;
23738
23832
  let uploaderServices;
23739
23833
  if (versionVerify() !== "1.0.0.0") {
@@ -23955,6 +24049,61 @@ Error: ` + error
23955
24049
  _screenStream: (_a2 = this.allRecorders.screenRecorder) == null ? void 0 : _a2.screenStream
23956
24050
  };
23957
24051
  }
24052
+ async startChallenge(templateId) {
24053
+ var _a2;
24054
+ if (!this.sessionOptions.useChallenge) {
24055
+ throw new Error("useChallenge is set as false on start method");
24056
+ }
24057
+ await this.extensionEasycatcher.checkExtensionInstalled().catch((err) => {
24058
+ throw new Error("EasyCatcher Extension is not installed");
24059
+ });
24060
+ this.extensionEasycatcher.start();
24061
+ const start = Date.now() - ((_a2 = this.allRecorders.cameraRecorder.getStartTime()) == null ? void 0 : _a2.getTime()) || 0;
24062
+ await this.backend.startChallenge({
24063
+ proctoringId: this.proctoringId,
24064
+ templateId,
24065
+ start
24066
+ }).then((resp) => {
24067
+ console.log(resp);
24068
+ this.challengeId = resp.id;
24069
+ }).catch((reason) => {
24070
+ trackers.registerError(
24071
+ this.proctoringId,
24072
+ "N\xE3o foi poss\xEDvel iniciar desafio!"
24073
+ );
24074
+ throw reason;
24075
+ });
24076
+ this.isChallengeRunning = true;
24077
+ }
24078
+ async stopChallenge() {
24079
+ var _a2;
24080
+ if (!this.isChallengeRunning) {
24081
+ throw new Error("Challenge not started");
24082
+ }
24083
+ try {
24084
+ const sessionData = await this.extensionEasycatcher.getSessionData();
24085
+ const end = Date.now() - ((_a2 = this.allRecorders.cameraRecorder.getStartTime()) == null ? void 0 : _a2.getTime()) || 0;
24086
+ await this.backend.stopChallenge(
24087
+ this.challengeId,
24088
+ {
24089
+ end,
24090
+ data: sessionData
24091
+ }
24092
+ ).catch((reason) => {
24093
+ trackers.registerError(
24094
+ this.proctoringId,
24095
+ "N\xE3o foi poss\xEDvel finalizar o desafio no backend!"
24096
+ );
24097
+ return void 0;
24098
+ });
24099
+ this.isChallengeRunning = false;
24100
+ } catch (error) {
24101
+ trackers.registerError(
24102
+ this.proctoringId,
24103
+ "Erro ao recuperar dados da extens\xE3o: " + error.message
24104
+ );
24105
+ }
24106
+ }
23958
24107
  };
23959
24108
 
23960
24109
  // src/proctoring/SignTerm.ts
@@ -24183,6 +24332,8 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
24183
24332
  return originalStart(parameters2, videoOptions);
24184
24333
  };
24185
24334
  const finish = proctoring.finish.bind(proctoring);
24335
+ const startChallenge = proctoring.startChallenge.bind(proctoring);
24336
+ const stopChallenge = proctoring.stopChallenge.bind(proctoring);
24186
24337
  const pause = proctoring.pause.bind(proctoring);
24187
24338
  const resume = proctoring.resume.bind(proctoring);
24188
24339
  const onFocus = proctoring.setOnFocusCallback.bind(proctoring);
@@ -24207,6 +24358,8 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
24207
24358
  login,
24208
24359
  start,
24209
24360
  finish,
24361
+ startChallenge,
24362
+ stopChallenge,
24210
24363
  onFocus,
24211
24364
  onLostFocus,
24212
24365
  onChangeDevices: onChangeDevices2,
@@ -0,0 +1,11 @@
1
+ import { ProctoringFinisherOptions } from "../proctoring/proctoring";
2
+ export declare class ExtensionEasyCatcher {
3
+ hasExtension: boolean;
4
+ tryes: number;
5
+ responseStart: boolean;
6
+ options: ProctoringFinisherOptions;
7
+ constructor(options?: ProctoringFinisherOptions);
8
+ checkExtensionInstalled(timeoutMs?: number): Promise<string>;
9
+ getSessionData(timeoutMs?: number): Promise<any>;
10
+ start(): Promise<boolean>;
11
+ }
@@ -1,5 +1,5 @@
1
1
  import { ProctoringFinisherOptions } from "../proctoring/proctoring";
2
- export declare class Extension {
2
+ export declare class ExtensionEasyProctor {
3
3
  hasExtension: boolean;
4
4
  tryes: number;
5
5
  responseStart: boolean;