med-scribe-alliance-ts-sdk 2.0.0 → 2.0.2
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 +237 -285
- package/dist/index.d.ts +1067 -17
- package/dist/index.mjs +1823 -0
- package/dist/worker.bundle.js +14 -0
- package/package.json +19 -5
- package/dist/audio/audio-buffer-manager.d.ts +0 -60
- package/dist/audio/audio-buffer-manager.d.ts.map +0 -1
- package/dist/audio/audio-buffer-manager.js +0 -109
- package/dist/audio/audio-file-manager.d.ts +0 -85
- package/dist/audio/audio-file-manager.d.ts.map +0 -1
- package/dist/audio/audio-file-manager.js +0 -159
- package/dist/audio/constants.d.ts +0 -31
- package/dist/audio/constants.d.ts.map +0 -1
- package/dist/audio/constants.js +0 -39
- package/dist/audio/index.d.ts +0 -7
- package/dist/audio/index.d.ts.map +0 -1
- package/dist/audio/index.js +0 -5
- package/dist/audio/mp3-encoder.d.ts +0 -17
- package/dist/audio/mp3-encoder.d.ts.map +0 -1
- package/dist/audio/mp3-encoder.js +0 -45
- package/dist/audio/vad-client.d.ts +0 -119
- package/dist/audio/vad-client.d.ts.map +0 -1
- package/dist/audio/vad-client.js +0 -326
- package/dist/callbacks/callback-registry.d.ts +0 -37
- package/dist/callbacks/callback-registry.d.ts.map +0 -1
- package/dist/callbacks/callback-registry.js +0 -72
- package/dist/client.d.ts +0 -168
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -367
- package/dist/constants/index.d.ts +0 -82
- package/dist/constants/index.d.ts.map +0 -1
- package/dist/constants/index.js +0 -98
- package/dist/discovery/discovery-manager.d.ts +0 -43
- package/dist/discovery/discovery-manager.d.ts.map +0 -1
- package/dist/discovery/discovery-manager.js +0 -126
- package/dist/discovery/resolved-config.d.ts +0 -11
- package/dist/discovery/resolved-config.d.ts.map +0 -1
- package/dist/discovery/resolved-config.js +0 -40
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -22
- package/dist/recording/chunked-recorder.d.ts +0 -98
- package/dist/recording/chunked-recorder.d.ts.map +0 -1
- package/dist/recording/chunked-recorder.js +0 -255
- package/dist/recording/index.d.ts +0 -5
- package/dist/recording/index.d.ts.map +0 -1
- package/dist/recording/index.js +0 -3
- package/dist/recording/recording-manager.d.ts +0 -143
- package/dist/recording/recording-manager.d.ts.map +0 -1
- package/dist/recording/recording-manager.js +0 -491
- package/dist/recording/single-recorder.d.ts +0 -58
- package/dist/recording/single-recorder.d.ts.map +0 -1
- package/dist/recording/single-recorder.js +0 -203
- package/dist/session/session-manager.d.ts +0 -55
- package/dist/session/session-manager.d.ts.map +0 -1
- package/dist/session/session-manager.js +0 -196
- package/dist/transport/http-transport.d.ts +0 -48
- package/dist/transport/http-transport.d.ts.map +0 -1
- package/dist/transport/http-transport.js +0 -227
- package/dist/transport/ipc-transport.d.ts +0 -61
- package/dist/transport/ipc-transport.d.ts.map +0 -1
- package/dist/transport/ipc-transport.js +0 -272
- package/dist/transport/transport.interface.d.ts +0 -9
- package/dist/transport/transport.interface.d.ts.map +0 -1
- package/dist/transport/transport.interface.js +0 -8
- package/dist/types/callbacks.d.ts +0 -113
- package/dist/types/callbacks.d.ts.map +0 -1
- package/dist/types/callbacks.js +0 -4
- package/dist/types/common.d.ts +0 -25
- package/dist/types/common.d.ts.map +0 -1
- package/dist/types/common.js +0 -4
- package/dist/types/config.d.ts +0 -24
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js +0 -4
- package/dist/types/discovery.d.ts +0 -77
- package/dist/types/discovery.d.ts.map +0 -1
- package/dist/types/discovery.js +0 -4
- package/dist/types/index.d.ts +0 -12
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/recording.d.ts +0 -66
- package/dist/types/recording.d.ts.map +0 -1
- package/dist/types/recording.js +0 -4
- package/dist/types/session.d.ts +0 -75
- package/dist/types/session.d.ts.map +0 -1
- package/dist/types/session.js +0 -4
- package/dist/types/transport.d.ts +0 -49
- package/dist/types/transport.d.ts.map +0 -1
- package/dist/types/transport.js +0 -4
- package/dist/types/worker.d.ts +0 -39
- package/dist/types/worker.d.ts.map +0 -1
- package/dist/types/worker.js +0 -4
- package/dist/utils/errors.d.ts +0 -49
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -98
- package/dist/utils/retry.d.ts +0 -22
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/retry.js +0 -58
- package/dist/validation/schemas/discovery-schema.d.ts +0 -59
- package/dist/validation/schemas/discovery-schema.d.ts.map +0 -1
- package/dist/validation/schemas/discovery-schema.js +0 -69
- package/dist/validation/schemas/request-schema.d.ts +0 -20
- package/dist/validation/schemas/request-schema.d.ts.map +0 -1
- package/dist/validation/schemas/request-schema.js +0 -23
- package/dist/validation/schemas/session-schema.d.ts +0 -63
- package/dist/validation/schemas/session-schema.d.ts.map +0 -1
- package/dist/validation/schemas/session-schema.js +0 -78
- package/dist/validation/validator.d.ts +0 -34
- package/dist/validation/validator.d.ts.map +0 -1
- package/dist/validation/validator.js +0 -114
- package/dist/worker/index.d.ts +0 -3
- package/dist/worker/index.d.ts.map +0 -1
- package/dist/worker/index.js +0 -1
- package/dist/worker/shared-worker.d.ts +0 -17
- package/dist/worker/shared-worker.d.ts.map +0 -1
- package/dist/worker/shared-worker.js +0 -194
- package/dist/worker/worker-manager.d.ts +0 -88
- package/dist/worker/worker-manager.d.ts.map +0 -1
- package/dist/worker/worker-manager.js +0 -270
package/README.md
CHANGED
|
@@ -1,382 +1,334 @@
|
|
|
1
|
+
# MedScribe Alliance TS SDK
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
A TypeScript SDK for the [MedScribe Alliance Protocol](https://github.com/MedScribeAlliance/scribe-emr-protocol), providing a clean and type-safe interface for medical transcription services.
|
|
5
|
-
|
|
6
|
-
## Features
|
|
7
|
-
|
|
8
|
-
- ✅ **Protocol Compliant**: Fully implements the MedScribe Alliance Protocol specification
|
|
9
|
-
- ✅ **Type-Safe**: Complete TypeScript definitions for all API interactions
|
|
10
|
-
- ✅ **Auto-Discovery**: Automatic service capability discovery via well-known endpoint
|
|
11
|
-
- ✅ **Schema Validation**: AJV-powered validation against OpenAPI schemas
|
|
12
|
-
- ✅ **Error Handling**: Comprehensive error handling with typed error codes
|
|
13
|
-
- ✅ **Event System**: Built-in event emitter for session lifecycle events
|
|
14
|
-
- ✅ **Polling Support**: Automatic polling for session completion
|
|
3
|
+
TypeScript SDK for the [MedScribe Alliance Protocol](https://github.com/MedScribeAlliance/scribe-emr-protocol) — handles discovery, recording, audio chunking (VAD), MP3 compression, upload, session lifecycle, and output retrieval.
|
|
15
4
|
|
|
16
5
|
## Installation
|
|
17
6
|
|
|
18
7
|
```bash
|
|
19
|
-
npm install scribe-
|
|
8
|
+
npm install med-scribe-alliance-ts-sdk
|
|
20
9
|
```
|
|
21
10
|
|
|
11
|
+
Peer dependencies (installed automatically):
|
|
12
|
+
|
|
13
|
+
- `@ricky0123/vad-web` — Voice Activity Detection
|
|
14
|
+
- `@breezystack/lamejs` — MP3 encoding
|
|
15
|
+
- `zod` — Schema validation
|
|
16
|
+
|
|
22
17
|
## Quick Start
|
|
23
18
|
|
|
24
|
-
```
|
|
25
|
-
import { ScribeClient } from 'scribe-
|
|
19
|
+
```ts
|
|
20
|
+
import { ScribeClient } from 'med-scribe-alliance-ts-sdk';
|
|
26
21
|
|
|
27
|
-
// Initialize the SDK
|
|
28
22
|
const client = new ScribeClient({
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
baseUrl: 'https://api.example.com/voice/api/v2',
|
|
24
|
+
accessToken: 'your-bearer-token',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Register callbacks
|
|
28
|
+
client.registerCallback('onUploadEvent', (event) => {
|
|
29
|
+
if (event.type === 'progress') {
|
|
30
|
+
console.log(`Uploaded ${event.data.successCount}/${event.data.totalCount}`);
|
|
31
|
+
}
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
client.registerCallback('onError', (event) => {
|
|
35
|
+
console.error(`[${event.error.code}] ${event.error.message}`);
|
|
36
|
+
});
|
|
36
37
|
|
|
37
|
-
// Start
|
|
38
|
-
const
|
|
39
|
-
templates: ['soap', '
|
|
40
|
-
|
|
41
|
-
model: 'pro',
|
|
38
|
+
// Start recording — creates session, starts mic, begins chunked upload
|
|
39
|
+
const result = await client.startRecording({
|
|
40
|
+
templates: ['soap', 'prescription'],
|
|
41
|
+
uploadType: 'chunked',
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
console.
|
|
44
|
+
if (!result.success) {
|
|
45
|
+
console.error(result.error);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ... user speaks ...
|
|
46
49
|
|
|
47
|
-
//
|
|
50
|
+
// Stop recording — flushes remaining audio, waits for uploads, ends session
|
|
51
|
+
const stopResult = await client.endRecording();
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log(
|
|
53
|
+
if (stopResult.success) {
|
|
54
|
+
console.log(`${stopResult.data.totalFiles} files uploaded`);
|
|
55
|
+
console.log(`${stopResult.data.failedUploads.length} failed`);
|
|
56
|
+
}
|
|
52
57
|
|
|
53
|
-
// Poll for
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
console.log(
|
|
58
|
+
// Poll for results
|
|
59
|
+
const status = await client.getSessionStatus(result.data.session_id, {
|
|
60
|
+
poll: {
|
|
61
|
+
maxAttempts: 60,
|
|
62
|
+
intervalMs: 2000,
|
|
63
|
+
onProgress: (s) => console.log(`Status: ${s.status}`),
|
|
59
64
|
},
|
|
60
65
|
});
|
|
61
|
-
|
|
62
|
-
// Access the results
|
|
63
|
-
if (result.status === 'completed') {
|
|
64
|
-
console.log('Transcript:', result.transcript);
|
|
65
|
-
console.log('Templates:', result.templates);
|
|
66
|
-
}
|
|
67
66
|
```
|
|
68
67
|
|
|
69
|
-
##
|
|
68
|
+
## Configuration
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
```ts
|
|
71
|
+
interface ScribeSDKConfig {
|
|
72
|
+
/** Base URL of the scribe service (required) */
|
|
73
|
+
baseUrl: string;
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
/** Bearer token for authentication */
|
|
76
|
+
accessToken?: string;
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
/** Transport mode: 'direct' (HTTP) or 'ipc' (Electron). Default: 'direct' */
|
|
79
|
+
mode?: 'direct' | 'ipc';
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```
|
|
81
|
+
/** IPC bridge — required when mode is 'ipc' */
|
|
82
|
+
ipcTransport?: IpcBridge;
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- `baseUrl` (optional): Base URL of the Scribe service
|
|
84
|
-
- `debug` (optional): Enable debug logging (default: `false`)
|
|
85
|
-
- `autoDiscovery` (optional): Auto-fetch service capabilities (default: `true`)
|
|
84
|
+
/** SharedWorker: true (require), false (disable), 'auto' (detect). Default: 'auto' */
|
|
85
|
+
useWorker?: boolean | 'auto';
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
/** URL to worker.bundle.js. Use getWorkerUrl() to resolve. */
|
|
88
|
+
workerScriptUrl?: string;
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
/** Enable debug logging. Default: false */
|
|
91
|
+
debug?: boolean;
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
/** Auto-fetch discovery document on init. Default: true */
|
|
94
|
+
autoDiscovery?: boolean;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
## API Reference
|
|
99
|
+
|
|
100
|
+
### Lifecycle
|
|
101
|
+
|
|
102
|
+
| Method | Returns | Description |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `init()` | `SDKResult<void>` | Fetch discovery document. Called automatically by `startRecording`. |
|
|
105
|
+
| `reset()` | `Promise<void>` | Stop recording, clear all state and caches. |
|
|
106
|
+
|
|
107
|
+
### Recording
|
|
108
|
+
|
|
109
|
+
| Method | Returns | Description |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| `startRecording(options)` | `SDKResult<CreateSessionResponse>` | Create session + start mic + begin upload. |
|
|
112
|
+
| `startRecordingWithSession(session, options?)` | `SDKResult<void>` | Attach recorder to an existing session. |
|
|
113
|
+
| `pauseRecording()` | `void` | Pause VAD (mic stays open, no chunks created). |
|
|
114
|
+
| `resumeRecording()` | `void` | Resume VAD processing. |
|
|
115
|
+
| `endRecording()` | `SDKResult<StopRecordingResult>` | Stop mic, flush audio, wait for uploads, end session. |
|
|
116
|
+
| `isRecording()` | `boolean` | Whether a recording is active. |
|
|
117
|
+
| `isRecordingPaused()` | `boolean` | Whether the active recording is paused. |
|
|
118
|
+
| `retryFailedUploads()` | `SDKResult<RetryUploadResult>` | Retry uploads that failed during the last recording. |
|
|
119
|
+
| `hasFailedUploads()` | `boolean` | Whether there are retryable failed uploads. |
|
|
120
|
+
|
|
121
|
+
#### Recording Options
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
interface RecordingOptions {
|
|
125
|
+
templates: string[]; // Template IDs for extraction (required)
|
|
126
|
+
model?: string; // Model ID from discovery
|
|
127
|
+
languageHint?: string[]; // Language codes for audio input
|
|
128
|
+
transcriptLanguage?: string[];// Language codes for transcript output
|
|
129
|
+
uploadType?: string; // 'chunked' | 'single' (default: 'chunked')
|
|
130
|
+
communicationProtocol?: string;// 'http' | 'websocket' (default: 'http')
|
|
131
|
+
additionalData?: Record<string, any>;
|
|
132
|
+
deviceId?: string; // Specific microphone device ID
|
|
133
|
+
}
|
|
95
134
|
```
|
|
96
135
|
|
|
97
|
-
|
|
136
|
+
### Session
|
|
98
137
|
|
|
99
|
-
|
|
138
|
+
| Method | Returns | Description |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `createSession(request)` | `SDKResult<CreateSessionResponse>` | Create a session without starting a recording. |
|
|
141
|
+
| `getSessionStatus(sessionId?, options?)` | `SDKResult<GetSessionStatusResponse>` | Get status. Pass `{ poll: PollOptions }` to poll until completion. |
|
|
142
|
+
| `getCurrentSession()` | `CreateSessionResponse \| null` | Get the active session if any. |
|
|
100
143
|
|
|
101
|
-
|
|
102
|
-
- `templates` (required): Array of template IDs to extract (e.g., `['soap', 'medications']`)
|
|
103
|
-
- `model` (optional): Model ID from discovery
|
|
104
|
-
- `languageHint` (optional): Array of ISO 639-1 language codes for audio input (e.g., `['en', 'es']`)
|
|
105
|
-
- `transcriptLanguage` (optional): Array of ISO 639-1 codes for transcript output (e.g., `['en']`)
|
|
106
|
-
- `uploadType` (optional): `'chunked'` or `'single'`
|
|
107
|
-
- `communicationProtocol` (optional): `'http'`, `'websocket'`, or `'rpc'` (default: `'http'`)
|
|
108
|
-
- `additionalData` (optional): Pass-through data for your application
|
|
144
|
+
#### Polling
|
|
109
145
|
|
|
110
|
-
|
|
111
|
-
- `session_id`: Unique session identifier
|
|
112
|
-
- `status`: Session status (`'created'`)
|
|
113
|
-
- `created_at`: ISO 8601 timestamp
|
|
114
|
-
- `expires_at`: ISO 8601 expiry timestamp
|
|
115
|
-
- `upload_url`: URL for uploading audio
|
|
146
|
+
Pass `poll` options to `getSessionStatus` to poll until the session reaches a terminal state:
|
|
116
147
|
|
|
117
|
-
```
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
patient_id: 'pat_123',
|
|
124
|
-
encounter_id: 'enc_456',
|
|
148
|
+
```ts
|
|
149
|
+
const result = await client.getSessionStatus(sessionId, {
|
|
150
|
+
poll: {
|
|
151
|
+
maxAttempts: 60,
|
|
152
|
+
intervalMs: 2000,
|
|
153
|
+
onProgress: (status) => console.log(status.status),
|
|
125
154
|
},
|
|
126
155
|
});
|
|
127
156
|
```
|
|
128
157
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
End the current recording session and trigger processing.
|
|
158
|
+
### Discovery
|
|
132
159
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
160
|
+
| Method | Returns | Description |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `getDiscoveryDocument()` | `DiscoveryDocument \| null` | Raw discovery document. |
|
|
163
|
+
| `getDiscoveryConfig()` | `SDKResult<ResolvedConfig>` | Resolved config from discovery. |
|
|
164
|
+
| `refreshDiscovery()` | `SDKResult<ResolvedConfig>` | Force-refresh discovery. |
|
|
136
165
|
|
|
137
|
-
|
|
166
|
+
### Auth
|
|
138
167
|
|
|
139
|
-
|
|
168
|
+
| Method | Description |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `setAccessToken(token)` | Update Bearer token. Propagates to transport, recorder, and worker. |
|
|
140
171
|
|
|
141
|
-
|
|
142
|
-
const status = await client.getOutputStatus(session.session_id);
|
|
172
|
+
### Callbacks
|
|
143
173
|
|
|
144
|
-
|
|
145
|
-
console.log('Transcript:', status.transcript);
|
|
146
|
-
console.log('Templates:', status.templates);
|
|
147
|
-
}
|
|
148
|
-
```
|
|
174
|
+
Register with `client.registerCallback(name, handler)`, remove with `client.removeCallback(name, handler)`.
|
|
149
175
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
| Callback | Payload | Description |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `onRecordingStateChange` | `RecordingStateChangeEvent` | Recording started, paused, resumed, or ended. |
|
|
179
|
+
| `onAudioEvent` | `AudioEvent` | Speech detection, silence warnings, chunk ready. |
|
|
180
|
+
| `onUploadEvent` | `UploadEvent` | Upload progress and failures. |
|
|
181
|
+
| `onSessionEvent` | `SessionEvent` | Session created, ended, status updates. |
|
|
182
|
+
| `onError` | `ErrorEvent` | VAD, worker, transport, or validation errors. |
|
|
183
|
+
| `onTokenRequired` | `TokenRequiredEvent` | 401 received — call `event.resolve(newToken)` to retry. |
|
|
156
184
|
|
|
157
|
-
|
|
185
|
+
## Error Handling
|
|
158
186
|
|
|
159
|
-
|
|
187
|
+
All public async methods return `SDKResult<T>` — errors are returned, not thrown:
|
|
160
188
|
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
onProgress: (status) => {
|
|
166
|
-
console.log('Current status:', status.status);
|
|
167
|
-
},
|
|
168
|
-
});
|
|
189
|
+
```ts
|
|
190
|
+
type SDKResult<T> =
|
|
191
|
+
| { success: true; data: T }
|
|
192
|
+
| { success: false; error: ScribeError };
|
|
169
193
|
```
|
|
170
194
|
|
|
171
|
-
|
|
195
|
+
```ts
|
|
196
|
+
const result = await client.startRecording({ templates: ['soap'] });
|
|
172
197
|
|
|
173
|
-
|
|
198
|
+
if (!result.success) {
|
|
199
|
+
console.error(result.error.code, result.error.message);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
174
202
|
|
|
175
|
-
|
|
176
|
-
|
|
203
|
+
// result.data is typed as CreateSessionResponse
|
|
204
|
+
console.log(result.data.session_id);
|
|
177
205
|
```
|
|
178
206
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
207
|
+
### Error Classes
|
|
208
|
+
|
|
209
|
+
| Error | HTTP | Description |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| `ScribeError` | — | Base error class |
|
|
212
|
+
| `ValidationError` | 400 | Invalid request or config |
|
|
213
|
+
| `AuthenticationError` | 401 | Auth failed (after token refresh attempt) |
|
|
214
|
+
| `ForbiddenError` | 403 | Access denied |
|
|
215
|
+
| `SessionNotFoundError` | 404 | Session doesn't exist |
|
|
216
|
+
| `SessionExpiredError` | 410 | Session expired |
|
|
217
|
+
| `RateLimitError` | 429 | Rate limit exceeded |
|
|
218
|
+
| `DiscoveryError` | — | Discovery fetch/parse failed |
|
|
219
|
+
| `TransportError` | — | Network / IPC failure |
|
|
220
|
+
| `WorkerError` | — | SharedWorker failure |
|
|
221
|
+
| `UploadError` | — | Audio upload failure |
|
|
222
|
+
|
|
223
|
+
### Auto Token Refresh
|
|
224
|
+
|
|
225
|
+
When a 401 is received, the SDK dispatches `onTokenRequired`. Supply a new token to retry the request:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
client.registerCallback('onTokenRequired', async (event) => {
|
|
229
|
+
const newToken = await refreshMyAuthToken();
|
|
230
|
+
event.resolve(newToken);
|
|
231
|
+
});
|
|
187
232
|
```
|
|
188
233
|
|
|
189
|
-
|
|
234
|
+
Concurrent 401s are deduplicated — only one callback fires regardless of how many requests failed simultaneously.
|
|
190
235
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
client.on('discovery:complete', (event) => {
|
|
195
|
-
console.log('Discovery complete:', event.data);
|
|
196
|
-
});
|
|
236
|
+
## SharedWorker Support
|
|
197
237
|
|
|
198
|
-
|
|
199
|
-
console.log('Session created:', event.data);
|
|
200
|
-
});
|
|
238
|
+
The SDK offloads MP3 compression and upload to a SharedWorker for better main-thread performance. The worker is bundled separately as `dist/worker.bundle.js`.
|
|
201
239
|
|
|
202
|
-
|
|
203
|
-
console.log('Session ended:', event.data);
|
|
204
|
-
});
|
|
240
|
+
### Setup
|
|
205
241
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
});
|
|
242
|
+
```ts
|
|
243
|
+
import { ScribeClient, getWorkerUrl } from 'med-scribe-alliance-ts-sdk';
|
|
209
244
|
|
|
210
|
-
client
|
|
211
|
-
|
|
245
|
+
const client = new ScribeClient({
|
|
246
|
+
baseUrl: 'https://api.example.com',
|
|
247
|
+
workerScriptUrl: getWorkerUrl(), // or a custom path
|
|
212
248
|
});
|
|
213
249
|
```
|
|
214
250
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
The SDK automatically validates all API requests against the OpenAPI schema using AJV. This ensures that:
|
|
218
|
-
|
|
219
|
-
- Request bodies conform to the expected structure
|
|
220
|
-
- Required fields are present
|
|
221
|
-
- Field types match the specification
|
|
222
|
-
- Session IDs follow the correct pattern (`ses_[a-zA-Z0-9]+`)
|
|
223
|
-
- Enum values are valid (e.g., `model`, `upload_type`, `communication_protocol`)
|
|
224
|
-
|
|
225
|
-
Validation happens automatically before any API call:
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// This will throw a ValidationError if the request is invalid
|
|
229
|
-
try {
|
|
230
|
-
const session = await client.startRecording({
|
|
231
|
-
templates: ['soap', 'medications'],
|
|
232
|
-
model: 'invalid-model', // ❌ Will fail validation if not in enum
|
|
233
|
-
uploadType: 'chunked',
|
|
234
|
-
});
|
|
235
|
-
} catch (error) {
|
|
236
|
-
if (error instanceof ValidationError) {
|
|
237
|
-
console.error('Validation failed:', error.message);
|
|
238
|
-
// Example: "Validation failed for CreateSessionRequest:
|
|
239
|
-
// - /model: must be equal to one of the allowed values (allowed values: pro, lite)"
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**Validated Operations:**
|
|
245
|
-
- `createSession()`: Validates request body against `CreateSessionRequest` schema
|
|
246
|
-
- `getSessionStatus()`: Validates session ID format
|
|
247
|
-
- `endSession()`: Validates session ID format
|
|
248
|
-
- `pollSessionStatus()`: Validates session ID format
|
|
251
|
+
### Serving the Worker
|
|
249
252
|
|
|
250
|
-
|
|
253
|
+
The worker file must be served as a static asset:
|
|
251
254
|
|
|
252
|
-
|
|
253
|
-
|
|
255
|
+
**Copy to your public directory:**
|
|
256
|
+
```bash
|
|
257
|
+
cp node_modules/med-scribe-alliance-ts-sdk/dist/worker.bundle.js public/
|
|
258
|
+
```
|
|
254
259
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
} catch (error) {
|
|
259
|
-
console.error('Invalid session ID:', error.message);
|
|
260
|
-
}
|
|
260
|
+
**Or use a CDN blob URL (avoids same-origin restrictions):**
|
|
261
|
+
```ts
|
|
262
|
+
import { createWorkerBlobUrl } from 'med-scribe-alliance-ts-sdk';
|
|
261
263
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
communication_protocol: 'http',
|
|
268
|
-
});
|
|
269
|
-
} catch (error) {
|
|
270
|
-
console.error('Invalid request:', error.message);
|
|
271
|
-
}
|
|
264
|
+
const workerUrl = await createWorkerBlobUrl();
|
|
265
|
+
const client = new ScribeClient({
|
|
266
|
+
baseUrl: '...',
|
|
267
|
+
workerScriptUrl: workerUrl,
|
|
268
|
+
});
|
|
272
269
|
```
|
|
273
270
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
import {
|
|
280
|
-
ScribeError,
|
|
281
|
-
AuthenticationError,
|
|
282
|
-
SessionNotFoundError,
|
|
283
|
-
SessionExpiredError,
|
|
284
|
-
RateLimitError,
|
|
285
|
-
ValidationError,
|
|
286
|
-
} from 'scribe-standard-sdk';
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
await client.startRecording({ templates: ['soap'] });
|
|
290
|
-
} catch (error) {
|
|
291
|
-
if (error instanceof ValidationError) {
|
|
292
|
-
console.error('Validation error:', error.message);
|
|
293
|
-
} else if (error instanceof AuthenticationError) {
|
|
294
|
-
console.error('Authentication failed:', error.message);
|
|
295
|
-
} else if (error instanceof SessionExpiredError) {
|
|
296
|
-
console.error('Session expired:', error.details);
|
|
297
|
-
} else if (error instanceof RateLimitError) {
|
|
298
|
-
console.error('Rate limited, retry after:', error.details?.retry_after_seconds);
|
|
299
|
-
} else if (error instanceof ScribeError) {
|
|
300
|
-
console.error('Scribe error:', error.code, error.message);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
271
|
+
**Or set a global override:**
|
|
272
|
+
```ts
|
|
273
|
+
window.__MEDSCRIBE_WORKER_URL__ = '/assets/worker.bundle.js';
|
|
303
274
|
```
|
|
304
275
|
|
|
305
|
-
|
|
276
|
+
If the SharedWorker fails to initialize, the SDK silently falls back to main-thread compression and upload.
|
|
306
277
|
|
|
307
|
-
|
|
278
|
+
## Electron / IPC Mode
|
|
308
279
|
|
|
309
|
-
|
|
310
|
-
const status = await client.getOutputStatus(sessionId);
|
|
280
|
+
For Electron apps where network requests must go through the main process:
|
|
311
281
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const soap = status.templates['soap'];
|
|
315
|
-
if (soap.status === 'success') {
|
|
316
|
-
console.log('SOAP Note:', soap.data);
|
|
317
|
-
} else {
|
|
318
|
-
console.error('SOAP extraction failed:', soap.error);
|
|
319
|
-
}
|
|
282
|
+
```ts
|
|
283
|
+
import { ScribeClient, TransportMode } from 'med-scribe-alliance-ts-sdk';
|
|
320
284
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
285
|
+
const client = new ScribeClient({
|
|
286
|
+
baseUrl: 'https://api.example.com',
|
|
287
|
+
mode: TransportMode.IPC,
|
|
288
|
+
ipcTransport: {
|
|
289
|
+
send: (request) => ipcRenderer.send('scribe-request', request),
|
|
290
|
+
onResponse: (handler) => ipcRenderer.on('scribe-response', (_, res) => handler(res)),
|
|
291
|
+
},
|
|
292
|
+
});
|
|
327
293
|
```
|
|
328
294
|
|
|
329
|
-
|
|
295
|
+
IPC mode always uses main-thread compression (SharedWorker can't access the IPC bridge).
|
|
330
296
|
|
|
331
|
-
|
|
297
|
+
## Two-Step Flow
|
|
332
298
|
|
|
333
|
-
|
|
334
|
-
const discovery = client.getDiscoveryDocument();
|
|
299
|
+
For apps that need to create the session separately from recording:
|
|
335
300
|
|
|
336
|
-
|
|
337
|
-
|
|
301
|
+
```ts
|
|
302
|
+
// Step 1: Create session
|
|
303
|
+
const session = await client.createSession({
|
|
304
|
+
templates: ['soap'],
|
|
305
|
+
upload_type: 'chunked',
|
|
306
|
+
communication_protocol: 'http',
|
|
307
|
+
});
|
|
338
308
|
|
|
339
|
-
|
|
340
|
-
console.log('Max chunk duration:', discovery.capabilities.max_chunk_duration_seconds);
|
|
309
|
+
if (!session.success) return;
|
|
341
310
|
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
console.log(` Languages: ${model.languages.join(', ')}`);
|
|
346
|
-
console.log(` Max duration: ${model.max_session_duration_seconds}s`);
|
|
347
|
-
console.log(` Features:`, model.features);
|
|
311
|
+
// Step 2: Start recording with the existing session
|
|
312
|
+
await client.startRecordingWithSession(session.data, {
|
|
313
|
+
uploadType: 'chunked',
|
|
348
314
|
});
|
|
349
315
|
```
|
|
350
316
|
|
|
351
|
-
##
|
|
317
|
+
## Building from Source
|
|
352
318
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
import type {
|
|
357
|
-
ScribeSDKConfig,
|
|
358
|
-
RecordingOptions,
|
|
359
|
-
CreateSessionResponse,
|
|
360
|
-
GetSessionStatusResponse,
|
|
361
|
-
TemplatesOutput,
|
|
362
|
-
DiscoveryDocument,
|
|
363
|
-
} from 'scribe-standard-sdk';
|
|
319
|
+
```bash
|
|
320
|
+
npm install
|
|
321
|
+
npm run build
|
|
364
322
|
```
|
|
365
323
|
|
|
366
|
-
|
|
324
|
+
Build output (`dist/`):
|
|
367
325
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
- **Spec 11**: Error Handling - Standard error codes and HTTP status mapping
|
|
326
|
+
| File | Description |
|
|
327
|
+
|---|---|
|
|
328
|
+
| `index.mjs` | Minified ESM bundle |
|
|
329
|
+
| `index.d.ts` | Bundled type declarations |
|
|
330
|
+
| `worker.bundle.js` | Self-contained IIFE SharedWorker |
|
|
374
331
|
|
|
375
332
|
## License
|
|
376
333
|
|
|
377
334
|
MIT
|
|
378
|
-
|
|
379
|
-
## Contributing
|
|
380
|
-
|
|
381
|
-
Contributions are welcome! Please open an issue or submit a pull request.
|
|
382
|
-
|