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/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;
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,10 +31822,8 @@ 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;
@@ -31852,34 +31832,31 @@ var BackgroundUploadService = class _BackgroundUploadService {
31852
31832
  let virtualStart = this.totalBytesPurged;
31853
31833
  const chunksWithMeta = allChunks.map((c3) => {
31854
31834
  const start = virtualStart;
31855
- const end = start + c3.arrayBuffer.byteLength - 1;
31856
- virtualStart += c3.arrayBuffer.byteLength;
31835
+ const end = start + c3.blob.size - 1;
31836
+ virtualStart += c3.blob.size;
31857
31837
  return { chunk: c3, start, end };
31858
31838
  });
31859
- const combinedBufferParts = [];
31839
+ let combinedBlobParts = [];
31860
31840
  let lastProcessedChunkId = null;
31861
31841
  let finalChunkIndex = 0;
31862
- 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";
31863
- console.log(`[BackgroundUpload] passo 1 ok`);
31842
+ let mimeType = pendingChunks[0].mimeType;
31864
31843
  for (const meta of chunksWithMeta) {
31865
31844
  if (this.currentOffset > meta.end) continue;
31866
31845
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
31867
- const chunkSlice = meta.chunk.arrayBuffer.slice(sliceStart);
31868
- combinedBufferParts.push(chunkSlice);
31846
+ const chunkSlice = meta.chunk.blob.slice(sliceStart);
31847
+ combinedBlobParts.push(chunkSlice);
31869
31848
  lastProcessedChunkId = meta.chunk.id;
31870
31849
  finalChunkIndex = meta.chunk.chunkIndex;
31871
31850
  }
31872
- console.log(`[BackgroundUpload] passo 2 ok`);
31873
- if (combinedBufferParts.length === 0 && !isFinal) {
31851
+ if (combinedBlobParts.length === 0 && !isFinal) {
31874
31852
  this.isProcessing = false;
31875
31853
  return;
31876
31854
  }
31877
- const fullBuffer = _BackgroundUploadService.concatArrayBuffers(combinedBufferParts);
31878
- console.log(`[BackgroundUpload] passo 3 ok`);
31879
- let sendableSize = fullBuffer.byteLength;
31855
+ let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
31856
+ let sendableSize = fullBlob.size;
31880
31857
  let totalSizeForHeader = void 0;
31881
31858
  if (!isFinal) {
31882
- 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;
31883
31860
  if (sendableSize === 0) {
31884
31861
  console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
31885
31862
  this.isProcessing = false;
@@ -31888,47 +31865,33 @@ var BackgroundUploadService = class _BackgroundUploadService {
31888
31865
  } else {
31889
31866
  totalSizeForHeader = virtualStart;
31890
31867
  }
31891
- console.log(`[BackgroundUpload] passo 4 ok`);
31892
- const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
31868
+ const blobToSend = fullBlob.slice(0, sendableSize);
31893
31869
  try {
31894
- await this.uploadData(bufferToSend, mimeType, finalChunkIndex, totalSizeForHeader);
31895
- console.log(`[BackgroundUpload] passo 5 ok`);
31896
- const fullySent = chunksWithMeta.filter(
31897
- (meta) => meta.chunk.uploaded === 0 && meta.end < this.currentOffset && meta.chunk.id != null
31898
- );
31899
- const sizePurged = fullySent.reduce(
31900
- (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
31901
- 0
31902
- );
31903
- const idsToDelete = fullySent.map((m3) => m3.chunk.id);
31904
- if (idsToDelete.length > 0) {
31905
- await this.chunkStorage.deleteChunkIds(idsToDelete);
31906
- 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);
31907
31874
  this.retryCount.delete(meta.chunk.id);
31908
- (_e3 = this.onChunkUploaded) == null ? void 0 : _e3.call(this, meta.chunk.id, meta.chunk.chunkIndex);
31909
- console.log(
31910
- `[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
31911
- );
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.`);
31912
31877
  }
31913
31878
  }
31914
- console.log(`[BackgroundUpload] passo 6 ok`);
31915
- await this.chunkStorage.clearUploadedChunks(this.proctoringId);
31916
- console.log(`[BackgroundUpload] passo 7 ok`);
31917
- if (this.config.cleanAfterUpload && sizePurged > 0) {
31918
- this.totalBytesPurged += sizePurged;
31919
- this.saveSessionState();
31920
- console.log(
31921
- `[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`
31922
- );
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
+ }
31923
31888
  }
31924
- console.log(`[BackgroundUpload] passo 8 ok`);
31925
31889
  if (isFinal) {
31926
31890
  this.clearSessionState();
31927
31891
  }
31928
- console.log(`[BackgroundUpload] passo 9 ok`);
31929
31892
  } catch (error) {
31930
31893
  console.error("[BackgroundUpload] Falha no upload:", error);
31931
- (_f = this.onUploadError) == null ? void 0 : _f.call(this, lastProcessedChunkId || 0, error);
31894
+ (_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
31932
31895
  }
31933
31896
  } catch (error) {
31934
31897
  console.error("[BackgroundUpload] Erro ao processar fila:", error);
@@ -31939,18 +31902,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
31939
31902
  /**
31940
31903
  * Faz o upload bruto de dados para a sessão GCS.
31941
31904
  */
31942
- static concatArrayBuffers(parts) {
31943
- if (parts.length === 0) return new ArrayBuffer(0);
31944
- const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
31945
- const merged = new Uint8Array(total);
31946
- let offset = 0;
31947
- for (const p3 of parts) {
31948
- merged.set(new Uint8Array(p3), offset);
31949
- offset += p3.byteLength;
31950
- }
31951
- return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
31952
- }
31953
- async uploadData(data, mimeType, chunkIndex, totalSize) {
31905
+ async uploadData(blob, mimeType, chunkIndex, totalSize) {
31954
31906
  const fileName = `EP_${this.proctoringId}_camera_0.webm`;
31955
31907
  if (!this.sessionUrl) {
31956
31908
  const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
@@ -31982,16 +31934,15 @@ var BackgroundUploadService = class _BackgroundUploadService {
31982
31934
  console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
31983
31935
  }
31984
31936
  const start = this.currentOffset;
31985
- const end = start + data.byteLength - 1;
31937
+ const end = start + blob.size - 1;
31986
31938
  const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
31987
- const contentRangeHeader = data.byteLength === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
31988
- console.log(
31989
- `[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
31990
- );
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})`);
31991
31941
  const response = await fetch(this.sessionUrl, {
31992
31942
  method: "PUT",
31993
31943
  headers: { "Content-Range": contentRangeHeader },
31994
- body: data.byteLength > 0 ? data : null
31944
+ body: blob.size > 0 ? blob : null
31945
+ // Usa null para garantir corpo vazio se necessário
31995
31946
  });
31996
31947
  console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
31997
31948
  if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
@@ -32004,13 +31955,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
32004
31955
  const lastByte = parseInt(rangeHeader.split("-")[1], 10);
32005
31956
  this.currentOffset = lastByte + 1;
32006
31957
  } else {
32007
- this.currentOffset += data.byteLength;
31958
+ this.currentOffset += blob.size;
32008
31959
  }
32009
31960
  this.saveSessionState();
32010
31961
  trackers.registerUploadFile(
32011
31962
  this.proctoringId,
32012
31963
  `GCS Stream Upload
32013
- Size: ${data.byteLength}
31964
+ Size: ${blob.size}
32014
31965
  Range: ${start}-${end}
32015
31966
  Last Index: ${chunkIndex}`,
32016
31967
  "CameraChunk"
@@ -32145,7 +32096,7 @@ var _CameraRecorder = class _CameraRecorder {
32145
32096
  * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
32146
32097
  */
32147
32098
  get isChunkEnabled() {
32148
- return !!this.proctoringId && this.options.proctoringType === "REALTIME" && !isSafeBrowser();
32099
+ return false;
32149
32100
  }
32150
32101
  setProctoringId(proctoringId2) {
32151
32102
  this.proctoringId = proctoringId2;
@@ -32549,15 +32500,14 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32549
32500
  * Salva o chunk no IndexedDB para persistência e recuperação.
32550
32501
  */
32551
32502
  async handleNewChunk(blob, idx) {
32552
- if (!this.proctoringId || !this.chunkStorage || blob.size == 0) return;
32503
+ if (!this.proctoringId || !this.chunkStorage) return;
32553
32504
  const savePromise = (async () => {
32554
32505
  var _a2;
32555
32506
  try {
32556
- const arrayBuffer = await blob.arrayBuffer();
32557
32507
  await this.chunkStorage.saveChunk({
32558
32508
  proctoringId: this.proctoringId,
32559
32509
  chunkIndex: this.chunkIndex,
32560
- arrayBuffer,
32510
+ blob,
32561
32511
  timestamp: Date.now(),
32562
32512
  uploaded: 0,
32563
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.9",
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",