@yannelli/nextvisit-sdk 1.0.1
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 +399 -0
- package/dist/index.d.mts +1416 -0
- package/dist/index.d.ts +1416 -0
- package/dist/index.js +953 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +937 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# Nextvisit API JavaScript/TypeScript SDK
|
|
2
|
+
|
|
3
|
+
Official JavaScript/TypeScript client library for the [Nextvisit AI Clinical Documentation API](https://developers.nextvisit.app).
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@yannelli/nextvisit-sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Full TypeScript support with comprehensive type definitions
|
|
11
|
+
- Promise-based async/await API
|
|
12
|
+
- Automatic error handling with typed exceptions
|
|
13
|
+
- Support for both CommonJS and ES Modules
|
|
14
|
+
- Zero runtime dependencies
|
|
15
|
+
- Node.js 18+ support
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @yannelli/nextvisit-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or with yarn:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add @yannelli/nextvisit-sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { NextvisitClient } from "@yannelli/nextvisit-sdk";
|
|
33
|
+
|
|
34
|
+
// Create a client with an existing token
|
|
35
|
+
const client = new NextvisitClient({
|
|
36
|
+
token: "nv-sk-your-token-here"
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Get the current user
|
|
40
|
+
const user = await client.users.getCurrentUser();
|
|
41
|
+
console.log(`Logged in as ${user.name}`);
|
|
42
|
+
|
|
43
|
+
// List patients
|
|
44
|
+
const patients = await client.patients.list();
|
|
45
|
+
console.log(`Found ${patients.meta.total} patients`);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
### Client Options
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface NextvisitClientConfig {
|
|
54
|
+
token?: string; // Authentication token
|
|
55
|
+
baseUrl?: string; // API base URL (default: https://nextvisit.app/api)
|
|
56
|
+
fetch?: typeof fetch; // Custom fetch implementation
|
|
57
|
+
timeout?: number; // Request timeout in ms (default: 30000)
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// Production client
|
|
65
|
+
const client = new NextvisitClient({
|
|
66
|
+
token: "nv-sk-your-token"
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Local development
|
|
70
|
+
const client = new NextvisitClient({
|
|
71
|
+
baseUrl: "http://localhost/api",
|
|
72
|
+
token: "dev-token"
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Custom timeout
|
|
76
|
+
const client = new NextvisitClient({
|
|
77
|
+
token: "nv-sk-your-token",
|
|
78
|
+
timeout: 60000 // 60 seconds
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Token Management
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Set token after initialization
|
|
86
|
+
client.setToken("nv-sk-new-token");
|
|
87
|
+
|
|
88
|
+
// Clear token
|
|
89
|
+
client.clearToken();
|
|
90
|
+
|
|
91
|
+
// Check if authenticated
|
|
92
|
+
if (client.hasToken()) {
|
|
93
|
+
console.log("Client is authenticated");
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API Reference
|
|
98
|
+
|
|
99
|
+
### Authentication
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Login and get a token
|
|
103
|
+
const response = await client.auth.createToken({
|
|
104
|
+
email: "user@example.com",
|
|
105
|
+
password: "password",
|
|
106
|
+
device_name: "My App"
|
|
107
|
+
});
|
|
108
|
+
client.setToken(response.token);
|
|
109
|
+
|
|
110
|
+
// List all tokens
|
|
111
|
+
const { tokens } = await client.auth.listTokens();
|
|
112
|
+
tokens.forEach(t => {
|
|
113
|
+
console.log(`${t.name} - Created: ${t.created_at}`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Logout (revoke current token)
|
|
117
|
+
await client.auth.revokeCurrentToken();
|
|
118
|
+
|
|
119
|
+
// Revoke a specific token
|
|
120
|
+
await client.auth.revokeToken(tokenId);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Users
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// Get current authenticated user
|
|
127
|
+
const user = await client.users.getCurrentUser();
|
|
128
|
+
console.log(user.id, user.name, user.email);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Patients
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// List patients with pagination
|
|
135
|
+
const response = await client.patients.list({
|
|
136
|
+
per_page: 25,
|
|
137
|
+
sort: "-created_at" // Descending by creation date
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Filter patients
|
|
141
|
+
const filtered = await client.patients.list({
|
|
142
|
+
"filter[last_name]": "Smith",
|
|
143
|
+
"filter[trashed]": "with" // Include soft-deleted
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Get a single patient
|
|
147
|
+
const patient = await client.patients.get(patientId);
|
|
148
|
+
|
|
149
|
+
// Create a patient
|
|
150
|
+
const newPatient = await client.patients.create({
|
|
151
|
+
first_name: "John",
|
|
152
|
+
last_name: "Doe",
|
|
153
|
+
date_of_birth: "1990-05-15",
|
|
154
|
+
notes: "Referred for consultation",
|
|
155
|
+
allergies: "Penicillin"
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Update a patient
|
|
159
|
+
const updated = await client.patients.update(patientId, {
|
|
160
|
+
first_name: "Jonathan",
|
|
161
|
+
notes: "Updated contact information"
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Delete a patient (soft delete)
|
|
165
|
+
await client.patients.delete(patientId);
|
|
166
|
+
|
|
167
|
+
// Search patients
|
|
168
|
+
const results = await client.patients.search(userId, {
|
|
169
|
+
query: "John",
|
|
170
|
+
per_page: 10,
|
|
171
|
+
sort_by: "desc"
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Progress Notes
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// List progress notes
|
|
179
|
+
const notes = await client.progressNotes.list({
|
|
180
|
+
patient_id: patientId,
|
|
181
|
+
per_page: 25,
|
|
182
|
+
sort_by: "latest" // or "oldest"
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Get a single progress note
|
|
186
|
+
const note = await client.progressNotes.get(noteId);
|
|
187
|
+
|
|
188
|
+
// Get audio URL for a note
|
|
189
|
+
const { url } = await client.progressNotes.getAudioUrl(noteId);
|
|
190
|
+
|
|
191
|
+
// Create a progress note with audio
|
|
192
|
+
const audioFile = new File([audioBlob], "recording.mp3", {
|
|
193
|
+
type: "audio/mpeg"
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const note = await client.progressNotes.create({
|
|
197
|
+
name: "Follow-up Visit - John Doe",
|
|
198
|
+
patient_id: patientId,
|
|
199
|
+
file: audioFile,
|
|
200
|
+
model_id: 1 // Optional template
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Create a text-only progress note
|
|
204
|
+
const textNote = await client.progressNotes.create({
|
|
205
|
+
name: "Patient Consultation",
|
|
206
|
+
note: "Patient presented with symptoms of...",
|
|
207
|
+
model_id: 1,
|
|
208
|
+
patient_id: patientId
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Update a progress note
|
|
212
|
+
const updated = await client.progressNotes.update(noteId, {
|
|
213
|
+
name: "Updated Visit Name",
|
|
214
|
+
transcription: "Corrected transcription..."
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Sign/approve a progress note
|
|
218
|
+
const signedNote = await client.progressNotes.sign(noteId);
|
|
219
|
+
|
|
220
|
+
// Delete a progress note
|
|
221
|
+
await client.progressNotes.delete(noteId);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Supported audio formats:** mp3, mp4, m4a, wav, ogg, webm, flac, aac, pcm, opus
|
|
225
|
+
**Maximum file size:** 200MB
|
|
226
|
+
|
|
227
|
+
### Billing
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// List available plans
|
|
231
|
+
const { plans } = await client.billing.listPlans();
|
|
232
|
+
plans.forEach(plan => {
|
|
233
|
+
console.log(`${plan.name}: ${plan.price_prefix}${plan.price}${plan.price_suffix}`);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Get billing estimate
|
|
237
|
+
const estimate = await client.billing.getEstimate();
|
|
238
|
+
console.log(`Users: ${estimate.users}`);
|
|
239
|
+
console.log(`Total: ${estimate.total_formatted}`);
|
|
240
|
+
console.log(`Renewal: ${estimate.renewal_date}`);
|
|
241
|
+
|
|
242
|
+
// Get usage statistics
|
|
243
|
+
const usage = await client.billing.getUsage("notes");
|
|
244
|
+
console.log(`Plan: ${usage.plan.name} (${usage.plan.status})`);
|
|
245
|
+
console.log(`Usage: ${usage.usage.used} / ${usage.usage.limit}`);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Transcription
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// Transcribe an audio file
|
|
252
|
+
const response = await client.transcription.transcribeFile({
|
|
253
|
+
file: audioFile // File or Blob
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
if (response.success) {
|
|
257
|
+
console.log(response.text);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Get temporary token for client-side transcription
|
|
261
|
+
const tokenResponse = await client.transcription.getToken();
|
|
262
|
+
if (tokenResponse.success && tokenResponse.token) {
|
|
263
|
+
// Use with Deepgram SDK for real-time transcription
|
|
264
|
+
const deepgram = createClient(tokenResponse.token);
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Error Handling
|
|
269
|
+
|
|
270
|
+
The SDK provides typed error classes for different failure scenarios:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import {
|
|
274
|
+
NextvisitError,
|
|
275
|
+
NextvisitApiError,
|
|
276
|
+
UnauthorizedError,
|
|
277
|
+
ForbiddenError,
|
|
278
|
+
NotFoundError,
|
|
279
|
+
ValidationError,
|
|
280
|
+
NetworkError
|
|
281
|
+
} from "@yannelli/nextvisit-sdk";
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const user = await client.users.getCurrentUser();
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (error instanceof UnauthorizedError) {
|
|
287
|
+
// 401 - Authentication failed
|
|
288
|
+
console.error("Please log in again");
|
|
289
|
+
} else if (error instanceof ForbiddenError) {
|
|
290
|
+
// 403 - Access denied
|
|
291
|
+
console.error("You don't have permission");
|
|
292
|
+
} else if (error instanceof NotFoundError) {
|
|
293
|
+
// 404 - Resource not found
|
|
294
|
+
console.error("Resource not found");
|
|
295
|
+
} else if (error instanceof ValidationError) {
|
|
296
|
+
// 422 - Validation failed
|
|
297
|
+
console.error("Validation errors:", error.errors);
|
|
298
|
+
} else if (error instanceof NetworkError) {
|
|
299
|
+
// Network request failed
|
|
300
|
+
console.error("Network error:", error.message);
|
|
301
|
+
} else if (error instanceof NextvisitApiError) {
|
|
302
|
+
// Other API error
|
|
303
|
+
console.error(`API error (${error.status}): ${error.message}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Error Classes
|
|
309
|
+
|
|
310
|
+
| Class | Status Code | Description |
|
|
311
|
+
|-------|-------------|-------------|
|
|
312
|
+
| `NextvisitError` | - | Base error class |
|
|
313
|
+
| `NextvisitApiError` | Any | API error with status code |
|
|
314
|
+
| `UnauthorizedError` | 401 | Authentication required |
|
|
315
|
+
| `ForbiddenError` | 403 | Access denied |
|
|
316
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
317
|
+
| `ValidationError` | 422 | Validation failed (includes `errors` field) |
|
|
318
|
+
| `NetworkError` | - | Network request failure |
|
|
319
|
+
|
|
320
|
+
## TypeScript Support
|
|
321
|
+
|
|
322
|
+
All API responses are fully typed. Import types directly from the package:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import type {
|
|
326
|
+
User,
|
|
327
|
+
PatientFull,
|
|
328
|
+
ProgressNote,
|
|
329
|
+
PaginatedResponse,
|
|
330
|
+
TokenResponse,
|
|
331
|
+
BillingPlan,
|
|
332
|
+
BillingUsage
|
|
333
|
+
} from "@yannelli/nextvisit-sdk";
|
|
334
|
+
|
|
335
|
+
// Types are inferred from API calls
|
|
336
|
+
const patient = await client.patients.get(123);
|
|
337
|
+
// patient is typed as PatientFull
|
|
338
|
+
|
|
339
|
+
const notes = await client.progressNotes.list();
|
|
340
|
+
// notes is typed as PaginatedResponse<ProgressNote>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Development
|
|
344
|
+
|
|
345
|
+
### Prerequisites
|
|
346
|
+
|
|
347
|
+
- Node.js 18.0.0 or higher
|
|
348
|
+
|
|
349
|
+
### Setup
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Clone the repository
|
|
353
|
+
git clone https://github.com/yannelli/nextvisit-sdk.git
|
|
354
|
+
cd nextvisit-sdk
|
|
355
|
+
|
|
356
|
+
# Install dependencies
|
|
357
|
+
npm install
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Scripts
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
# Build the package
|
|
364
|
+
npm run build
|
|
365
|
+
|
|
366
|
+
# Run tests
|
|
367
|
+
npm test
|
|
368
|
+
|
|
369
|
+
# Run tests in watch mode
|
|
370
|
+
npm run test:watch
|
|
371
|
+
|
|
372
|
+
# Run tests with coverage
|
|
373
|
+
npm run test:coverage
|
|
374
|
+
|
|
375
|
+
# Type checking
|
|
376
|
+
npm run typecheck
|
|
377
|
+
|
|
378
|
+
# Lint code
|
|
379
|
+
npm run lint
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Building
|
|
383
|
+
|
|
384
|
+
The package is built using [tsup](https://tsup.egoist.dev/) and outputs:
|
|
385
|
+
|
|
386
|
+
- `dist/index.js` - CommonJS bundle
|
|
387
|
+
- `dist/index.mjs` - ES Module bundle
|
|
388
|
+
- `dist/index.d.ts` - TypeScript declarations
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
393
|
+
|
|
394
|
+
## Links
|
|
395
|
+
|
|
396
|
+
- [Documentation](https://docs.nextvisitai.com)
|
|
397
|
+
- [Nextvisit AI](https://nextvisitai.com)
|
|
398
|
+
- [npm Package](https://www.npmjs.com/package/@yannelli/nextvisit-sdk)
|
|
399
|
+
- [GitHub Repository](https://github.com/yannelli/nextvisit-sdk)
|