easyproctor-hml 2.7.9 → 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;
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,10 +13725,8 @@ 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;
@@ -13755,34 +13735,31 @@ var BackgroundUploadService = class _BackgroundUploadService {
13755
13735
  let virtualStart = this.totalBytesPurged;
13756
13736
  const chunksWithMeta = allChunks.map((c3) => {
13757
13737
  const start = virtualStart;
13758
- const end = start + c3.arrayBuffer.byteLength - 1;
13759
- virtualStart += c3.arrayBuffer.byteLength;
13738
+ const end = start + c3.blob.size - 1;
13739
+ virtualStart += c3.blob.size;
13760
13740
  return { chunk: c3, start, end };
13761
13741
  });
13762
- const combinedBufferParts = [];
13742
+ let combinedBlobParts = [];
13763
13743
  let lastProcessedChunkId = null;
13764
13744
  let finalChunkIndex = 0;
13765
- const mimeType = (_d = (_c2 = (_a2 = pendingChunks[0]) == null ? void 0 : _a2.mimeType) != null ? _c2 : (_b = allChunks[allChunks.length - 1]) == null ? void 0 : _b.mimeType) != null ? _d : "video/webm";
13766
- console.log(`[BackgroundUpload] passo 1 ok`);
13745
+ let mimeType = pendingChunks[0].mimeType;
13767
13746
  for (const meta of chunksWithMeta) {
13768
13747
  if (this.currentOffset > meta.end) continue;
13769
13748
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
13770
- const chunkSlice = meta.chunk.arrayBuffer.slice(sliceStart);
13771
- combinedBufferParts.push(chunkSlice);
13749
+ const chunkSlice = meta.chunk.blob.slice(sliceStart);
13750
+ combinedBlobParts.push(chunkSlice);
13772
13751
  lastProcessedChunkId = meta.chunk.id;
13773
13752
  finalChunkIndex = meta.chunk.chunkIndex;
13774
13753
  }
13775
- console.log(`[BackgroundUpload] passo 2 ok`);
13776
- if (combinedBufferParts.length === 0 && !isFinal) {
13754
+ if (combinedBlobParts.length === 0 && !isFinal) {
13777
13755
  this.isProcessing = false;
13778
13756
  return;
13779
13757
  }
13780
- const fullBuffer = _BackgroundUploadService.concatArrayBuffers(combinedBufferParts);
13781
- console.log(`[BackgroundUpload] passo 3 ok`);
13782
- let sendableSize = fullBuffer.byteLength;
13758
+ let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
13759
+ let sendableSize = fullBlob.size;
13783
13760
  let totalSizeForHeader = void 0;
13784
13761
  if (!isFinal) {
13785
- 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;
13786
13763
  if (sendableSize === 0) {
13787
13764
  console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
13788
13765
  this.isProcessing = false;
@@ -13791,47 +13768,33 @@ var BackgroundUploadService = class _BackgroundUploadService {
13791
13768
  } else {
13792
13769
  totalSizeForHeader = virtualStart;
13793
13770
  }
13794
- console.log(`[BackgroundUpload] passo 4 ok`);
13795
- const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
13771
+ const blobToSend = fullBlob.slice(0, sendableSize);
13796
13772
  try {
13797
- await this.uploadData(bufferToSend, mimeType, finalChunkIndex, totalSizeForHeader);
13798
- console.log(`[BackgroundUpload] passo 5 ok`);
13799
- const fullySent = chunksWithMeta.filter(
13800
- (meta) => meta.chunk.uploaded === 0 && meta.end < this.currentOffset && meta.chunk.id != null
13801
- );
13802
- const sizePurged = fullySent.reduce(
13803
- (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
13804
- 0
13805
- );
13806
- const idsToDelete = fullySent.map((m3) => m3.chunk.id);
13807
- if (idsToDelete.length > 0) {
13808
- await this.chunkStorage.deleteChunkIds(idsToDelete);
13809
- 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);
13810
13777
  this.retryCount.delete(meta.chunk.id);
13811
- (_e3 = this.onChunkUploaded) == null ? void 0 : _e3.call(this, meta.chunk.id, meta.chunk.chunkIndex);
13812
- console.log(
13813
- `[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
13814
- );
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.`);
13815
13780
  }
13816
13781
  }
13817
- console.log(`[BackgroundUpload] passo 6 ok`);
13818
- await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13819
- console.log(`[BackgroundUpload] passo 7 ok`);
13820
- if (this.config.cleanAfterUpload && sizePurged > 0) {
13821
- this.totalBytesPurged += sizePurged;
13822
- this.saveSessionState();
13823
- console.log(
13824
- `[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`
13825
- );
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
+ }
13826
13791
  }
13827
- console.log(`[BackgroundUpload] passo 8 ok`);
13828
13792
  if (isFinal) {
13829
13793
  this.clearSessionState();
13830
13794
  }
13831
- console.log(`[BackgroundUpload] passo 9 ok`);
13832
13795
  } catch (error) {
13833
13796
  console.error("[BackgroundUpload] Falha no upload:", error);
13834
- (_f = this.onUploadError) == null ? void 0 : _f.call(this, lastProcessedChunkId || 0, error);
13797
+ (_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
13835
13798
  }
13836
13799
  } catch (error) {
13837
13800
  console.error("[BackgroundUpload] Erro ao processar fila:", error);
@@ -13842,18 +13805,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
13842
13805
  /**
13843
13806
  * Faz o upload bruto de dados para a sessão GCS.
13844
13807
  */
13845
- static concatArrayBuffers(parts) {
13846
- if (parts.length === 0) return new ArrayBuffer(0);
13847
- const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
13848
- const merged = new Uint8Array(total);
13849
- let offset = 0;
13850
- for (const p3 of parts) {
13851
- merged.set(new Uint8Array(p3), offset);
13852
- offset += p3.byteLength;
13853
- }
13854
- return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
13855
- }
13856
- async uploadData(data, mimeType, chunkIndex, totalSize) {
13808
+ async uploadData(blob, mimeType, chunkIndex, totalSize) {
13857
13809
  const fileName = `EP_${this.proctoringId}_camera_0.webm`;
13858
13810
  if (!this.sessionUrl) {
13859
13811
  const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
@@ -13885,16 +13837,15 @@ var BackgroundUploadService = class _BackgroundUploadService {
13885
13837
  console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
13886
13838
  }
13887
13839
  const start = this.currentOffset;
13888
- const end = start + data.byteLength - 1;
13840
+ const end = start + blob.size - 1;
13889
13841
  const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
13890
- const contentRangeHeader = data.byteLength === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
13891
- console.log(
13892
- `[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
13893
- );
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})`);
13894
13844
  const response = await fetch(this.sessionUrl, {
13895
13845
  method: "PUT",
13896
13846
  headers: { "Content-Range": contentRangeHeader },
13897
- body: data.byteLength > 0 ? data : null
13847
+ body: blob.size > 0 ? blob : null
13848
+ // Usa null para garantir corpo vazio se necessário
13898
13849
  });
13899
13850
  console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
13900
13851
  if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
@@ -13907,13 +13858,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
13907
13858
  const lastByte = parseInt(rangeHeader.split("-")[1], 10);
13908
13859
  this.currentOffset = lastByte + 1;
13909
13860
  } else {
13910
- this.currentOffset += data.byteLength;
13861
+ this.currentOffset += blob.size;
13911
13862
  }
13912
13863
  this.saveSessionState();
13913
13864
  trackers.registerUploadFile(
13914
13865
  this.proctoringId,
13915
13866
  `GCS Stream Upload
13916
- Size: ${data.byteLength}
13867
+ Size: ${blob.size}
13917
13868
  Range: ${start}-${end}
13918
13869
  Last Index: ${chunkIndex}`,
13919
13870
  "CameraChunk"
@@ -14048,7 +13999,7 @@ var _CameraRecorder = class _CameraRecorder {
14048
13999
  * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
14049
14000
  */
14050
14001
  get isChunkEnabled() {
14051
- return !!this.proctoringId && this.options.proctoringType === "REALTIME" && !isSafeBrowser();
14002
+ return false;
14052
14003
  }
14053
14004
  setProctoringId(proctoringId2) {
14054
14005
  this.proctoringId = proctoringId2;
@@ -14452,15 +14403,14 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14452
14403
  * Salva o chunk no IndexedDB para persistência e recuperação.
14453
14404
  */
14454
14405
  async handleNewChunk(blob, idx) {
14455
- if (!this.proctoringId || !this.chunkStorage || blob.size == 0) return;
14406
+ if (!this.proctoringId || !this.chunkStorage) return;
14456
14407
  const savePromise = (async () => {
14457
14408
  var _a2;
14458
14409
  try {
14459
- const arrayBuffer = await blob.arrayBuffer();
14460
14410
  await this.chunkStorage.saveChunk({
14461
14411
  proctoringId: this.proctoringId,
14462
14412
  chunkIndex: this.chunkIndex,
14463
- arrayBuffer,
14413
+ blob,
14464
14414
  timestamp: Date.now(),
14465
14415
  uploaded: 0,
14466
14416
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"