med-scribe-alliance-ts-sdk 2.0.32 → 2.0.34

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/README.md CHANGED
@@ -198,6 +198,8 @@ await client.reset(); // stops recording if active, clears all state and caches
198
198
  - **`cancelSession()` does NOT trigger processing.** It stops the recorder locally, cleans up state, and tells the server the session is cancelled. No `endSession` call is made to the backend.
199
199
  - **All async methods return `SDKResult<T>`, never throw.** Always check `result.success` before accessing `result.data`. Errors are in `result.error`.
200
200
  - **The SDK validates inputs against the discovery document.** If the server doesn't support an upload type, language, or model you requested, you'll get a `ValidationError` before the API call is made.
201
+ - **Audio uploads go to a server-selected storage backend.** Discovery's `capabilities.storage_provider` (default `aws`) decides the backend; uploads go directly to it (e.g. S3 presigned POST).
202
+
201
203
  - **SharedWorker is optional.** If you provide `workerScriptUrl`, the SDK offloads MP3 compression and upload to a SharedWorker. If the worker fails to load, it silently falls back to main-thread processing.
202
204
  - **Microphone permission is requested on `startRecording()`.** The browser will prompt the user for mic access. If denied, you'll get an error via `onError` callback.
203
205
  - **`reset()` is a full teardown.** It destroys the transport, clears discovery cache, removes all callbacks, and sets the client back to uninitialized state. You'll need to call `init()` (or `startRecording()`) again after reset.
@@ -273,6 +275,42 @@ if (client.hasFailedUploads()) {
273
275
  }
274
276
  ```
275
277
 
278
+ ### Upload a Pre-recorded Audio File
279
+
280
+ For processing an already-recorded file (no mic/VAD). Create a session, then upload the file using the session's `upload_url`, then end the session and poll.
281
+
282
+ ```ts
283
+ // 1. Create a session (gives you upload_url)
284
+ const session = await client.createSession({
285
+ templates: ['soap'],
286
+ upload_type: 'single',
287
+ communication_protocol: 'http',
288
+ });
289
+ if (!session.success) return;
290
+
291
+ // 2. Upload the file to storage using the session's upload_url
292
+ const upload = await client.uploadAudioFile(
293
+ myAudioFile,
294
+ 'audio_0.mp3', // storage object name
295
+ session.data.upload_url
296
+ );
297
+ if (!upload.success) {
298
+ console.error('Upload failed:', upload.error.message);
299
+ return;
300
+ }
301
+ console.log(upload.data.status, upload.data.headers); // full storage response
302
+
303
+ // 3. End the session to trigger processing, then poll getSessionStatus()
304
+ await client.endSession(
305
+ { audio_files_sent: 1, audio_files_uploaded: 1 },
306
+ session.data.session_id
307
+ );
308
+ ```
309
+
310
+ - `file` is a `File`/`Blob`; `fileName` is the storage object name; `upload` is the `upload_url` object from the create-session response.
311
+ - Returns `UploadAudioFileResult` — `{ fileName, status, headers, response }` (the full storage response; the body is usually empty for an S3 presigned POST).
312
+ - Failures (network, malformed `upload_url`) come back as `UploadError` in `result.error`; an unknown provider as `UnsupportedStorageProviderError`.
313
+
276
314
  ### Update Auth Token
277
315
 
278
316
  ```ts
