easyproctor-hml 2.7.10 → 2.7.11

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/esm/index.js CHANGED
@@ -13317,16 +13317,6 @@ var _ChunkStorageService = class _ChunkStorageService {
13317
13317
  constructor() {
13318
13318
  this.db = null;
13319
13319
  }
13320
- /**
13321
- * WebKit pode falhar em IDB.add/put com "Error preparing Blob/File data..." se o
13322
- * ArrayBuffer ainda estiver associado ao Blob (ex.: blob.arrayBuffer()) ou no
13323
- * round-trip get→put do mesmo registro. Esta cópia remove qualquer vínculo.
13324
- */
13325
- static detachedArrayBufferCopy(src) {
13326
- const next = new Uint8Array(src.byteLength);
13327
- next.set(new Uint8Array(src));
13328
- return next.buffer;
13329
- }
13330
13320
  /**
13331
13321
  * Abre a conexão com o IndexedDB, criando o banco e o object store se necessário.
13332
13322
  */
@@ -13366,18 +13356,10 @@ var _ChunkStorageService = class _ChunkStorageService {
13366
13356
  */
13367
13357
  async saveChunk(chunk) {
13368
13358
  const db = await this.connect();
13369
- const record = {
13370
- proctoringId: chunk.proctoringId,
13371
- chunkIndex: chunk.chunkIndex,
13372
- arrayBuffer: _ChunkStorageService.detachedArrayBufferCopy(chunk.arrayBuffer),
13373
- timestamp: chunk.timestamp,
13374
- uploaded: chunk.uploaded,
13375
- mimeType: chunk.mimeType
13376
- };
13377
13359
  return new Promise((resolve, reject) => {
13378
13360
  const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
13379
13361
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
13380
- const request = store.add(record);
13362
+ const request = store.add(chunk);
13381
13363
  request.onsuccess = () => {
13382
13364
  resolve(request.result);
13383
13365
  };
@@ -13434,35 +13416,36 @@ var _ChunkStorageService = class _ChunkStorageService {
13434
13416
  });
13435
13417
  }
13436
13418
  /**
13437
- * Remove vários chunks numa única transação (delete por chave — sem put com buffer).
13419
+ * Marca um chunk como enviado (uploaded = 1).
13438
13420
  */
13439
- async deleteChunkIds(chunkIds) {
13440
- if (chunkIds.length === 0) return;
13421
+ async markAsUploaded(chunkId) {
13441
13422
  const db = await this.connect();
13442
13423
  return new Promise((resolve, reject) => {
13443
13424
  const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
13444
13425
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
13445
- transaction.oncomplete = () => resolve();
13446
- transaction.onerror = () => {
13447
- var _a2, _b;
13448
- return reject(
13449
- new Error(
13450
- `Erro ao remover chunks em lote: ${(_b = (_a2 = transaction.error) == null ? void 0 : _a2.message) != null ? _b : "unknown"}`
13451
- )
13452
- );
13426
+ const getRequest = store.get(chunkId);
13427
+ getRequest.onsuccess = () => {
13428
+ const chunk = getRequest.result;
13429
+ if (!chunk) {
13430
+ resolve();
13431
+ return;
13432
+ }
13433
+ chunk.uploaded = 1;
13434
+ const putRequest = store.put(chunk);
13435
+ putRequest.onsuccess = () => resolve();
13436
+ putRequest.onerror = () => {
13437
+ var _a2;
13438
+ return reject(new Error(`Erro ao marcar chunk como enviado: ${(_a2 = putRequest.error) == null ? void 0 : _a2.message}`));
13439
+ };
13453
13440
  };
13454
- transaction.onabort = () => {
13455
- var _a2, _b;
13456
- return reject(new Error(`Remo\xE7\xE3o em lote abortada: ${(_b = (_a2 = transaction.error) == null ? void 0 : _a2.message) != null ? _b : "unknown"}`));
13441
+ getRequest.onerror = () => {
13442
+ var _a2;
13443
+ return reject(new Error(`Erro ao buscar chunk para marcar: ${(_a2 = getRequest.error) == null ? void 0 : _a2.message}`));
13457
13444
  };
13458
- for (const id of chunkIds) {
13459
- store.delete(id);
13460
- }
13461
13445
  });
13462
13446
  }
13463
13447
  /**
13464
- * Remove registros com uploaded = 1 (vestígio de versões antigas que faziam put).
13465
- * Usa openKeyCursor + delete por primaryKey para não materializar o valor (WebKit/iOS).
13448
+ * Remove todos os chunks enviados de um proctoringId para liberar espaço.
13466
13449
  */
13467
13450
  async clearUploadedChunks(proctoringId2) {
13468
13451
  const db = await this.connect();
@@ -13471,11 +13454,11 @@ var _ChunkStorageService = class _ChunkStorageService {
13471
13454
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
13472
13455
  const index = store.index("proctoringId_uploaded");
13473
13456
  const range = IDBKeyRange.only([proctoringId2, 1]);
13474
- const request = index.openKeyCursor(range);
13457
+ const request = index.openCursor(range);
13475
13458
  request.onsuccess = () => {
13476
13459
  const cursor = request.result;
13477
13460
  if (cursor) {
13478
- store.delete(cursor.primaryKey);
13461
+ cursor.delete();
13479
13462
  cursor.continue();
13480
13463
  } else {
13481
13464
  resolve();
@@ -13497,11 +13480,11 @@ var _ChunkStorageService = class _ChunkStorageService {
13497
13480
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
13498
13481
  const index = store.index("proctoringId");
13499
13482
  const range = IDBKeyRange.only(proctoringId2);
13500
- const request = index.openKeyCursor(range);
13483
+ const request = index.openCursor(range);
13501
13484
  request.onsuccess = () => {
13502
13485
  const cursor = request.result;
13503
13486
  if (cursor) {
13504
- store.delete(cursor.primaryKey);
13487
+ cursor.delete();
13505
13488
  cursor.continue();
13506
13489
  } else {
13507
13490
  resolve();
@@ -13568,8 +13551,8 @@ var _ChunkStorageService = class _ChunkStorageService {
13568
13551
  }
13569
13552
  };
13570
13553
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
13571
- /** v2: índices numéricos; v3: ArrayBuffer no payload; v4: cópia explícita de bytes (WebKit/iOS) */
13572
- _ChunkStorageService.DB_VERSION = 4;
13554
+ /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
13555
+ _ChunkStorageService.DB_VERSION = 2;
13573
13556
  _ChunkStorageService.STORE_NAME = "chunks";
13574
13557
  var ChunkStorageService = _ChunkStorageService;
13575
13558
 
@@ -13729,8 +13712,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
13729
13712
  * @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
13730
13713
  */
13731
13714
  async processQueue(isFinal = false) {
13732
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
13733
- console.log(`[BackgroundUpload] processQueue init`);
13715
+ var _a2, _b;
13734
13716
  if (this.isProcessing) return;
13735
13717
  this.isProcessing = true;
13736
13718
  try {
@@ -13743,115 +13725,76 @@ var BackgroundUploadService = class _BackgroundUploadService {
13743
13725
  return;
13744
13726
  }
13745
13727
  }
13746
- console.log(`[BackgroundUpload] processQueue syncOffset ok`);
13747
13728
  const allChunks = await this.chunkStorage.getAllChunks(this.proctoringId);
13748
13729
  const pendingChunks = allChunks.filter((c3) => c3.uploaded === 0);
13749
- console.log(`[BackgroundUpload] processQueue getAllChunks ok`);
13750
13730
  if (pendingChunks.length === 0 && !isFinal) {
13751
13731
  this.isProcessing = false;
13752
13732
  return;
13753
13733
  }
13754
- if (pendingChunks.length === 0 && isFinal) {
13755
- const mimeTypeFallback = (_b = (_a2 = allChunks[allChunks.length - 1]) == null ? void 0 : _a2.mimeType) != null ? _b : "video/webm";
13756
- try {
13757
- if (!this.sessionUrl) {
13758
- this.clearSessionState();
13759
- return;
13760
- }
13761
- const totalBytes = this.currentOffset;
13762
- await this.uploadData(new ArrayBuffer(0), mimeTypeFallback, 0, totalBytes);
13763
- await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13764
- this.clearSessionState();
13765
- } catch (error) {
13766
- console.error(
13767
- "[BackgroundUpload] Falha ao finalizar upload (flush sem pendentes):",
13768
- error
13769
- );
13770
- (_c2 = this.onUploadError) == null ? void 0 : _c2.call(this, 0, error);
13771
- }
13772
- return;
13773
- }
13774
13734
  console.log(`[BackgroundUpload] ${pendingChunks.length} chunks pendentes encontrados. Modo final: ${isFinal}`);
13775
13735
  let virtualStart = this.totalBytesPurged;
13776
13736
  const chunksWithMeta = allChunks.map((c3) => {
13777
13737
  const start = virtualStart;
13778
- const end = start + c3.arrayBuffer.byteLength - 1;
13779
- virtualStart += c3.arrayBuffer.byteLength;
13738
+ const end = start + c3.blob.size - 1;
13739
+ virtualStart += c3.blob.size;
13780
13740
  return { chunk: c3, start, end };
13781
13741
  });
13782
- const combinedBufferParts = [];
13742
+ let combinedBlobParts = [];
13783
13743
  let lastProcessedChunkId = null;
13784
13744
  let finalChunkIndex = 0;
13785
- const mimeType = (_g = (_f = (_d = pendingChunks[0]) == null ? void 0 : _d.mimeType) != null ? _f : (_e3 = allChunks[allChunks.length - 1]) == null ? void 0 : _e3.mimeType) != null ? _g : "video/webm";
13786
- console.log(`[BackgroundUpload] passo 1 ok`);
13745
+ let mimeType = pendingChunks[0].mimeType;
13787
13746
  for (const meta of chunksWithMeta) {
13788
13747
  if (this.currentOffset > meta.end) continue;
13789
13748
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
13790
- const chunkSlice = meta.chunk.arrayBuffer.slice(sliceStart);
13791
- combinedBufferParts.push(chunkSlice);
13749
+ const chunkSlice = meta.chunk.blob.slice(sliceStart);
13750
+ combinedBlobParts.push(chunkSlice);
13792
13751
  lastProcessedChunkId = meta.chunk.id;
13793
13752
  finalChunkIndex = meta.chunk.chunkIndex;
13794
13753
  }
13795
- console.log(`[BackgroundUpload] passo 2 ok`);
13796
- if (combinedBufferParts.length === 0 && !isFinal) {
13754
+ if (combinedBlobParts.length === 0 && !isFinal) {
13797
13755
  this.isProcessing = false;
13798
13756
  return;
13799
13757
  }
13800
- const fullBuffer = _BackgroundUploadService.concatArrayBuffers(combinedBufferParts);
13801
- console.log(`[BackgroundUpload] passo 3 ok`);
13802
- let sendableSize = fullBuffer.byteLength;
13758
+ let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
13759
+ let sendableSize = fullBlob.size;
13803
13760
  let totalSizeForHeader = void 0;
13804
13761
  if (!isFinal) {
13805
- sendableSize = Math.floor(fullBuffer.byteLength / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
13762
+ sendableSize = Math.floor(fullBlob.size / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
13806
13763
  if (sendableSize === 0) {
13807
13764
  console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
13808
13765
  this.isProcessing = false;
13809
13766
  return;
13810
13767
  }
13811
13768
  } else {
13812
- totalSizeForHeader = fullBuffer.byteLength > 0 ? virtualStart : Math.max(virtualStart, this.currentOffset);
13769
+ totalSizeForHeader = virtualStart;
13813
13770
  }
13814
- console.log(`[BackgroundUpload] passo 4 ok`);
13815
- const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
13771
+ const blobToSend = fullBlob.slice(0, sendableSize);
13816
13772
  try {
13817
- await this.uploadData(bufferToSend, mimeType, finalChunkIndex, totalSizeForHeader);
13818
- console.log(`[BackgroundUpload] passo 5 ok`);
13819
- const fullySent = chunksWithMeta.filter(
13820
- (meta) => meta.chunk.uploaded === 0 && meta.end < this.currentOffset && meta.chunk.id != null
13821
- );
13822
- const sizePurged = fullySent.reduce(
13823
- (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
13824
- 0
13825
- );
13826
- const idsToDelete = fullySent.map((m3) => m3.chunk.id);
13827
- if (idsToDelete.length > 0) {
13828
- await this.chunkStorage.deleteChunkIds(idsToDelete);
13829
- for (const meta of fullySent) {
13773
+ await this.uploadData(blobToSend, mimeType, finalChunkIndex, totalSizeForHeader);
13774
+ for (const meta of chunksWithMeta) {
13775
+ if (meta.chunk.uploaded === 0 && meta.end < this.currentOffset) {
13776
+ await this.chunkStorage.markAsUploaded(meta.chunk.id);
13830
13777
  this.retryCount.delete(meta.chunk.id);
13831
- (_h = this.onChunkUploaded) == null ? void 0 : _h.call(this, meta.chunk.id, meta.chunk.chunkIndex);
13832
- console.log(
13833
- `[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
13834
- );
13778
+ (_a2 = this.onChunkUploaded) == null ? void 0 : _a2.call(this, meta.chunk.id, meta.chunk.chunkIndex);
13779
+ console.log(`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} marcado como enviado.`);
13835
13780
  }
13836
13781
  }
13837
- console.log(`[BackgroundUpload] passo 6 ok`);
13838
- await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13839
- console.log(`[BackgroundUpload] passo 7 ok`);
13840
- if (this.config.cleanAfterUpload && sizePurged > 0) {
13841
- this.totalBytesPurged += sizePurged;
13842
- this.saveSessionState();
13843
- console.log(
13844
- `[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`
13845
- );
13782
+ if (this.config.cleanAfterUpload) {
13783
+ const chunksToClear = chunksWithMeta.filter((meta) => meta.chunk.uploaded === 1 || meta.chunk.uploaded === 0 && meta.end < this.currentOffset);
13784
+ const sizePurged = chunksToClear.reduce((acc, meta) => acc + meta.chunk.blob.size, 0);
13785
+ await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13786
+ if (sizePurged > 0) {
13787
+ this.totalBytesPurged += sizePurged;
13788
+ this.saveSessionState();
13789
+ console.log(`[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`);
13790
+ }
13846
13791
  }
13847
- console.log(`[BackgroundUpload] passo 8 ok`);
13848
13792
  if (isFinal) {
13849
13793
  this.clearSessionState();
13850
13794
  }
13851
- console.log(`[BackgroundUpload] passo 9 ok`);
13852
13795
  } catch (error) {
13853
13796
  console.error("[BackgroundUpload] Falha no upload:", error);
13854
- (_i3 = this.onUploadError) == null ? void 0 : _i3.call(this, lastProcessedChunkId || 0, error);
13797
+ (_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
13855
13798
  }
13856
13799
  } catch (error) {
13857
13800
  console.error("[BackgroundUpload] Erro ao processar fila:", error);
@@ -13862,18 +13805,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
13862
13805
  /**
13863
13806
  * Faz o upload bruto de dados para a sessão GCS.
13864
13807
  */
13865
- static concatArrayBuffers(parts) {
13866
- if (parts.length === 0) return new ArrayBuffer(0);
13867
- const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
13868
- const merged = new Uint8Array(total);
13869
- let offset = 0;
13870
- for (const p3 of parts) {
13871
- merged.set(new Uint8Array(p3), offset);
13872
- offset += p3.byteLength;
13873
- }
13874
- return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
13875
- }
13876
- async uploadData(data, mimeType, chunkIndex, totalSize) {
13808
+ async uploadData(blob, mimeType, chunkIndex, totalSize) {
13877
13809
  const fileName = `EP_${this.proctoringId}_camera_0.webm`;
13878
13810
  if (!this.sessionUrl) {
13879
13811
  const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
@@ -13905,16 +13837,15 @@ var BackgroundUploadService = class _BackgroundUploadService {
13905
13837
  console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
13906
13838
  }
13907
13839
  const start = this.currentOffset;
13908
- const end = start + data.byteLength - 1;
13840
+ const end = start + blob.size - 1;
13909
13841
  const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
13910
- const contentRangeHeader = data.byteLength === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
13911
- console.log(
13912
- `[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
13913
- );
13842
+ const contentRangeHeader = blob.size === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
13843
+ console.log(`[BackgroundUpload] Enviando ${blob.size > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${blob.size})`);
13914
13844
  const response = await fetch(this.sessionUrl, {
13915
13845
  method: "PUT",
13916
13846
  headers: { "Content-Range": contentRangeHeader },
13917
- body: data.byteLength > 0 ? data : null
13847
+ body: blob.size > 0 ? blob : null
13848
+ // Usa null para garantir corpo vazio se necessário
13918
13849
  });
13919
13850
  console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
13920
13851
  if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
@@ -13927,13 +13858,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
13927
13858
  const lastByte = parseInt(rangeHeader.split("-")[1], 10);
13928
13859
  this.currentOffset = lastByte + 1;
13929
13860
  } else {
13930
- this.currentOffset += data.byteLength;
13861
+ this.currentOffset += blob.size;
13931
13862
  }
13932
13863
  this.saveSessionState();
13933
13864
  trackers.registerUploadFile(
13934
13865
  this.proctoringId,
13935
13866
  `GCS Stream Upload
13936
- Size: ${data.byteLength}
13867
+ Size: ${blob.size}
13937
13868
  Range: ${start}-${end}
13938
13869
  Last Index: ${chunkIndex}`,
13939
13870
  "CameraChunk"
@@ -14068,7 +13999,7 @@ var _CameraRecorder = class _CameraRecorder {
14068
13999
  * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
14069
14000
  */
14070
14001
  get isChunkEnabled() {
14071
- return !!this.proctoringId && this.options.proctoringType === "REALTIME" && !isSafeBrowser();
14002
+ return false;
14072
14003
  }
14073
14004
  setProctoringId(proctoringId2) {
14074
14005
  this.proctoringId = proctoringId2;
@@ -14472,15 +14403,14 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14472
14403
  * Salva o chunk no IndexedDB para persistência e recuperação.
14473
14404
  */
14474
14405
  async handleNewChunk(blob, idx) {
14475
- if (!this.proctoringId || !this.chunkStorage || blob.size == 0) return;
14406
+ if (!this.proctoringId || !this.chunkStorage) return;
14476
14407
  const savePromise = (async () => {
14477
14408
  var _a2;
14478
14409
  try {
14479
- const arrayBuffer = await blob.arrayBuffer();
14480
14410
  await this.chunkStorage.saveChunk({
14481
14411
  proctoringId: this.proctoringId,
14482
14412
  chunkIndex: this.chunkIndex,
14483
- arrayBuffer,
14413
+ blob,
14484
14414
  timestamp: Date.now(),
14485
14415
  uploaded: 0,
14486
14416
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"