sera-ai 1.0.12 → 1.0.14

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/dist/index.js CHANGED
@@ -713,7 +713,7 @@ var useAudioRecovery = (reprocessSession) => {
713
713
  const store = transaction.objectStore(STORE_NAME);
714
714
  const session = {
715
715
  id: sessionId,
716
- audioChunks: [],
716
+ audioChunks: {},
717
717
  metadata: {
718
718
  ...metadata,
719
719
  timestamp: Date.now(),
@@ -2716,9 +2716,11 @@ var createAudioProcessorWorker = () => {
2716
2716
  }
2717
2717
 
2718
2718
  if (event.data.command === "resetUploadChunk") {
2719
+ // Only reset the upload flags, NOT the buffer
2720
+ // The buffer is cleared after chunk data is extracted and sent
2719
2721
  this._uploadChunk = false;
2720
2722
  this._uploadingChunk = false;
2721
- this._buffer = [];
2723
+ // NOTE: Do NOT clear buffer here - it's cleared in the chunk upload logic after data is sent
2722
2724
  }
2723
2725
 
2724
2726
  if (event.data.command === "pause") {
@@ -2866,6 +2868,7 @@ var useAudioRecorder = ({
2866
2868
  const [selectedModel, setSelectedModel] = React3__namespace.default.useState("new-large");
2867
2869
  const [isRetryingSession, setIsRetryingSession] = React3__namespace.default.useState(false);
2868
2870
  const [showRetrySessionPrompt, setShowRetrySessionPrompt] = React3__namespace.default.useState(false);
2871
+ const sessionHasFailedChunkRef = React3__namespace.default.useRef(false);
2869
2872
  const effectiveApiKey = apiKey;
2870
2873
  const {
2871
2874
  convertTranscriptionResponse,
@@ -2892,7 +2895,6 @@ var useAudioRecorder = ({
2892
2895
  markSessionFailed,
2893
2896
  retrySession,
2894
2897
  getFailedSession,
2895
- hasFailedSession,
2896
2898
  clearFailedSessions
2897
2899
  } = useAudioRecovery_default(async (audioChunks, metadata) => {
2898
2900
  try {
@@ -3055,15 +3057,6 @@ var useAudioRecorder = ({
3055
3057
  console.log("Speciality set, microphone validation will happen on recording start");
3056
3058
  }
3057
3059
  }, [speciality]);
3058
- React3__namespace.default.useEffect(() => {
3059
- const checkFailedSessions = async () => {
3060
- const failedSession = await hasFailedSession();
3061
- if (failedSession) {
3062
- setShowRetrySessionPrompt(true);
3063
- }
3064
- };
3065
- checkFailedSessions();
3066
- }, [hasFailedSession]);
3067
3060
  const clearAllSessions = React3__namespace.default.useCallback(async () => {
3068
3061
  await clearFailedSessions();
3069
3062
  setShowRetrySessionPrompt(false);
@@ -3078,17 +3071,28 @@ var useAudioRecorder = ({
3078
3071
  isFinalChunk,
3079
3072
  sequence,
3080
3073
  audioDataLength: audioData?.length,
3081
- requestFormat: selectedFormatRef.current
3082
- // Log the request format
3074
+ requestFormat: selectedFormatRef.current,
3075
+ sessionHasFailed: sessionHasFailedChunkRef.current
3083
3076
  });
3084
3077
  processorRef.current?.port.postMessage({ command: "resetUploadChunk" });
3085
3078
  if (audioData && localSessionIdRef.current && !retry) {
3086
3079
  try {
3080
+ console.log(`[DB] Saving chunk ${sequence} to IndexedDB session ${localSessionIdRef.current}`);
3087
3081
  await appendAudioToSession(localSessionIdRef.current, audioData, sequence);
3088
- console.log(`[SUCCESS] Successfully saved audio chunk ${sequence} to local session`);
3082
+ console.log(`[DB] \u2713 Successfully saved audio chunk ${sequence} to IndexedDB (${audioData.length} samples)`);
3089
3083
  } catch (error2) {
3090
- console.error(`[ERROR] Failed to save audio to local session:`, error2);
3084
+ console.error(`[DB] \u2717 Failed to save audio chunk ${sequence} to IndexedDB:`, error2);
3091
3085
  }
3086
+ } else {
3087
+ console.log(`[DB] Skipping IndexedDB save: audioData=${!!audioData}, sessionId=${localSessionIdRef.current}, retry=${retry}`);
3088
+ }
3089
+ if (sessionHasFailedChunkRef.current && !retry) {
3090
+ console.log(`[SKIP] Session has failed chunk - skipping server upload for sequence ${sequence}, continuing to record audio`);
3091
+ if (isFinalChunk) {
3092
+ setShowRetrySessionPrompt(true);
3093
+ setIsProcessing(false);
3094
+ }
3095
+ return;
3092
3096
  }
3093
3097
  try {
3094
3098
  const data = await pRetry__default.default(
@@ -3338,11 +3342,13 @@ var useAudioRecorder = ({
3338
3342
  }
3339
3343
  const isAbortError = err instanceof Error && err.name === "AbortError";
3340
3344
  const statusCode = err instanceof Error && err.message.includes("HTTP") ? parseInt(err.message.split("HTTP")[1].trim()) : null;
3341
- if (localSessionIdRef.current) {
3345
+ if (localSessionIdRef.current && !retry) {
3346
+ sessionHasFailedChunkRef.current = true;
3342
3347
  await markSessionFailed(
3343
3348
  localSessionIdRef.current,
3344
3349
  err instanceof Error ? err.message : "Unknown error"
3345
3350
  );
3351
+ console.log(`[FAIL] Chunk ${sequence} failed - session marked as failed, will continue recording audio locally`);
3346
3352
  if (isFinalChunk) {
3347
3353
  setShowRetrySessionPrompt(true);
3348
3354
  }
@@ -3433,16 +3439,12 @@ var useAudioRecorder = ({
3433
3439
  sequenceCounterRef.current = 0;
3434
3440
  nextExpectedSequenceRef.current = 0;
3435
3441
  receivedTranscriptionsRef.current.clear();
3442
+ sessionHasFailedChunkRef.current = false;
3443
+ chunkQueueRef.current = [];
3436
3444
  const localSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
3437
3445
  localSessionIdRef.current = localSessionId;
3438
3446
  sessionIdRef.current = null;
3439
3447
  console.log("Created local session ID for IndexedDB:", localSessionId);
3440
- await createSession(localSessionId, {
3441
- patientId,
3442
- patientName: patientName || void 0,
3443
- patientHistory: patientHistory || void 0,
3444
- speciality
3445
- });
3446
3448
  }
3447
3449
  const stream = await navigator.mediaDevices.getUserMedia({
3448
3450
  audio: {
@@ -3466,6 +3468,15 @@ var useAudioRecorder = ({
3466
3468
  });
3467
3469
  }
3468
3470
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
3471
+ if (!isPaused && localSessionIdRef.current) {
3472
+ await createSession(localSessionIdRef.current, {
3473
+ patientId,
3474
+ patientName: patientName || void 0,
3475
+ patientHistory: patientHistory || void 0,
3476
+ speciality,
3477
+ sampleRate: audioContext.sampleRate
3478
+ });
3479
+ }
3469
3480
  const processorUrl = createAudioProcessorWorker();
3470
3481
  await audioContext.audioWorklet.addModule(processorUrl);
3471
3482
  URL.revokeObjectURL(processorUrl);
@@ -3481,16 +3492,19 @@ var useAudioRecorder = ({
3481
3492
  totalRecordingTime,
3482
3493
  lastAudioTime
3483
3494
  } = event.data;
3484
- const sequence = sequenceCounterRef.current++;
3485
3495
  if (command === "finalChunk" && audioBuffer) {
3496
+ const sequence = sequenceCounterRef.current++;
3486
3497
  const audioArray = new Float32Array(audioBuffer);
3487
- console.log(`Received final chunk: ${audioArray.length} samples`);
3498
+ console.log(`[RECEIVE] Final chunk: ${audioArray.length} samples, sequence: ${sequence}`);
3488
3499
  enqueueChunk(audioArray, true, sequence);
3489
- } else if (command === "uploadChunk" && audioBuffer) {
3500
+ } else if (command === "chunk" && audioBuffer) {
3501
+ const sequence = sequenceCounterRef.current++;
3490
3502
  const audioArray = new Float32Array(audioBuffer);
3503
+ console.log(`[RECEIVE] Chunk: ${audioArray.length} samples, sequence: ${sequence}`);
3491
3504
  enqueueChunk(audioArray, false, sequence);
3492
3505
  } else if (command === "pauseChunk" && audioBuffer) {
3493
- console.log("Received pauseChunk with audioBuffer", audioBuffer);
3506
+ const sequence = sequenceCounterRef.current++;
3507
+ console.log(`[RECEIVE] Pause chunk with audioBuffer, sequence: ${sequence}`);
3494
3508
  enqueueChunk(new Float32Array(audioBuffer), false, sequence, true);
3495
3509
  } else if (command === "audioLevel") {
3496
3510
  setAudioLevel(level);
@@ -3568,6 +3582,14 @@ var useAudioRecorder = ({
3568
3582
  }, 500);
3569
3583
  }
3570
3584
  setIsRecording(false);
3585
+ console.log("\u{1F50D} Stop recording - checking for failed session:", {
3586
+ hasSessionFailed: sessionHasFailedChunkRef.current,
3587
+ localSessionId: localSessionIdRef.current
3588
+ });
3589
+ if (sessionHasFailedChunkRef.current && localSessionIdRef.current) {
3590
+ console.log("\u26A0\uFE0F Recording stopped with failed session - showing retry UI");
3591
+ setShowRetrySessionPrompt(true);
3592
+ }
3571
3593
  }, [uploadChunkInterval]);
3572
3594
  React3__namespace.default.useEffect(() => {
3573
3595
  const handleDeviceChange = async () => {
@@ -3641,9 +3663,12 @@ var useAudioRecorder = ({
3641
3663
  }, 47e3);
3642
3664
  setUploadChunkInterval(intervalId);
3643
3665
  }, [isPaused]);
3644
- const processNextChunkInQueue = React3__namespace.default.useCallback(() => {
3645
- if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0) return;
3666
+ const processNextChunkInQueue = React3__namespace.default.useCallback(async () => {
3667
+ if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0) {
3668
+ return;
3669
+ }
3646
3670
  const { chunk, isFinal, sequence, isPaused: isPaused2 = false } = chunkQueueRef.current.shift();
3671
+ console.log(`[QUEUE] Processing chunk ${sequence} from queue, remaining: ${chunkQueueRef.current.length}`);
3647
3672
  isProcessingQueueRef.current = true;
3648
3673
  uploadChunkToServer(chunk, isFinal, sequence, false, isPaused2).finally(() => {
3649
3674
  isProcessingQueueRef.current = false;
@@ -3652,9 +3677,13 @@ var useAudioRecorder = ({
3652
3677
  }, [uploadChunkToServer, isLoaded]);
3653
3678
  const enqueueChunk = React3__namespace.default.useCallback(
3654
3679
  (audioData, isFinalChunk, sequence, isPausedChunk = false) => {
3680
+ console.log(`[QUEUE] Enqueuing ${isFinalChunk ? "FINAL" : isPausedChunk ? "PAUSED" : "regular"} chunk ${sequence}, samples: ${audioData?.length || 0}, queue size: ${chunkQueueRef.current.length}`);
3655
3681
  if (isFinalChunk) {
3656
- console.log("Enqueuing final chunk:", sequence);
3657
- setIsProcessing(true);
3682
+ if (!sessionHasFailedChunkRef.current) {
3683
+ setIsProcessing(true);
3684
+ } else {
3685
+ console.log("\u26A0\uFE0F Session has failed - skipping processing state (IndexedDB save only)");
3686
+ }
3658
3687
  }
3659
3688
  chunkQueueRef.current.push({
3660
3689
  chunk: audioData,