@@ -351,6 +389,7 @@ interface RecordingOptions {
351
389
  | `isRecordingPaused()` | `boolean` | Whether the active recording is paused. |
352
390
  | `retryFailedUploads()` | `SDKResult<RetryUploadResult>` | Retry uploads that failed during the last recording. |
353
391
  | `hasFailedUploads()` | `boolean` | Whether there are retryable failed uploads. |
392
+ | `uploadAudioFile(file, fileName, upload)` | `SDKResult<UploadAudioFileResult>` | Upload one pre-recorded audio file to storage using a session's `upload_url`. No mic/recorder. |
354
393
 
355
394
  ### Session
356
395
 
@@ -457,10 +496,12 @@ interface CreateSessionResponse {
457
496
  status: SessionStatus;
458
497
  created_at: string;
459
498
  expires_at: string;
460
- upload_url: string;
499
+ upload_url: SessionUploadInfo;
461
500
  patient_details?: PatientDetails;
462
501
  }
463
502
 
503
+ type SessionUploadInfo = Record<string, unknown>;
504
+
464
505
  interface PatchSessionRequest {
465
506
  user_status?: string;
466
507
  processing_status?: string;
@@ -585,7 +626,8 @@ console.log(result.data.session_id);
585
626
  | `DiscoveryError` | — | Discovery fetch/parse failed |
586
627
  | `TransportError` | — | Network / IPC failure |
587
628
  | `WorkerError` | — | SharedWorker failure |
588
- | `UploadError` | — | Audio upload failure |
629
+ | `UploadError` | — | Audio upload failure (failed transfer, or malformed `upload_url` payload). Has `failedFiles: string[]`. |
630
+ | `UnsupportedStorageProviderError` | — | Discovery advertised a `storage_provider` this SDK build has no wrapper for. Thrown at `startRecording`. Has `provider: string`. |
589
631
 
590
632
  ## SharedWorker Support
591
633
 
@@ -648,6 +690,8 @@ const client = new ScribeClient({
648
690
 
649
691
  IPC mode always uses main-thread compression (SharedWorker can't access the IPC bridge).
650
692
 
693
+ > **Presigned uploads over IPC need host support.** The SDK forwards `uploadFormFields` + base64 file (`blobData`) in the `IpcRequest`; your host must build the `multipart/form-data` POST (no `Authorization` header). Browser/HTTP mode works out of the box.
694
+
651
695
  ## Building from Source
652
696
 
653
697
  ```bash
package/dist/index.d.ts CHANGED
@@ -98,7 +98,8 @@ export declare enum ErrorCode {
98
98
  VAD_START_FAILED = "vad_start_failed",
99
99
  STOP_FAILED = "stop_failed",
100
100
  INTERNAL_RETRY_FAILED = "internal_retry_failed",
101
- SESSION_END_FAILED = "session_end_failed"
101
+ SESSION_END_FAILED = "session_end_failed",
102
+ UNSUPPORTED_STORAGE_PROVIDER = "unsupported_storage_provider"
102
103
  }
103
104
  export declare enum HttpStatus {
104
105
  OK = 200,
@@ -155,6 +156,11 @@ export declare class UploadError extends ScribeError {
155
156
  constructor(message: string, failedFiles: string[], details?: Record<string, any>);
156
157
  toJSON(): Record<string, any>;
157
158
  }
159
+ /** Thrown when discovery advertises a storage provider the SDK has no wrapper for. */
160
+ export declare class UnsupportedStorageProviderError extends ScribeError {
161
+ readonly provider: string;
162
+ constructor(provider: string);
163
+ }
158
164
  export interface ApiError {
159
165
  code: ErrorCode | string;
160
166
  message: string;
@@ -201,6 +207,13 @@ export interface TransportRequest {
201
207
  body?: any;
202
208
  isUpload?: boolean;
203
209
  uploadBlob?: Blob;
210
+ uploadFileName?: string;
211
+ /** Multipart form fields; when set, the request is multipart/form-data with uploadBlob as the file. */
212
+ uploadFormFields?: Record<string, string>;
213
+ /** Multipart field name for the file part. Defaults to 'file'. */
214
+ uploadFileFieldName?: string;
215
+ /** Attach the service Bearer + flavour header. Defaults to true; false for presigned uploads. */
216
+ attachAuth?: boolean;
204
217
  /** Additional HTTP status codes to treat as success (not throw). */
205
218
  acceptStatuses?: number[];
206
219
  maxRetries?: number;
@@ -229,8 +242,14 @@ export interface IpcRequest {
229
242
  url: string;
230
243
  headers?: Record<string, string>;
231
244
  body?: any;
232
- /** Base64-encoded blob data for uploads */
245
+ /** Base64-encoded file bytes for uploads. */
233
246
  blobData?: string;
247
+ /** Multipart fields for presigned uploads — host builds multipart from these + blobData (no auth header). */
248
+ uploadFormFields?: Record<string, string>;
249
+ /** Multipart field name for the file part. Defaults to 'file'. */
250
+ uploadFileFieldName?: string;
251
+ /** File name for the multipart file part. */
252
+ uploadFileName?: string;
234
253
  }
235
254
  export interface IpcResponse {
236
255
  correlationId: string;
@@ -304,6 +323,7 @@ export interface CapabilitiesInfo {
304
323
  upload_methods?: string[];
305
324
  webhook_delivery?: boolean;
306
325
  client_sdk_delivery?: boolean;
326
+ storage_provider?: string;
307
327
  }
308
328
  export interface ModelConfig {
309
329
  id: string;
@@ -332,6 +352,7 @@ export interface ResolvedConfig {
332
352
  autoDetectLanguage: boolean;
333
353
  supportedAudioFormats: string[];
334
354
  supportedUploadMethods: string[];
355
+ storageProvider: string;
335
356
  maxChunkDurationSeconds: number;
336
357
  /** modelId -> max session duration in seconds */
337
358
  maxSessionDurationSeconds: Map<string, number>;
@@ -360,12 +381,14 @@ export interface CreateSessionRequest {
360
381
  patient_details?: PatientDetails;
361
382
  session_id?: string;
362
383
  }
384
+ /** Provider-specific upload payload; validated/interpreted by the StorageProvider, not the session schema. */
385
+ export type SessionUploadInfo = Record<string, unknown>;
363
386
  export interface CreateSessionResponse {
364
387
  session_id: string;
365
388
  status: SessionStatus;
366
389
  created_at: string;
367
390
  expires_at: string;
368
- upload_url: string;
391
+ upload_url: SessionUploadInfo;
369
392
  patient_details?: PatientDetails;
370
393
  }
371
394
  export interface EndSessionRequest {
@@ -472,7 +495,9 @@ export interface RecordingOptions {
472
495
  }
473
496
  export interface RecorderConfig {
474
497
  accessToken?: string;
475
- uploadUrl: string;
498
+ /** Provider-specific upload payload from the create-session response. */
499
+ upload: SessionUploadInfo;
500
+ storageProvider: string;
476
501
  uploadHeaders: Record<string, string>;
477
502
  sessionId: string;
478
503
  }
@@ -527,6 +552,17 @@ export interface RetryUploadResult {
527
552
  /** File names that still failed after retry */
528
553
  stillFailed: string[];
529
554
  }
555
+ /** Result of ScribeClient.uploadAudioFile() — the storage backend's full response. */
556
+ export interface UploadAudioFileResult {
557
+ /** The storage object name used. */
558
+ fileName: string;
559
+ /** HTTP status from the storage backend (e.g. 204 for an S3 presigned POST). */
560
+ status: number;
561
+ /** Response headers from the storage backend (e.g. ETag). */
562
+ headers: Record<string, string>;
563
+ /** Raw response body from the storage backend (often empty for S3). */
564
+ response: unknown;
565
+ }
530
566
  export interface RecordingStateChangeEvent {
531
567
  type: RecordingState$1;
532
568
  timestamp: string;
@@ -648,7 +684,9 @@ export interface WorkerCompressAndUploadMessage {
648
684
  type: "compress_and_upload";
649
685
  audioFrames: Float32Array;
650
686
  fileName: string;
651
- uploadUrl: string;
687
+ storageProvider: string;
688
+ /** Provider-specific upload payload from the create-session response. */
689
+ upload: unknown;
652
690
  headers: Record<string, string>;
653
691
  }
654
692
  export interface WorkerWaitForUploadsMessage {
@@ -758,6 +796,14 @@ export declare class ScribeClient {
758
796
  * Call this after receiving a 'chunk_limit_reached' error to resume chunk uploads.
759
797
  */
760
798
  forceAllowMoreChunks(): void;
799
+ /**
800
+ * Upload a single pre-recorded audio file to storage.
801
+ * @param file - The audio file/blob to upload.
802
+ * @param upload - The `upload_url` payload from the create-session response.
803
+ * @param options.fileName - Storage object name. Defaults to the File's name,
804
+ * else `audio_0.<ext>` derived from the MIME type.
805
+ */
806
+ uploadAudioFile(file: Blob, fileName: string, upload: SessionUploadInfo): Promise<SDKResult<UploadAudioFileResult>>;
761
807
  /**
762
808
  * Create a session directly (without starting a recording).
763
809
  */
@@ -871,12 +917,49 @@ export declare class ScribeClient {
871
917
  */
872
918
  private createTransport;
873
919
  private resolveWorkerConfig;
920
+ /** Storage provider name from discovery; defaults to 'aws'. */
921
+ private getStorageProviderName;
874
922
  /**
875
923
  * Get the effective base URL — prefer discovery's base_url, fall back to config.
876
924
  */
877
925
  private getEffectiveBaseUrl;
878
926
  private validateConfig;
879
927
  }
928
+ /**
929
+ * Storage provider abstraction — the only part of the upload flow that varies
930
+ * between backends (AWS S3, GCP, ...). A provider is a pure, DOM-free request
931
+ * builder so it runs on the main thread, in the SharedWorker, and over IPC.
932
+ *
933
+ * To add a provider: implement StorageProvider in `<name>-provider.ts` and
934
+ * register it in `storage-provider-factory.ts`. No other changes needed.
935
+ */
936
+ export interface UploadContext {
937
+ fileName: string;
938
+ blob: Blob;
939
+ upload: unknown;
940
+ }
941
+ export interface PreparedUpload {
942
+ url: string;
943
+ method: "POST" | "PUT";
944
+ bodyMode: "multipart" | "binary";
945
+ formFields?: Record<string, string>;
946
+ fileFieldName?: string;
947
+ headers: Record<string, string>;
948
+ attachAuth: boolean;
949
+ }
950
+ export interface StorageProvider {
951
+ /** Matches discovery's `capabilities.storage_provider`. */
952
+ readonly name: string;
953
+ /** @throws UploadError if the upload payload is malformed. */
954
+ prepareUpload(ctx: UploadContext): PreparedUpload;
955
+ }
956
+ export declare class AwsS3StorageProvider implements StorageProvider {
957
+ readonly name = "aws";
958
+ prepareUpload({ fileName, blob, upload }: UploadContext): PreparedUpload;
959
+ }
960
+ export declare function isStorageProviderSupported(name: string): boolean;
961
+ /** @throws UnsupportedStorageProviderError if no wrapper is registered. */
962
+ export declare function getStorageProvider(name: string): StorageProvider;
880
963
  export declare class CallbackRegistry {
881
964
  private handlers;
882
965
  /**
@@ -1132,7 +1215,7 @@ export declare class RecordingManager {
1132
1215
  finalizeAfterExternalEndSession(sessionId: string): void;
1133
1216
  /**
1134
1217
  * Retry uploading audio files that failed during the last recording.
1135
- * Uses the stored MP3 blobs and the original upload URL.
1218
+ * Uses the stored MP3 blobs and the same storage provider as the recording.
1136
1219
  *
1137
1220
  * Each file is re-uploaded via transport.request() with retry logic.
1138
1221
  * Successfully retried files are removed from the retry context.
@@ -1142,6 +1225,10 @@ export declare class RecordingManager {
1142
1225
  * Create the appropriate recorder based on upload type.
1143
1226
  */
1144
1227
  private createRecorder;
1228
+ /** Storage provider name from discovery; defaults to 'aws'. */
1229
+ private getStorageProviderName;
1230
+ /** Validate the provider has a wrapper (throws UnsupportedStorageProviderError) and return its name. */
1231
+ private resolveStorageProviderName;
1145
1232
  /**
1146
1233
  * Build upload headers from the current auth state.
1147
1234
  */