easyproctor-hml 2.7.6 → 2.7.8

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,6 +13317,16 @@ 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
+ }
13320
13330
  /**
13321
13331
  * Abre a conexão com o IndexedDB, criando o banco e o object store se necessário.
13322
13332
  */
@@ -13356,10 +13366,18 @@ var _ChunkStorageService = class _ChunkStorageService {
13356
13366
  */
13357
13367
  async saveChunk(chunk) {
13358
13368
  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
+ };
13359
13377
  return new Promise((resolve, reject) => {
13360
13378
  const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
13361
13379
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
13362
- const request = store.add(chunk);
13380
+ const request = store.add(record);
13363
13381
  request.onsuccess = () => {
13364
13382
  resolve(request.result);
13365
13383
  };
@@ -13430,8 +13448,16 @@ var _ChunkStorageService = class _ChunkStorageService {
13430
13448
  resolve();
13431
13449
  return;
13432
13450
  }
13433
- chunk.uploaded = 1;
13434
- const putRequest = store.put(chunk);
13451
+ const updated = {
13452
+ id: chunk.id,
13453
+ proctoringId: chunk.proctoringId,
13454
+ chunkIndex: chunk.chunkIndex,
13455
+ arrayBuffer: _ChunkStorageService.detachedArrayBufferCopy(chunk.arrayBuffer),
13456
+ timestamp: chunk.timestamp,
13457
+ uploaded: 1,
13458
+ mimeType: chunk.mimeType
13459
+ };
13460
+ const putRequest = store.put(updated);
13435
13461
  putRequest.onsuccess = () => resolve();
13436
13462
  putRequest.onerror = () => {
13437
13463
  var _a2;
@@ -13551,8 +13577,8 @@ var _ChunkStorageService = class _ChunkStorageService {
13551
13577
  }
13552
13578
  };
13553
13579
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
13554
- /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
13555
- _ChunkStorageService.DB_VERSION = 2;
13580
+ /** v2: índices numéricos; v3: ArrayBuffer no payload; v4: cópia explícita de bytes (WebKit/iOS) */
13581
+ _ChunkStorageService.DB_VERSION = 4;
13556
13582
  _ChunkStorageService.STORE_NAME = "chunks";
13557
13583
  var ChunkStorageService = _ChunkStorageService;
13558
13584
 
@@ -13712,7 +13738,8 @@ var BackgroundUploadService = class _BackgroundUploadService {
13712
13738
  * @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
13713
13739
  */
13714
13740
  async processQueue(isFinal = false) {
13715
- var _a2, _b;
13741
+ var _a2, _b, _c2, _d, _e3, _f;
13742
+ console.log(`[BackgroundUpload] processQueue init`);
13716
13743
  if (this.isProcessing) return;
13717
13744
  this.isProcessing = true;
13718
13745
  try {
@@ -13725,8 +13752,10 @@ var BackgroundUploadService = class _BackgroundUploadService {
13725
13752
  return;
13726
13753
  }
13727
13754
  }
13755
+ console.log(`[BackgroundUpload] processQueue syncOffset ok`);
13728
13756
  const allChunks = await this.chunkStorage.getAllChunks(this.proctoringId);
13729
13757
  const pendingChunks = allChunks.filter((c3) => c3.uploaded === 0);
13758
+ console.log(`[BackgroundUpload] processQueue getAllChunks ok`);
13730
13759
  if (pendingChunks.length === 0 && !isFinal) {
13731
13760
  this.isProcessing = false;
13732
13761
  return;
@@ -13735,31 +13764,31 @@ var BackgroundUploadService = class _BackgroundUploadService {
13735
13764
  let virtualStart = this.totalBytesPurged;
13736
13765
  const chunksWithMeta = allChunks.map((c3) => {
13737
13766
  const start = virtualStart;
13738
- const end = start + c3.blob.size - 1;
13739
- virtualStart += c3.blob.size;
13767
+ const end = start + c3.arrayBuffer.byteLength - 1;
13768
+ virtualStart += c3.arrayBuffer.byteLength;
13740
13769
  return { chunk: c3, start, end };
13741
13770
  });
13742
- let combinedBlobParts = [];
13771
+ const combinedBufferParts = [];
13743
13772
  let lastProcessedChunkId = null;
13744
13773
  let finalChunkIndex = 0;
13745
- let mimeType = pendingChunks[0].mimeType;
13774
+ 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";
13746
13775
  for (const meta of chunksWithMeta) {
13747
13776
  if (this.currentOffset > meta.end) continue;
13748
13777
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
13749
- const chunkSlice = meta.chunk.blob.slice(sliceStart);
13750
- combinedBlobParts.push(chunkSlice);
13778
+ const chunkSlice = meta.chunk.arrayBuffer.slice(sliceStart);
13779
+ combinedBufferParts.push(chunkSlice);
13751
13780
  lastProcessedChunkId = meta.chunk.id;
13752
13781
  finalChunkIndex = meta.chunk.chunkIndex;
13753
13782
  }
13754
- if (combinedBlobParts.length === 0 && !isFinal) {
13783
+ if (combinedBufferParts.length === 0 && !isFinal) {
13755
13784
  this.isProcessing = false;
13756
13785
  return;
13757
13786
  }
13758
- let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
13759
- let sendableSize = fullBlob.size;
13787
+ const fullBuffer = _BackgroundUploadService.concatArrayBuffers(combinedBufferParts);
13788
+ let sendableSize = fullBuffer.byteLength;
13760
13789
  let totalSizeForHeader = void 0;
13761
13790
  if (!isFinal) {
13762
- sendableSize = Math.floor(fullBlob.size / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
13791
+ sendableSize = Math.floor(fullBuffer.byteLength / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
13763
13792
  if (sendableSize === 0) {
13764
13793
  console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
13765
13794
  this.isProcessing = false;
@@ -13768,20 +13797,23 @@ var BackgroundUploadService = class _BackgroundUploadService {
13768
13797
  } else {
13769
13798
  totalSizeForHeader = virtualStart;
13770
13799
  }
13771
- const blobToSend = fullBlob.slice(0, sendableSize);
13800
+ const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
13772
13801
  try {
13773
- await this.uploadData(blobToSend, mimeType, finalChunkIndex, totalSizeForHeader);
13802
+ await this.uploadData(bufferToSend, mimeType, finalChunkIndex, totalSizeForHeader);
13774
13803
  for (const meta of chunksWithMeta) {
13775
13804
  if (meta.chunk.uploaded === 0 && meta.end < this.currentOffset) {
13776
13805
  await this.chunkStorage.markAsUploaded(meta.chunk.id);
13777
13806
  this.retryCount.delete(meta.chunk.id);
13778
- (_a2 = this.onChunkUploaded) == null ? void 0 : _a2.call(this, meta.chunk.id, meta.chunk.chunkIndex);
13807
+ (_e3 = this.onChunkUploaded) == null ? void 0 : _e3.call(this, meta.chunk.id, meta.chunk.chunkIndex);
13779
13808
  console.log(`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} marcado como enviado.`);
13780
13809
  }
13781
13810
  }
13782
13811
  if (this.config.cleanAfterUpload) {
13783
13812
  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);
13813
+ const sizePurged = chunksToClear.reduce(
13814
+ (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
13815
+ 0
13816
+ );
13785
13817
  await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13786
13818
  if (sizePurged > 0) {
13787
13819
  this.totalBytesPurged += sizePurged;
@@ -13794,7 +13826,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
13794
13826
  }
13795
13827
  } catch (error) {
13796
13828
  console.error("[BackgroundUpload] Falha no upload:", error);
13797
- (_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
13829
+ (_f = this.onUploadError) == null ? void 0 : _f.call(this, lastProcessedChunkId || 0, error);
13798
13830
  }
13799
13831
  } catch (error) {
13800
13832
  console.error("[BackgroundUpload] Erro ao processar fila:", error);
@@ -13805,7 +13837,18 @@ var BackgroundUploadService = class _BackgroundUploadService {
13805
13837
  /**
13806
13838
  * Faz o upload bruto de dados para a sessão GCS.
13807
13839
  */
13808
- async uploadData(blob, mimeType, chunkIndex, totalSize) {
13840
+ static concatArrayBuffers(parts) {
13841
+ if (parts.length === 0) return new ArrayBuffer(0);
13842
+ const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
13843
+ const merged = new Uint8Array(total);
13844
+ let offset = 0;
13845
+ for (const p3 of parts) {
13846
+ merged.set(new Uint8Array(p3), offset);
13847
+ offset += p3.byteLength;
13848
+ }
13849
+ return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
13850
+ }
13851
+ async uploadData(data, mimeType, chunkIndex, totalSize) {
13809
13852
  const fileName = `EP_${this.proctoringId}_camera_0.webm`;
13810
13853
  if (!this.sessionUrl) {
13811
13854
  const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
@@ -13837,15 +13880,16 @@ var BackgroundUploadService = class _BackgroundUploadService {
13837
13880
  console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
13838
13881
  }
13839
13882
  const start = this.currentOffset;
13840
- const end = start + blob.size - 1;
13883
+ const end = start + data.byteLength - 1;
13841
13884
  const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
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})`);
13885
+ const contentRangeHeader = data.byteLength === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
13886
+ console.log(
13887
+ `[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
13888
+ );
13844
13889
  const response = await fetch(this.sessionUrl, {
13845
13890
  method: "PUT",
13846
13891
  headers: { "Content-Range": contentRangeHeader },
13847
- body: blob.size > 0 ? blob : null
13848
- // Usa null para garantir corpo vazio se necessário
13892
+ body: data.byteLength > 0 ? data : null
13849
13893
  });
13850
13894
  console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
13851
13895
  if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
@@ -13858,13 +13902,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
13858
13902
  const lastByte = parseInt(rangeHeader.split("-")[1], 10);
13859
13903
  this.currentOffset = lastByte + 1;
13860
13904
  } else {
13861
- this.currentOffset += blob.size;
13905
+ this.currentOffset += data.byteLength;
13862
13906
  }
13863
13907
  this.saveSessionState();
13864
13908
  trackers.registerUploadFile(
13865
13909
  this.proctoringId,
13866
13910
  `GCS Stream Upload
13867
- Size: ${blob.size}
13911
+ Size: ${data.byteLength}
13868
13912
  Range: ${start}-${end}
13869
13913
  Last Index: ${chunkIndex}`,
13870
13914
  "CameraChunk"
@@ -14407,10 +14451,11 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14407
14451
  const savePromise = (async () => {
14408
14452
  var _a2;
14409
14453
  try {
14454
+ const arrayBuffer = await blob.arrayBuffer();
14410
14455
  await this.chunkStorage.saveChunk({
14411
14456
  proctoringId: this.proctoringId,
14412
14457
  chunkIndex: this.chunkIndex,
14413
- blob,
14458
+ arrayBuffer,
14414
14459
  timestamp: Date.now(),
14415
14460
  uploaded: 0,
14416
14461
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
@@ -23730,15 +23775,11 @@ Error: ${error}`
23730
23775
  await this.recorder.stopAll();
23731
23776
  this.spyCam && this.spyCam.stopCheckSpyCam();
23732
23777
  this.appChecker && await this.appChecker.disconnectWebSocket();
23733
- trackers.registerError(this.proctoringId, `finish saveAllOnSession`);
23734
23778
  await this.recorder.saveAllOnSession();
23735
- trackers.registerError(this.proctoringId, `finish sendPendingRealtimeAlerts`);
23736
23779
  await this.sendPendingRealtimeAlerts();
23737
- trackers.registerError(this.proctoringId, `finish this.repository.save`);
23738
23780
  await this.repository.save(this.proctoringSession);
23739
23781
  let uploader;
23740
23782
  let uploaderServices;
23741
- trackers.registerError(this.proctoringId, `finish uploader`);
23742
23783
  if (versionVerify() !== "1.0.0.0") {
23743
23784
  uploader = new ProctoringUploader(
23744
23785
  this.proctoringSession,
@@ -23812,7 +23853,6 @@ Upload Services: ${uploaderServices}`,
23812
23853
  this.serviceType
23813
23854
  );
23814
23855
  }
23815
- trackers.registerError(this.proctoringId, `finish uploader success`);
23816
23856
  if (this.proctoringSession.alerts.length > 0) {
23817
23857
  await this.backend.saveAlerts(this.context, this.proctoringSession).catch((err) => {
23818
23858
  trackers.registerFinish(
@@ -23822,7 +23862,6 @@ Upload Services: ${uploaderServices}`,
23822
23862
  );
23823
23863
  });
23824
23864
  }
23825
- trackers.registerError(this.proctoringId, `finish saveAlerts ok`);
23826
23865
  await this.backend.finishAndSendUrls(this.context).then((finishResponse) => {
23827
23866
  var _a2, _b, _c2, _d;
23828
23867
  trackers.registerFinish(this.proctoringSession.id, true, "");
@@ -23834,7 +23873,6 @@ Upload Services: ${uploaderServices}`,
23834
23873
  "finish error: " + error
23835
23874
  );
23836
23875
  });
23837
- trackers.registerError(this.proctoringId, `finish call`);
23838
23876
  if (this.appChecker) {
23839
23877
  const externalSessionId = this.appChecker.getExternalCameraSessionId();
23840
23878
  if (externalSessionId != "null") {
package/index.js CHANGED
@@ -31414,6 +31414,16 @@ 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
+ }
31417
31427
  /**
31418
31428
  * Abre a conexão com o IndexedDB, criando o banco e o object store se necessário.
31419
31429
  */
@@ -31453,10 +31463,18 @@ var _ChunkStorageService = class _ChunkStorageService {
31453
31463
  */
31454
31464
  async saveChunk(chunk) {
31455
31465
  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
+ };
31456
31474
  return new Promise((resolve, reject) => {
31457
31475
  const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
31458
31476
  const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
31459
- const request = store.add(chunk);
31477
+ const request = store.add(record);
31460
31478
  request.onsuccess = () => {
31461
31479
  resolve(request.result);
31462
31480
  };
@@ -31527,8 +31545,16 @@ var _ChunkStorageService = class _ChunkStorageService {
31527
31545
  resolve();
31528
31546
  return;
31529
31547
  }
31530
- chunk.uploaded = 1;
31531
- const putRequest = store.put(chunk);
31548
+ const updated = {
31549
+ id: chunk.id,
31550
+ proctoringId: chunk.proctoringId,
31551
+ chunkIndex: chunk.chunkIndex,
31552
+ arrayBuffer: _ChunkStorageService.detachedArrayBufferCopy(chunk.arrayBuffer),
31553
+ timestamp: chunk.timestamp,
31554
+ uploaded: 1,
31555
+ mimeType: chunk.mimeType
31556
+ };
31557
+ const putRequest = store.put(updated);
31532
31558
  putRequest.onsuccess = () => resolve();
31533
31559
  putRequest.onerror = () => {
31534
31560
  var _a2;
@@ -31648,8 +31674,8 @@ var _ChunkStorageService = class _ChunkStorageService {
31648
31674
  }
31649
31675
  };
31650
31676
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
31651
- /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
31652
- _ChunkStorageService.DB_VERSION = 2;
31677
+ /** v2: índices numéricos; v3: ArrayBuffer no payload; v4: cópia explícita de bytes (WebKit/iOS) */
31678
+ _ChunkStorageService.DB_VERSION = 4;
31653
31679
  _ChunkStorageService.STORE_NAME = "chunks";
31654
31680
  var ChunkStorageService = _ChunkStorageService;
31655
31681
 
@@ -31809,7 +31835,8 @@ var BackgroundUploadService = class _BackgroundUploadService {
31809
31835
  * @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
31810
31836
  */
31811
31837
  async processQueue(isFinal = false) {
31812
- var _a2, _b;
31838
+ var _a2, _b, _c2, _d, _e3, _f;
31839
+ console.log(`[BackgroundUpload] processQueue init`);
31813
31840
  if (this.isProcessing) return;
31814
31841
  this.isProcessing = true;
31815
31842
  try {
@@ -31822,8 +31849,10 @@ var BackgroundUploadService = class _BackgroundUploadService {
31822
31849
  return;
31823
31850
  }
31824
31851
  }
31852
+ console.log(`[BackgroundUpload] processQueue syncOffset ok`);
31825
31853
  const allChunks = await this.chunkStorage.getAllChunks(this.proctoringId);
31826
31854
  const pendingChunks = allChunks.filter((c3) => c3.uploaded === 0);
31855
+ console.log(`[BackgroundUpload] processQueue getAllChunks ok`);
31827
31856
  if (pendingChunks.length === 0 && !isFinal) {
31828
31857
  this.isProcessing = false;
31829
31858
  return;
@@ -31832,31 +31861,31 @@ var BackgroundUploadService = class _BackgroundUploadService {
31832
31861
  let virtualStart = this.totalBytesPurged;
31833
31862
  const chunksWithMeta = allChunks.map((c3) => {
31834
31863
  const start = virtualStart;
31835
- const end = start + c3.blob.size - 1;
31836
- virtualStart += c3.blob.size;
31864
+ const end = start + c3.arrayBuffer.byteLength - 1;
31865
+ virtualStart += c3.arrayBuffer.byteLength;
31837
31866
  return { chunk: c3, start, end };
31838
31867
  });
31839
- let combinedBlobParts = [];
31868
+ const combinedBufferParts = [];
31840
31869
  let lastProcessedChunkId = null;
31841
31870
  let finalChunkIndex = 0;
31842
- let mimeType = pendingChunks[0].mimeType;
31871
+ 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";
31843
31872
  for (const meta of chunksWithMeta) {
31844
31873
  if (this.currentOffset > meta.end) continue;
31845
31874
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
31846
- const chunkSlice = meta.chunk.blob.slice(sliceStart);
31847
- combinedBlobParts.push(chunkSlice);
31875
+ const chunkSlice = meta.chunk.arrayBuffer.slice(sliceStart);
31876
+ combinedBufferParts.push(chunkSlice);
31848
31877
  lastProcessedChunkId = meta.chunk.id;
31849
31878
  finalChunkIndex = meta.chunk.chunkIndex;
31850
31879
  }
31851
- if (combinedBlobParts.length === 0 && !isFinal) {
31880
+ if (combinedBufferParts.length === 0 && !isFinal) {
31852
31881
  this.isProcessing = false;
31853
31882
  return;
31854
31883
  }
31855
- let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
31856
- let sendableSize = fullBlob.size;
31884
+ const fullBuffer = _BackgroundUploadService.concatArrayBuffers(combinedBufferParts);
31885
+ let sendableSize = fullBuffer.byteLength;
31857
31886
  let totalSizeForHeader = void 0;
31858
31887
  if (!isFinal) {
31859
- sendableSize = Math.floor(fullBlob.size / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
31888
+ sendableSize = Math.floor(fullBuffer.byteLength / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
31860
31889
  if (sendableSize === 0) {
31861
31890
  console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
31862
31891
  this.isProcessing = false;
@@ -31865,20 +31894,23 @@ var BackgroundUploadService = class _BackgroundUploadService {
31865
31894
  } else {
31866
31895
  totalSizeForHeader = virtualStart;
31867
31896
  }
31868
- const blobToSend = fullBlob.slice(0, sendableSize);
31897
+ const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
31869
31898
  try {
31870
- await this.uploadData(blobToSend, mimeType, finalChunkIndex, totalSizeForHeader);
31899
+ await this.uploadData(bufferToSend, mimeType, finalChunkIndex, totalSizeForHeader);
31871
31900
  for (const meta of chunksWithMeta) {
31872
31901
  if (meta.chunk.uploaded === 0 && meta.end < this.currentOffset) {
31873
31902
  await this.chunkStorage.markAsUploaded(meta.chunk.id);
31874
31903
  this.retryCount.delete(meta.chunk.id);
31875
- (_a2 = this.onChunkUploaded) == null ? void 0 : _a2.call(this, meta.chunk.id, meta.chunk.chunkIndex);
31904
+ (_e3 = this.onChunkUploaded) == null ? void 0 : _e3.call(this, meta.chunk.id, meta.chunk.chunkIndex);
31876
31905
  console.log(`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} marcado como enviado.`);
31877
31906
  }
31878
31907
  }
31879
31908
  if (this.config.cleanAfterUpload) {
31880
31909
  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);
31910
+ const sizePurged = chunksToClear.reduce(
31911
+ (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
31912
+ 0
31913
+ );
31882
31914
  await this.chunkStorage.clearUploadedChunks(this.proctoringId);
31883
31915
  if (sizePurged > 0) {
31884
31916
  this.totalBytesPurged += sizePurged;
@@ -31891,7 +31923,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
31891
31923
  }
31892
31924
  } catch (error) {
31893
31925
  console.error("[BackgroundUpload] Falha no upload:", error);
31894
- (_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
31926
+ (_f = this.onUploadError) == null ? void 0 : _f.call(this, lastProcessedChunkId || 0, error);
31895
31927
  }
31896
31928
  } catch (error) {
31897
31929
  console.error("[BackgroundUpload] Erro ao processar fila:", error);
@@ -31902,7 +31934,18 @@ var BackgroundUploadService = class _BackgroundUploadService {
31902
31934
  /**
31903
31935
  * Faz o upload bruto de dados para a sessão GCS.
31904
31936
  */
31905
- async uploadData(blob, mimeType, chunkIndex, totalSize) {
31937
+ static concatArrayBuffers(parts) {
31938
+ if (parts.length === 0) return new ArrayBuffer(0);
31939
+ const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
31940
+ const merged = new Uint8Array(total);
31941
+ let offset = 0;
31942
+ for (const p3 of parts) {
31943
+ merged.set(new Uint8Array(p3), offset);
31944
+ offset += p3.byteLength;
31945
+ }
31946
+ return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
31947
+ }
31948
+ async uploadData(data, mimeType, chunkIndex, totalSize) {
31906
31949
  const fileName = `EP_${this.proctoringId}_camera_0.webm`;
31907
31950
  if (!this.sessionUrl) {
31908
31951
  const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
@@ -31934,15 +31977,16 @@ var BackgroundUploadService = class _BackgroundUploadService {
31934
31977
  console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
31935
31978
  }
31936
31979
  const start = this.currentOffset;
31937
- const end = start + blob.size - 1;
31980
+ const end = start + data.byteLength - 1;
31938
31981
  const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
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})`);
31982
+ const contentRangeHeader = data.byteLength === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
31983
+ console.log(
31984
+ `[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
31985
+ );
31941
31986
  const response = await fetch(this.sessionUrl, {
31942
31987
  method: "PUT",
31943
31988
  headers: { "Content-Range": contentRangeHeader },
31944
- body: blob.size > 0 ? blob : null
31945
- // Usa null para garantir corpo vazio se necessário
31989
+ body: data.byteLength > 0 ? data : null
31946
31990
  });
31947
31991
  console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
31948
31992
  if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
@@ -31955,13 +31999,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
31955
31999
  const lastByte = parseInt(rangeHeader.split("-")[1], 10);
31956
32000
  this.currentOffset = lastByte + 1;
31957
32001
  } else {
31958
- this.currentOffset += blob.size;
32002
+ this.currentOffset += data.byteLength;
31959
32003
  }
31960
32004
  this.saveSessionState();
31961
32005
  trackers.registerUploadFile(
31962
32006
  this.proctoringId,
31963
32007
  `GCS Stream Upload
31964
- Size: ${blob.size}
32008
+ Size: ${data.byteLength}
31965
32009
  Range: ${start}-${end}
31966
32010
  Last Index: ${chunkIndex}`,
31967
32011
  "CameraChunk"
@@ -32504,10 +32548,11 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32504
32548
  const savePromise = (async () => {
32505
32549
  var _a2;
32506
32550
  try {
32551
+ const arrayBuffer = await blob.arrayBuffer();
32507
32552
  await this.chunkStorage.saveChunk({
32508
32553
  proctoringId: this.proctoringId,
32509
32554
  chunkIndex: this.chunkIndex,
32510
- blob,
32555
+ arrayBuffer,
32511
32556
  timestamp: Date.now(),
32512
32557
  uploaded: 0,
32513
32558
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
@@ -38979,15 +39024,11 @@ Error: ${error}`
38979
39024
  await this.recorder.stopAll();
38980
39025
  this.spyCam && this.spyCam.stopCheckSpyCam();
38981
39026
  this.appChecker && await this.appChecker.disconnectWebSocket();
38982
- trackers.registerError(this.proctoringId, `finish saveAllOnSession`);
38983
39027
  await this.recorder.saveAllOnSession();
38984
- trackers.registerError(this.proctoringId, `finish sendPendingRealtimeAlerts`);
38985
39028
  await this.sendPendingRealtimeAlerts();
38986
- trackers.registerError(this.proctoringId, `finish this.repository.save`);
38987
39029
  await this.repository.save(this.proctoringSession);
38988
39030
  let uploader;
38989
39031
  let uploaderServices;
38990
- trackers.registerError(this.proctoringId, `finish uploader`);
38991
39032
  if (versionVerify() !== "1.0.0.0") {
38992
39033
  uploader = new ProctoringUploader(
38993
39034
  this.proctoringSession,
@@ -39061,7 +39102,6 @@ Upload Services: ${uploaderServices}`,
39061
39102
  this.serviceType
39062
39103
  );
39063
39104
  }
39064
- trackers.registerError(this.proctoringId, `finish uploader success`);
39065
39105
  if (this.proctoringSession.alerts.length > 0) {
39066
39106
  await this.backend.saveAlerts(this.context, this.proctoringSession).catch((err) => {
39067
39107
  trackers.registerFinish(
@@ -39071,7 +39111,6 @@ Upload Services: ${uploaderServices}`,
39071
39111
  );
39072
39112
  });
39073
39113
  }
39074
- trackers.registerError(this.proctoringId, `finish saveAlerts ok`);
39075
39114
  await this.backend.finishAndSendUrls(this.context).then((finishResponse) => {
39076
39115
  var _a2, _b, _c2, _d;
39077
39116
  trackers.registerFinish(this.proctoringSession.id, true, "");
@@ -39083,7 +39122,6 @@ Upload Services: ${uploaderServices}`,
39083
39122
  "finish error: " + error
39084
39123
  );
39085
39124
  });
39086
- trackers.registerError(this.proctoringId, `finish call`);
39087
39125
  if (this.appChecker) {
39088
39126
  const externalSessionId = this.appChecker.getExternalCameraSessionId();
39089
39127
  if (externalSessionId != "null") {
@@ -32,6 +32,7 @@ export declare class BackgroundUploadService {
32
32
  flush(): Promise<void>;
33
33
  private syncOffset;
34
34
  private processQueue;
35
+ private static concatArrayBuffers;
35
36
  private uploadData;
36
37
  static recoverPendingUploads(backend: BackendService, token: string): Promise<string[]>;
37
38
  private sleep;
@@ -2,7 +2,7 @@ export interface VideoChunk {
2
2
  id?: number;
3
3
  proctoringId: string;
4
4
  chunkIndex: number;
5
- blob: Blob;
5
+ arrayBuffer: ArrayBuffer;
6
6
  timestamp: number;
7
7
  uploaded: number;
8
8
  mimeType: string;
@@ -12,6 +12,7 @@ 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;
15
16
  connect(): Promise<IDBDatabase>;
16
17
  saveChunk(chunk: Omit<VideoChunk, "id">): Promise<number>;
17
18
  getPendingChunks(proctoringId: string): Promise<VideoChunk[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyproctor-hml",
3
- "version": "2.7.6",
3
+ "version": "2.7.8",
4
4
  "description": "Modulo web de gravação do EasyProctor",
5
5
  "main": "./index.js",
6
6
  "module": "./esm/index.js",