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/index.js CHANGED
@@ -31414,16 +31414,6 @@ var _ChunkStorageService = class _ChunkStorageService {
31414
31414
  constructor() {
31415
31415
  this.db = null;
31416
31416
  }
31417
- /**
31418
- * WebKit pode falhar em IDB.add/put com "Error preparing Blob/File data..." se o
31419
- * ArrayBuffer ainda estiver associado ao Blob (ex.: blob.arrayBuffer()) ou no
31420
- * round-trip get→put do mesmo registro. Esta cópia remove qualquer vínculo.
31421
- */
31422
- static detachedArrayBufferCopy(src) {
31423
- const next = new Uint8Array(src.byteLength);
31424
- next.set(new Uint8Array(src));
31425
- return next.buffer;
31426
- }
31427
31417
  /**
31428
31418
  * Abre a conexão com o IndexedDB, criando o banco e o object store se necessário.
31429
31419
  */
@@ -31463,18 +31453,10 @@ var _ChunkStorageService = class _ChunkStorageService {
31463
31453
  */
31464
31454
  async saveChunk(chunk) {
31465
31455
  const db = await this.connect();
31466
- const record = {
31467
- proctoringId: chunk.proctoringId,
31468
- chunkIndex: chunk.chunkIndex,
31469
- arrayBuffer: _ChunkStorageService.detachedArrayBufferCopy(chunk.arrayBuffer),
31470
- timestamp: chunk.timestamp,
31471
- uploaded: chunk.uploaded,
31472
- mimeType: chunk.mimeType
31473
- };
31474
31456
  return new Promise((resolve, reject) => {
31475
31457
  const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
31476
31458
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
31477
- const request = store.add(record);
31459
+ const request = store.add(chunk);
31478
31460
  request.onsuccess = () => {
31479
31461
  resolve(request.result);
31480
31462
  };
@@ -31531,35 +31513,36 @@ var _ChunkStorageService = class _ChunkStorageService {
31531
31513
  });
31532
31514
  }
31533
31515
  /**
31534
- * Remove vários chunks numa única transação (delete por chave — sem put com buffer).
31516
+ * Marca um chunk como enviado (uploaded = 1).
31535
31517
  */
31536
- async deleteChunkIds(chunkIds) {
31537
- if (chunkIds.length === 0) return;
31518
+ async markAsUploaded(chunkId) {
31538
31519
  const db = await this.connect();
31539
31520
  return new Promise((resolve, reject) => {
31540
31521
  const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
31541
31522
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
31542
- transaction.oncomplete = () => resolve();
31543
- transaction.onerror = () => {
31544
- var _a2, _b;
31545
- return reject(
31546
- new Error(
31547
- `Erro ao remover chunks em lote: ${(_b = (_a2 = transaction.error) == null ? void 0 : _a2.message) != null ? _b : "unknown"}`
31548
- )
31549
- );
31523
+ const getRequest = store.get(chunkId);
31524
+ getRequest.onsuccess = () => {
31525
+ const chunk = getRequest.result;
31526
+ if (!chunk) {
31527
+ resolve();
31528
+ return;
31529
+ }
31530
+ chunk.uploaded = 1;
31531
+ const putRequest = store.put(chunk);
31532
+ putRequest.onsuccess = () => resolve();
31533
+ putRequest.onerror = () => {
31534
+ var _a2;
31535
+ return reject(new Error(`Erro ao marcar chunk como enviado: ${(_a2 = putRequest.error) == null ? void 0 : _a2.message}`));
31536
+ };
31550
31537
  };
31551
- transaction.onabort = () => {
31552
- var _a2, _b;
31553
- return reject(new Error(`Remo\xE7\xE3o em lote abortada: ${(_b = (_a2 = transaction.error) == null ? void 0 : _a2.message) != null ? _b : "unknown"}`));
31538
+ getRequest.onerror = () => {
31539
+ var _a2;
31540
+ return reject(new Error(`Erro ao buscar chunk para marcar: ${(_a2 = getRequest.error) == null ? void 0 : _a2.message}`));
31554
31541
  };
31555
- for (const id of chunkIds) {
31556
- store.delete(id);
31557
- }
31558
31542
  });
31559
31543
  }
31560
31544
  /**
31561
- * Remove registros com uploaded = 1 (vestígio de versões antigas que faziam put).
31562
- * Usa openKeyCursor + delete por primaryKey para não materializar o valor (WebKit/iOS).
31545
+ * Remove todos os chunks enviados de um proctoringId para liberar espaço.
31563
31546
  */
31564
31547
  async clearUploadedChunks(proctoringId2) {
31565
31548
  const db = await this.connect();
@@ -31568,11 +31551,11 @@ var _ChunkStorageService = class _ChunkStorageService {
31568
31551
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
31569
31552
  const index = store.index("proctoringId_uploaded");
31570
31553
  const range = IDBKeyRange.only([proctoringId2, 1]);
31571
- const request = index.openKeyCursor(range);
31554
+ const request = index.openCursor(range);
31572
31555
  request.onsuccess = () => {
31573
31556
  const cursor = request.result;
31574
31557
  if (cursor) {
31575
- store.delete(cursor.primaryKey);
31558
+ cursor.delete();
31576
31559
  cursor.continue();
31577
31560
  } else {
31578
31561
  resolve();
@@ -31594,11 +31577,11 @@ var _ChunkStorageService = class _ChunkStorageService {
31594
31577
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
31595
31578
  const index = store.index("proctoringId");
31596
31579
  const range = IDBKeyRange.only(proctoringId2);
31597
- const request = index.openKeyCursor(range);
31580
+ const request = index.openCursor(range);
31598
31581
  request.onsuccess = () => {
31599
31582
  const cursor = request.result;
31600
31583
  if (cursor) {
31601
- store.delete(cursor.primaryKey);
31584
+ cursor.delete();
31602
31585
  cursor.continue();
31603
31586
  } else {
31604
31587
  resolve();
@@ -31665,8 +31648,8 @@ var _ChunkStorageService = class _ChunkStorageService {
31665
31648
  }
31666
31649
  };
31667
31650
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
31668
- /** v2: índices numéricos; v3: ArrayBuffer no payload; v4: cópia explícita de bytes (WebKit/iOS) */
31669
- _ChunkStorageService.DB_VERSION = 4;
31651
+ /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
31652
+ _ChunkStorageService.DB_VERSION = 2;
31670
31653
  _ChunkStorageService.STORE_NAME = "chunks";
31671
31654
  var ChunkStorageService = _ChunkStorageService;
31672
31655
 
@@ -31826,8 +31809,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
31826
31809
  * @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
31827
31810
  */
31828
31811
  async processQueue(isFinal = false) {
31829
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
31830
- console.log(`[BackgroundUpload] processQueue init`);
31812
+ var _a2, _b;
31831
31813
  if (this.isProcessing) return;
31832
31814
  this.isProcessing = true;
31833
31815
  try {
@@ -31840,115 +31822,76 @@ var BackgroundUploadService = class _BackgroundUploadService {
31840
31822
  return;
31841
31823
  }
31842
31824
  }
31843
- console.log(`[BackgroundUpload] processQueue syncOffset ok`);
31844
31825
  const allChunks = await this.chunkStorage.getAllChunks(this.proctoringId);
31845
31826
  const pendingChunks = allChunks.filter((c3) => c3.uploaded === 0);
31846
- console.log(`[BackgroundUpload] processQueue getAllChunks ok`);
31847
31827
  if (pendingChunks.length === 0 && !isFinal) {
31848
31828
  this.isProcessing = false;
31849
31829
  return;
31850
31830
  }
31851
- if (pendingChunks.length === 0 && isFinal) {
31852
- const mimeTypeFallback = (_b = (_a2 = allChunks[allChunks.length - 1]) == null ? void 0 : _a2.mimeType) != null ? _b : "video/webm";
31853
- try {
31854
- if (!this.sessionUrl) {
31855
- this.clearSessionState();
31856
- return;
31857
- }
31858
- const totalBytes = this.currentOffset;
31859
- await this.uploadData(new ArrayBuffer(0), mimeTypeFallback, 0, totalBytes);
31860
- await this.chunkStorage.clearUploadedChunks(this.proctoringId);
31861
- this.clearSessionState();
31862
- } catch (error) {
31863
- console.error(
31864
- "[BackgroundUpload] Falha ao finalizar upload (flush sem pendentes):",
31865
- error
31866
- );
31867
- (_c2 = this.onUploadError) == null ? void 0 : _c2.call(this, 0, error);
31868
- }
31869
- return;
31870
- }
31871
31831
  console.log(`[BackgroundUpload] ${pendingChunks.length} chunks pendentes encontrados. Modo final: ${isFinal}`);
31872
31832
  let virtualStart = this.totalBytesPurged;
31873
31833
  const chunksWithMeta = allChunks.map((c3) => {
31874
31834
  const start = virtualStart;
31875
- const end = start + c3.arrayBuffer.byteLength - 1;
31876
- virtualStart += c3.arrayBuffer.byteLength;
31835
+ const end = start + c3.blob.size - 1;
31836
+ virtualStart += c3.blob.size;
31877
31837
  return { chunk: c3, start, end };
31878
31838
  });
31879
- const combinedBufferParts = [];
31839
+ let combinedBlobParts = [];
31880
31840
  let lastProcessedChunkId = null;
31881
31841
  let finalChunkIndex = 0;
31882
- 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";
31883
- console.log(`[BackgroundUpload] passo 1 ok`);
31842
+ let mimeType = pendingChunks[0].mimeType;
31884
31843
  for (const meta of chunksWithMeta) {
31885
31844
  if (this.currentOffset > meta.end) continue;
31886
31845
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
31887
- const chunkSlice = meta.chunk.arrayBuffer.slice(sliceStart);
31888
- combinedBufferParts.push(chunkSlice);
31846
+ const chunkSlice = meta.chunk.blob.slice(sliceStart);
31847
+ combinedBlobParts.push(chunkSlice);
31889
31848
  lastProcessedChunkId = meta.chunk.id;
31890
31849
  finalChunkIndex = meta.chunk.chunkIndex;
31891
31850
  }
31892
- console.log(`[BackgroundUpload] passo 2 ok`);
31893
- if (combinedBufferParts.length === 0 && !isFinal) {
31851
+ if (combinedBlobParts.length === 0 && !isFinal) {
31894
31852
  this.isProcessing = false;
31895
31853
  return;
31896
31854
  }
31897
- const fullBuffer = _BackgroundUploadService.concatArrayBuffers(combinedBufferParts);
31898
- console.log(`[BackgroundUpload] passo 3 ok`);
31899
- let sendableSize = fullBuffer.byteLength;
31855
+ let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
31856
+ let sendableSize = fullBlob.size;
31900
31857
  let totalSizeForHeader = void 0;
31901
31858
  if (!isFinal) {
31902
- sendableSize = Math.floor(fullBuffer.byteLength / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
31859
+ sendableSize = Math.floor(fullBlob.size / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
31903
31860
  if (sendableSize === 0) {
31904
31861
  console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
31905
31862
  this.isProcessing = false;
31906
31863
  return;
31907
31864
  }
31908
31865
  } else {
31909
- totalSizeForHeader = fullBuffer.byteLength > 0 ? virtualStart : Math.max(virtualStart, this.currentOffset);
31866
+ totalSizeForHeader = virtualStart;
31910
31867
  }
31911
- console.log(`[BackgroundUpload] passo 4 ok`);
31912
- const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
31868
+ const blobToSend = fullBlob.slice(0, sendableSize);
31913
31869
  try {
31914
- await this.uploadData(bufferToSend, mimeType, finalChunkIndex, totalSizeForHeader);
31915
- console.log(`[BackgroundUpload] passo 5 ok`);
31916
- const fullySent = chunksWithMeta.filter(
31917
- (meta) => meta.chunk.uploaded === 0 && meta.end < this.currentOffset && meta.chunk.id != null
31918
- );
31919
- const sizePurged = fullySent.reduce(
31920
- (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
31921
- 0
31922
- );
31923
- const idsToDelete = fullySent.map((m3) => m3.chunk.id);
31924
- if (idsToDelete.length > 0) {
31925
- await this.chunkStorage.deleteChunkIds(idsToDelete);
31926
- for (const meta of fullySent) {
31870
+ await this.uploadData(blobToSend, mimeType, finalChunkIndex, totalSizeForHeader);
31871
+ for (const meta of chunksWithMeta) {
31872
+ if (meta.chunk.uploaded === 0 && meta.end < this.currentOffset) {
31873
+ await this.chunkStorage.markAsUploaded(meta.chunk.id);
31927
31874
  this.retryCount.delete(meta.chunk.id);
31928
- (_h = this.onChunkUploaded) == null ? void 0 : _h.call(this, meta.chunk.id, meta.chunk.chunkIndex);
31929
- console.log(
31930
- `[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
31931
- );
31875
+ (_a2 = this.onChunkUploaded) == null ? void 0 : _a2.call(this, meta.chunk.id, meta.chunk.chunkIndex);
31876
+ console.log(`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} marcado como enviado.`);
31932
31877
  }
31933
31878
  }
31934
- console.log(`[BackgroundUpload] passo 6 ok`);
31935
- await this.chunkStorage.clearUploadedChunks(this.proctoringId);
31936
- console.log(`[BackgroundUpload] passo 7 ok`);
31937
- if (this.config.cleanAfterUpload && sizePurged > 0) {
31938
- this.totalBytesPurged += sizePurged;
31939
- this.saveSessionState();
31940
- console.log(
31941
- `[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`
31942
- );
31879
+ if (this.config.cleanAfterUpload) {
31880
+ const chunksToClear = chunksWithMeta.filter((meta) => meta.chunk.uploaded === 1 || meta.chunk.uploaded === 0 && meta.end < this.currentOffset);
31881
+ const sizePurged = chunksToClear.reduce((acc, meta) => acc + meta.chunk.blob.size, 0);
31882
+ await this.chunkStorage.clearUploadedChunks(this.proctoringId);
31883
+ if (sizePurged > 0) {
31884
+ this.totalBytesPurged += sizePurged;
31885
+ this.saveSessionState();
31886
+ console.log(`[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`);
31887
+ }
31943
31888
  }
31944
- console.log(`[BackgroundUpload] passo 8 ok`);
31945
31889
  if (isFinal) {
31946
31890
  this.clearSessionState();
31947
31891
  }
31948
- console.log(`[BackgroundUpload] passo 9 ok`);
31949
31892
  } catch (error) {
31950
31893
  console.error("[BackgroundUpload] Falha no upload:", error);
31951
- (_i3 = this.onUploadError) == null ? void 0 : _i3.call(this, lastProcessedChunkId || 0, error);
31894
+ (_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
31952
31895
  }
31953
31896
  } catch (error) {
31954
31897
  console.error("[BackgroundUpload] Erro ao processar fila:", error);
@@ -31959,18 +31902,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
31959
31902
  /**
31960
31903
  * Faz o upload bruto de dados para a sessão GCS.
31961
31904
  */
31962
- static concatArrayBuffers(parts) {
31963
- if (parts.length === 0) return new ArrayBuffer(0);
31964
- const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
31965
- const merged = new Uint8Array(total);
31966
- let offset = 0;
31967
- for (const p3 of parts) {
31968
- merged.set(new Uint8Array(p3), offset);
31969
- offset += p3.byteLength;
31970
- }
31971
- return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
31972
- }
31973
- async uploadData(data, mimeType, chunkIndex, totalSize) {
31905
+ async uploadData(blob, mimeType, chunkIndex, totalSize) {
31974
31906
  const fileName = `EP_${this.proctoringId}_camera_0.webm`;
31975
31907
  if (!this.sessionUrl) {
31976
31908
  const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
@@ -32002,16 +31934,15 @@ var BackgroundUploadService = class _BackgroundUploadService {
32002
31934
  console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
32003
31935
  }
32004
31936
  const start = this.currentOffset;
32005
- const end = start + data.byteLength - 1;
31937
+ const end = start + blob.size - 1;
32006
31938
  const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
32007
- const contentRangeHeader = data.byteLength === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
32008
- console.log(
32009
- `[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
32010
- );
31939
+ const contentRangeHeader = blob.size === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
31940
+ console.log(`[BackgroundUpload] Enviando ${blob.size > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${blob.size})`);
32011
31941
  const response = await fetch(this.sessionUrl, {
32012
31942
  method: "PUT",
32013
31943
  headers: { "Content-Range": contentRangeHeader },
32014
- body: data.byteLength > 0 ? data : null
31944
+ body: blob.size > 0 ? blob : null
31945
+ // Usa null para garantir corpo vazio se necessário
32015
31946
  });
32016
31947
  console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
32017
31948
  if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
@@ -32024,13 +31955,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
32024
31955
  const lastByte = parseInt(rangeHeader.split("-")[1], 10);
32025
31956
  this.currentOffset = lastByte + 1;
32026
31957
  } else {
32027
- this.currentOffset += data.byteLength;
31958
+ this.currentOffset += blob.size;
32028
31959
  }
32029
31960
  this.saveSessionState();
32030
31961
  trackers.registerUploadFile(
32031
31962
  this.proctoringId,
32032
31963
  `GCS Stream Upload
32033
- Size: ${data.byteLength}
31964
+ Size: ${blob.size}
32034
31965
  Range: ${start}-${end}
32035
31966
  Last Index: ${chunkIndex}`,
32036
31967
  "CameraChunk"
@@ -32165,7 +32096,7 @@ var _CameraRecorder = class _CameraRecorder {
32165
32096
  * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
32166
32097
  */
32167
32098
  get isChunkEnabled() {
32168
- return !!this.proctoringId && this.options.proctoringType === "REALTIME" && !isSafeBrowser();
32099
+ return false;
32169
32100
  }
32170
32101
  setProctoringId(proctoringId2) {
32171
32102
  this.proctoringId = proctoringId2;
@@ -32569,15 +32500,14 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32569
32500
  * Salva o chunk no IndexedDB para persistência e recuperação.
32570
32501
  */
32571
32502
  async handleNewChunk(blob, idx) {
32572
- if (!this.proctoringId || !this.chunkStorage || blob.size == 0) return;
32503
+ if (!this.proctoringId || !this.chunkStorage) return;
32573
32504
  const savePromise = (async () => {
32574
32505
  var _a2;
32575
32506
  try {
32576
- const arrayBuffer = await blob.arrayBuffer();
32577
32507
  await this.chunkStorage.saveChunk({
32578
32508
  proctoringId: this.proctoringId,
32579
32509
  chunkIndex: this.chunkIndex,
32580
- arrayBuffer,
32510
+ blob,
32581
32511
  timestamp: Date.now(),
32582
32512
  uploaded: 0,
32583
32513
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
@@ -32,7 +32,6 @@ export declare class BackgroundUploadService {
32
32
  flush(): Promise<void>;
33
33
  private syncOffset;
34
34
  private processQueue;
35
- private static concatArrayBuffers;
36
35
  private uploadData;
37
36
  static recoverPendingUploads(backend: BackendService, token: string): Promise<string[]>;
38
37
  private sleep;
@@ -2,7 +2,7 @@ export interface VideoChunk {
2
2
  id?: number;
3
3
  proctoringId: string;
4
4
  chunkIndex: number;
5
- arrayBuffer: ArrayBuffer;
5
+ blob: Blob;
6
6
  timestamp: number;
7
7
  uploaded: number;
8
8
  mimeType: string;
@@ -12,12 +12,11 @@ export declare class ChunkStorageService {
12
12
  private static readonly DB_VERSION;
13
13
  private static readonly STORE_NAME;
14
14
  private db;
15
- private static detachedArrayBufferCopy;
16
15
  connect(): Promise<IDBDatabase>;
17
16
  saveChunk(chunk: Omit<VideoChunk, "id">): Promise<number>;
18
17
  getPendingChunks(proctoringId: string): Promise<VideoChunk[]>;
19
18
  getAllChunks(proctoringId: string): Promise<VideoChunk[]>;
20
- deleteChunkIds(chunkIds: number[]): Promise<void>;
19
+ markAsUploaded(chunkId: number): Promise<void>;
21
20
  clearUploadedChunks(proctoringId: string): Promise<void>;
22
21
  clearAllChunks(proctoringId: string): Promise<void>;
23
22
  hasAnyPendingChunks(): Promise<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyproctor-hml",
3
- "version": "2.7.10",
3
+ "version": "2.7.11",
4
4
  "description": "Modulo web de gravação do EasyProctor",
5
5
  "main": "./index.js",
6
6
  "module": "./esm/index.js",