@reqvet-sdk/sdk 2.2.1 → 2.2.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/CHANGELOG.md +8 -0
- package/README.md +24 -5
- package/SDK_REFERENCE.md +109 -43
- package/SECURITY.md +16 -2
- package/package.json +8 -2
- package/src/index.d.ts +19 -1
- package/src/index.js +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.1
|
|
4
|
+
|
|
5
|
+
- **Feat** : ajout de `getSignedUploadUrl(fileName, contentType)` — obtenir une URL presignée Supabase pour uploader l'audio directement, sans passer par `/api/v1/upload` (Vercel Serverless Function, limite ~4.5 MB). Recommandé pour les proxies serveur (Next.js, Express…) gérant des fichiers > 4 MB.
|
|
6
|
+
- **Types** : ajout de `SignedUploadResult` dans `index.d.ts`.
|
|
7
|
+
- **Docs** : `uploadAudio()` annotée avec l'avertissement Vercel dans `index.js`, `index.d.ts`, `SDK_REFERENCE.md` et `README.md`.
|
|
8
|
+
- **Examples** : `nextjs/route-generate.ts` et `nextjs/route-generate.mjs` mis à jour pour utiliser `getSignedUploadUrl()`.
|
|
9
|
+
- **Security** : exemple proxy dans `SECURITY.md` mis à jour.
|
|
10
|
+
|
|
3
11
|
## 2.2.0
|
|
4
12
|
|
|
5
13
|
- **Feat** : ajout de `listJobs(options?)` — liste les jobs avec pagination (`limit`, `offset`) et filtres (`status`, `sort`, `order`). Aligne le SDK sur `GET /api/v1/jobs`.
|
package/README.md
CHANGED
|
@@ -51,6 +51,8 @@ const templateId = system[0].id;
|
|
|
51
51
|
|
|
52
52
|
### Flux webhook (recommandé)
|
|
53
53
|
|
|
54
|
+
Pour les intégrations serveur (Next.js, Express…), utilisez `getSignedUploadUrl()` qui uploade le fichier directement dans Supabase sans passer par une Vercel Serverless Function — **pas de limite de taille**.
|
|
55
|
+
|
|
54
56
|
```ts
|
|
55
57
|
import ReqVet from '@reqvet-sdk/sdk';
|
|
56
58
|
|
|
@@ -58,20 +60,36 @@ const reqvet = new ReqVet(process.env.REQVET_API_KEY!, {
|
|
|
58
60
|
baseUrl: process.env.REQVET_BASE_URL,
|
|
59
61
|
});
|
|
60
62
|
|
|
61
|
-
// 1.
|
|
62
|
-
const { path } = await reqvet.
|
|
63
|
+
// 1. Obtenir une URL signée Supabase (requête JSON légère, pas de fichier)
|
|
64
|
+
const { uploadUrl, path } = await reqvet.getSignedUploadUrl(
|
|
65
|
+
'consultation.webm',
|
|
66
|
+
'audio/webm',
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// 2. Uploader directement vers Supabase (contourne Vercel, pas de limite de taille)
|
|
70
|
+
await fetch(uploadUrl, {
|
|
71
|
+
method: 'PUT',
|
|
72
|
+
headers: { 'Content-Type': 'audio/webm' },
|
|
73
|
+
body: audioBuffer, // Buffer | Blob | File
|
|
74
|
+
});
|
|
63
75
|
|
|
64
|
-
//
|
|
76
|
+
// 3. Créer un job — ReqVet POSTera le résultat sur votre webhook quand il sera prêt
|
|
65
77
|
const job = await reqvet.createJob({
|
|
66
78
|
audioFile: path,
|
|
67
79
|
animalName: 'Rex',
|
|
68
80
|
templateId: 'your-template-uuid',
|
|
69
81
|
callbackUrl: 'https://your-app.com/api/reqvet/webhook',
|
|
70
|
-
metadata: { consultationId: 'abc123' },
|
|
82
|
+
metadata: { consultationId: 'abc123' },
|
|
71
83
|
});
|
|
72
84
|
// { job_id: '...', status: 'pending' }
|
|
73
85
|
```
|
|
74
86
|
|
|
87
|
+
> **`uploadAudio()` vs `getSignedUploadUrl()`**
|
|
88
|
+
>
|
|
89
|
+
> `uploadAudio()` est pratique pour des fichiers légers (< 4 MB) ou des contextes navigateur.
|
|
90
|
+
> Pour les proxies serveur, préférez `getSignedUploadUrl()` : le fichier va directement dans Supabase,
|
|
91
|
+
> sans passer par `/api/v1/upload` (Vercel Serverless Function, limite ~4.5 MB).
|
|
92
|
+
|
|
75
93
|
Votre webhook reçoit un événement `job.completed` :
|
|
76
94
|
|
|
77
95
|
```json
|
|
@@ -125,7 +143,8 @@ export async function POST(req: NextRequest) {
|
|
|
125
143
|
|
|
126
144
|
| Méthode | Description |
|
|
127
145
|
|---------|-------------|
|
|
128
|
-
| `
|
|
146
|
+
| `getSignedUploadUrl(fileName, contentType)` | URL signée Supabase pour upload direct (recommandé serveur) |
|
|
147
|
+
| `uploadAudio(audio, fileName?)` | Uploader un fichier audio via ReqVet (limite Vercel ~4.5 MB) |
|
|
129
148
|
| `generateReport(params)` | Upload + création de job (helper tout-en-un) |
|
|
130
149
|
| `createJob(params)` | Créer un job de génération |
|
|
131
150
|
| `listJobs(options?)` | Lister les jobs avec pagination et filtre par statut |
|
package/SDK_REFERENCE.md
CHANGED
|
@@ -11,8 +11,8 @@ import ReqVet from '@reqvet-sdk/sdk';
|
|
|
11
11
|
|
|
12
12
|
const reqvet = new ReqVet(process.env.REQVET_API_KEY!, {
|
|
13
13
|
baseUrl: process.env.REQVET_BASE_URL ?? 'https://api.reqvet.com',
|
|
14
|
-
pollInterval: 5000,
|
|
15
|
-
timeout: 5 * 60 * 1000,
|
|
14
|
+
pollInterval: 5000, // intervalle de polling en ms (défaut : 5000)
|
|
15
|
+
timeout: 5 * 60 * 1000, // attente maximale en polling en ms (défaut : 300 000 = 5 min)
|
|
16
16
|
});
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -25,6 +25,7 @@ La clé API doit commencer par `rqv_`. Une `Error` est levée immédiatement dan
|
|
|
25
25
|
### Obtenir vos identifiants
|
|
26
26
|
|
|
27
27
|
Votre responsable de compte ReqVet vous fournira :
|
|
28
|
+
|
|
28
29
|
- `REQVET_API_KEY` — votre clé API d'organisation (`rqv_live_...`)
|
|
29
30
|
- `REQVET_BASE_URL` — l'URL de base de l'API
|
|
30
31
|
- `REQVET_WEBHOOK_SECRET` — votre secret de signature webhook (si vous utilisez les webhooks)
|
|
@@ -68,9 +69,53 @@ const report = await reqvet.generateReport({ audio, animalName, templateId, wait
|
|
|
68
69
|
|
|
69
70
|
## 4) Méthodes
|
|
70
71
|
|
|
72
|
+
### `getSignedUploadUrl(fileName, contentType)` ⭐ recommandé serveur
|
|
73
|
+
|
|
74
|
+
Obtenir une URL signée Supabase pour uploader le fichier audio directement, sans passer par une Vercel Serverless Function.
|
|
75
|
+
|
|
76
|
+
**Quand l'utiliser :** intégrations serveur (Next.js proxy, Express, etc.), fichiers > 4 MB.
|
|
77
|
+
|
|
78
|
+
**Flow :**
|
|
79
|
+
```
|
|
80
|
+
getSignedUploadUrl() → PUT uploadUrl (Supabase direct) → createJob({ audioFile: path })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Paramètres :**
|
|
84
|
+
| Nom | Type | Requis | Description |
|
|
85
|
+
|-----|------|--------|-------------|
|
|
86
|
+
| `fileName` | `string` | ✅ | Nom du fichier (ex. `consultation.webm`) |
|
|
87
|
+
| `contentType` | `string` | ✅ | Type MIME (ex. `audio/webm`) |
|
|
88
|
+
|
|
89
|
+
**Réponse :**
|
|
90
|
+
```ts
|
|
91
|
+
{
|
|
92
|
+
uploadUrl: string; // URL presignée Supabase — à utiliser avec PUT
|
|
93
|
+
path: string; // chemin de stockage — à passer à createJob()
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Exemple :**
|
|
98
|
+
```ts
|
|
99
|
+
const { uploadUrl, path } = await reqvet.getSignedUploadUrl('consultation.webm', 'audio/webm');
|
|
100
|
+
|
|
101
|
+
await fetch(uploadUrl, {
|
|
102
|
+
method: 'PUT',
|
|
103
|
+
headers: { 'Content-Type': 'audio/webm' },
|
|
104
|
+
body: audioBuffer,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const job = await reqvet.createJob({ audioFile: path, animalName, templateId });
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
71
112
|
### `uploadAudio(audio, fileName?)`
|
|
72
113
|
|
|
73
|
-
Uploader un fichier audio vers le stockage ReqVet
|
|
114
|
+
Uploader un fichier audio vers le stockage ReqVet via `/api/v1/upload`.
|
|
115
|
+
|
|
116
|
+
> ⚠️ **Limite serveur** : `/api/v1/upload` est une Vercel Serverless Function avec une limite de payload de ~4.5 MB. Pour les proxies serveur gérant des fichiers > 4 MB, utilisez [`getSignedUploadUrl()`](#getsigneduploadurlfilename-contenttype--recommandé-serveur) à la place.
|
|
117
|
+
>
|
|
118
|
+
> `uploadAudio()` reste adapté pour les contextes navigateur (Blob/File natif, pas de limite Vercel) ou les fichiers légers.
|
|
74
119
|
|
|
75
120
|
**Paramètres :**
|
|
76
121
|
| Nom | Type | Requis | Description |
|
|
@@ -79,10 +124,11 @@ Uploader un fichier audio vers le stockage ReqVet.
|
|
|
79
124
|
| `fileName` | `string` | — | Nom du fichier, utilisé pour inférer le type MIME (défaut : `audio.webm`) |
|
|
80
125
|
|
|
81
126
|
**Réponse :**
|
|
127
|
+
|
|
82
128
|
```ts
|
|
83
129
|
{
|
|
84
|
-
audio_file: string;
|
|
85
|
-
path: string;
|
|
130
|
+
audio_file: string; // chemin de stockage canonique — à passer à createJob()
|
|
131
|
+
path: string; // alias de audio_file
|
|
86
132
|
size_bytes: number;
|
|
87
133
|
content_type: string;
|
|
88
134
|
}
|
|
@@ -110,6 +156,7 @@ Wrapper pratique : `uploadAudio → createJob`. Attend optionnellement la fin du
|
|
|
110
156
|
| `onStatus` | `(status: string) => void` | — | Appelé à chaque poll (uniquement si `waitForResult: true`) |
|
|
111
157
|
|
|
112
158
|
**Réponse :**
|
|
159
|
+
|
|
113
160
|
- `waitForResult: false` (défaut) : `{ job_id: string, status: 'pending' }`
|
|
114
161
|
- `waitForResult: true` : `ReqVetReport` (voir `waitForJob`)
|
|
115
162
|
|
|
@@ -130,8 +177,12 @@ Démarrer un pipeline de transcription + génération de compte rendu.
|
|
|
130
177
|
| `extraInstructions` | `string` | — | Instructions de génération supplémentaires (max 5 000 caractères) |
|
|
131
178
|
|
|
132
179
|
**Réponse :**
|
|
180
|
+
|
|
133
181
|
```ts
|
|
134
|
-
{
|
|
182
|
+
{
|
|
183
|
+
job_id: string;
|
|
184
|
+
status: 'pending';
|
|
185
|
+
}
|
|
135
186
|
```
|
|
136
187
|
|
|
137
188
|
> **Limite de débit** : 10 000 requêtes/minute par organisation.
|
|
@@ -152,6 +203,7 @@ Lister les jobs de l'organisation authentifiée, avec pagination et filtrage.
|
|
|
152
203
|
| `order` | `string` | `desc` | Direction : `asc` ou `desc` |
|
|
153
204
|
|
|
154
205
|
**Réponse :**
|
|
206
|
+
|
|
155
207
|
```ts
|
|
156
208
|
{
|
|
157
209
|
jobs: JobSummary[];
|
|
@@ -167,21 +219,22 @@ Obtenir l'état actuel et le résultat d'un job.
|
|
|
167
219
|
|
|
168
220
|
**Champs de réponse par statut :**
|
|
169
221
|
|
|
170
|
-
| Champ
|
|
171
|
-
|
|
172
|
-
| `job_id`
|
|
173
|
-
| `status`
|
|
174
|
-
| `animal_name`
|
|
175
|
-
| `metadata`
|
|
176
|
-
| `transcription` |
|
|
177
|
-
| `result.html`
|
|
178
|
-
| `result.fields` |
|
|
179
|
-
| `cost`
|
|
180
|
-
| `error`
|
|
222
|
+
| Champ | `pending` | `transcribing` | `generating` | `completed` | `failed` |
|
|
223
|
+
| --------------- | :-------: | :------------: | :----------: | :---------: | :------: |
|
|
224
|
+
| `job_id` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
225
|
+
| `status` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
226
|
+
| `animal_name` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
227
|
+
| `metadata` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
228
|
+
| `transcription` | — | — | ✅ | ✅ | — |
|
|
229
|
+
| `result.html` | — | — | — | ✅ | — |
|
|
230
|
+
| `result.fields` | — | — | — | ✅\* | — |
|
|
231
|
+
| `cost` | — | — | — | ✅ | — |
|
|
232
|
+
| `error` | — | — | — | — | ✅ |
|
|
181
233
|
|
|
182
|
-
|
|
234
|
+
\*`result.fields` n'est présent que si votre organisation a un `field_schema` configuré (extraction de données structurées). Vaut `null` sinon. Voir [Schéma de champs](#5-schéma-de-champs) ci-dessous.
|
|
183
235
|
|
|
184
236
|
**Structure du coût (jobs terminés) :**
|
|
237
|
+
|
|
185
238
|
```ts
|
|
186
239
|
cost: {
|
|
187
240
|
transcription_usd: number;
|
|
@@ -199,14 +252,19 @@ cost: {
|
|
|
199
252
|
Poller jusqu'à ce qu'un job atteigne `completed` ou `failed`. Respecte `pollInterval` et `timeout`.
|
|
200
253
|
|
|
201
254
|
**Réponse (`ReqVetReport`) :**
|
|
255
|
+
|
|
202
256
|
```ts
|
|
203
257
|
{
|
|
204
258
|
jobId: string;
|
|
205
|
-
html: string;
|
|
206
|
-
fields: ExtractedFields | null;
|
|
259
|
+
html: string; // HTML du compte rendu généré
|
|
260
|
+
fields: ExtractedFields | null; // null si aucun field_schema configuré
|
|
207
261
|
transcription: string;
|
|
208
262
|
animalName: string;
|
|
209
|
-
cost: {
|
|
263
|
+
cost: {
|
|
264
|
+
transcription_usd: number;
|
|
265
|
+
generation_usd: number;
|
|
266
|
+
total_usd: number;
|
|
267
|
+
}
|
|
210
268
|
metadata: Record<string, unknown>;
|
|
211
269
|
}
|
|
212
270
|
```
|
|
@@ -226,6 +284,7 @@ Régénérer le compte rendu d'un job terminé — par exemple avec des instruct
|
|
|
226
284
|
| `templateId` | `string` | Basculer vers un autre template |
|
|
227
285
|
|
|
228
286
|
**Réponse :**
|
|
287
|
+
|
|
229
288
|
```ts
|
|
230
289
|
{ job_id: string; status: 'completed'; result: { html: string; fields?: ExtractedFields } }
|
|
231
290
|
```
|
|
@@ -247,8 +306,14 @@ Ajouter un audio complémentaire à un job terminé. Le nouvel audio est transcr
|
|
|
247
306
|
| `templateId` | `string` | — | Basculer vers un autre template |
|
|
248
307
|
|
|
249
308
|
**Réponse :**
|
|
309
|
+
|
|
250
310
|
```ts
|
|
251
|
-
{
|
|
311
|
+
{
|
|
312
|
+
job_id: string;
|
|
313
|
+
status: 'amending';
|
|
314
|
+
amendment_number: number;
|
|
315
|
+
message: string;
|
|
316
|
+
}
|
|
252
317
|
```
|
|
253
318
|
|
|
254
319
|
Le job repasse à `completed` quand l'amendement est terminé. Utilisez `waitForJob()` ou écoutez l'événement webhook `job.amended`. Plusieurs amendements sont supportés — chacun est ajouté à la transcription complète.
|
|
@@ -275,6 +340,7 @@ Générer une version alternative d'un compte rendu terminé pour une audience s
|
|
|
275
340
|
| `custom` | Défini par `customInstructions` |
|
|
276
341
|
|
|
277
342
|
**Réponse (`ReqVetReformulation`) :**
|
|
343
|
+
|
|
278
344
|
```ts
|
|
279
345
|
{
|
|
280
346
|
id: string;
|
|
@@ -308,12 +374,12 @@ Générer une version alternative d'un compte rendu terminé pour une audience s
|
|
|
308
374
|
|
|
309
375
|
#### `createTemplate(params)` → `Template`
|
|
310
376
|
|
|
311
|
-
| Nom
|
|
312
|
-
|
|
313
|
-
| `name`
|
|
314
|
-
| `content`
|
|
315
|
-
| `description` | `string`
|
|
316
|
-
| `is_default`
|
|
377
|
+
| Nom | Type | Requis |
|
|
378
|
+
| ------------- | --------- | ------ |
|
|
379
|
+
| `name` | `string` | ✅ |
|
|
380
|
+
| `content` | `string` | ✅ |
|
|
381
|
+
| `description` | `string` | — |
|
|
382
|
+
| `is_default` | `boolean` | — |
|
|
317
383
|
|
|
318
384
|
#### `updateTemplate(templateId, updates)` → `Template`
|
|
319
385
|
|
|
@@ -462,10 +528,10 @@ import { verifyWebhookSignature } from '@reqvet-sdk/sdk/webhooks';
|
|
|
462
528
|
|
|
463
529
|
const { ok, reason } = verifyWebhookSignature({
|
|
464
530
|
secret: process.env.REQVET_WEBHOOK_SECRET!,
|
|
465
|
-
rawBody,
|
|
466
|
-
signature,
|
|
467
|
-
timestamp,
|
|
468
|
-
maxSkewMs: 5 * 60 * 1000,
|
|
531
|
+
rawBody, // corps brut de la requête — à lire AVANT JSON.parse
|
|
532
|
+
signature, // valeur de l'en-tête X-ReqVet-Signature
|
|
533
|
+
timestamp, // valeur de l'en-tête X-ReqVet-Timestamp
|
|
534
|
+
maxSkewMs: 5 * 60 * 1000, // rejeter les événements de plus de 5 min (défaut)
|
|
469
535
|
});
|
|
470
536
|
```
|
|
471
537
|
|
|
@@ -486,21 +552,21 @@ try {
|
|
|
486
552
|
const report = await reqvet.waitForJob(jobId);
|
|
487
553
|
} catch (err) {
|
|
488
554
|
if (err instanceof ReqVetError) {
|
|
489
|
-
console.error(err.message);
|
|
490
|
-
console.error(err.status);
|
|
491
|
-
console.error(err.body);
|
|
555
|
+
console.error(err.message); // message lisible par un humain
|
|
556
|
+
console.error(err.status); // statut HTTP (0 pour les erreurs réseau/timeout)
|
|
557
|
+
console.error(err.body); // corps brut de la réponse
|
|
492
558
|
}
|
|
493
559
|
}
|
|
494
560
|
```
|
|
495
561
|
|
|
496
|
-
| Statut | Signification
|
|
497
|
-
|
|
498
|
-
| `400`
|
|
499
|
-
| `401`
|
|
500
|
-
| `403`
|
|
501
|
-
| `404`
|
|
502
|
-
| `429`
|
|
503
|
-
| `500`
|
|
562
|
+
| Statut | Signification |
|
|
563
|
+
| ------ | ------------------------------------------------- |
|
|
564
|
+
| `400` | Erreur de validation — vérifiez `err.body.issues` |
|
|
565
|
+
| `401` | Clé API invalide ou manquante |
|
|
566
|
+
| `403` | Quota mensuel dépassé |
|
|
567
|
+
| `404` | Job ou template introuvable |
|
|
568
|
+
| `429` | Limite de débit dépassée — attendez et réessayez |
|
|
569
|
+
| `500` | Erreur interne ReqVet |
|
|
504
570
|
|
|
505
571
|
---
|
|
506
572
|
|
package/SECURITY.md
CHANGED
|
@@ -29,7 +29,7 @@ Example proxy route (Next.js App Router):
|
|
|
29
29
|
```ts
|
|
30
30
|
// app/api/reqvet/generate/route.ts
|
|
31
31
|
import { NextRequest, NextResponse } from 'next/server';
|
|
32
|
-
import ReqVet from '@reqvet/sdk';
|
|
32
|
+
import ReqVet from '@reqvet-sdk/sdk';
|
|
33
33
|
|
|
34
34
|
const reqvet = new ReqVet(process.env.REQVET_API_KEY!);
|
|
35
35
|
|
|
@@ -37,7 +37,21 @@ export async function POST(req: NextRequest) {
|
|
|
37
37
|
const form = await req.formData();
|
|
38
38
|
const audio = form.get('audio') as File;
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// Use getSignedUploadUrl() instead of uploadAudio() for server-side proxies.
|
|
41
|
+
// uploadAudio() posts to /api/v1/upload (Vercel Serverless Function, ~4.5 MB limit).
|
|
42
|
+
// getSignedUploadUrl() uploads directly to Supabase — no size limit.
|
|
43
|
+
const { uploadUrl, path } = await reqvet.getSignedUploadUrl(
|
|
44
|
+
audio.name || 'consultation.webm',
|
|
45
|
+
audio.type || 'audio/webm',
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const audioBuffer = Buffer.from(await audio.arrayBuffer());
|
|
49
|
+
await fetch(uploadUrl, {
|
|
50
|
+
method: 'PUT',
|
|
51
|
+
headers: { 'Content-Type': audio.type || 'audio/webm' },
|
|
52
|
+
body: audioBuffer,
|
|
53
|
+
});
|
|
54
|
+
|
|
41
55
|
const job = await reqvet.createJob({
|
|
42
56
|
audioFile: path,
|
|
43
57
|
animalName: form.get('animalName') as string,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reqvet-sdk/sdk",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "Official JavaScript SDK for the ReqVet veterinary report generation API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -18,7 +18,13 @@
|
|
|
18
18
|
},
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
|
-
"files": [
|
|
21
|
+
"files": [
|
|
22
|
+
"src/",
|
|
23
|
+
"README.md",
|
|
24
|
+
"SDK_REFERENCE.md",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"SECURITY.md"
|
|
27
|
+
],
|
|
22
28
|
"keywords": [
|
|
23
29
|
"reqvet",
|
|
24
30
|
"veterinary",
|
package/src/index.d.ts
CHANGED
|
@@ -15,6 +15,13 @@ export interface UploadResult {
|
|
|
15
15
|
content_type: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export interface SignedUploadResult {
|
|
19
|
+
/** Presigned URL for direct PUT upload to Supabase storage */
|
|
20
|
+
uploadUrl: string;
|
|
21
|
+
/** Storage path to pass to createJob({ audioFile: path }) */
|
|
22
|
+
path: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
export interface JobResult {
|
|
19
26
|
job_id: string;
|
|
20
27
|
status: 'pending' | 'transcribing' | 'generating' | 'completed' | 'failed' | 'amending';
|
|
@@ -201,7 +208,18 @@ export declare class ReqVet {
|
|
|
201
208
|
generateReport(params: GenerateReportParams & { waitForResult?: false }): Promise<JobResult>;
|
|
202
209
|
generateReport(params: GenerateReportParams): Promise<JobResult | ReqVetReport>;
|
|
203
210
|
|
|
204
|
-
/**
|
|
211
|
+
/**
|
|
212
|
+
* Get a presigned URL for direct upload to Supabase storage.
|
|
213
|
+
* Recommended for server-side proxies — bypasses Vercel's ~4.5 MB payload limit.
|
|
214
|
+
* PUT the audio buffer to uploadUrl, then pass path to createJob().
|
|
215
|
+
*/
|
|
216
|
+
getSignedUploadUrl(fileName: string, contentType: string): Promise<SignedUploadResult>;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Upload an audio file via ReqVet's /api/v1/upload endpoint.
|
|
220
|
+
* ⚠️ Subject to Vercel Serverless Function payload limit (~4.5 MB).
|
|
221
|
+
* For files > 4 MB in server-side contexts, use getSignedUploadUrl() instead.
|
|
222
|
+
*/
|
|
205
223
|
uploadAudio(audio: Blob | Buffer | File, fileName?: string): Promise<UploadResult>;
|
|
206
224
|
|
|
207
225
|
/** Create a generation job */
|
package/src/index.js
CHANGED
|
@@ -108,9 +108,37 @@ class ReqVet {
|
|
|
108
108
|
|
|
109
109
|
// ─── Upload ────────────────────────────────────────────────
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Get a presigned upload URL for direct upload to ReqVet storage (Supabase).
|
|
113
|
+
*
|
|
114
|
+
* Recommended for server-side integrations (e.g. Next.js proxy routes).
|
|
115
|
+
* The file goes directly to Supabase — it never passes through a Vercel
|
|
116
|
+
* Serverless Function, so there is no ~4.5 MB payload limit.
|
|
117
|
+
*
|
|
118
|
+
* Flow:
|
|
119
|
+
* 1. getSignedUploadUrl(fileName, contentType) — tiny JSON request, no file.
|
|
120
|
+
* 2. PUT the audio buffer to uploadUrl (direct to Supabase).
|
|
121
|
+
* 3. Pass path to createJob({ audioFile: path }).
|
|
122
|
+
*
|
|
123
|
+
* @param {string} fileName - File name (e.g. 'consultation.webm')
|
|
124
|
+
* @param {string} contentType - MIME type (e.g. 'audio/webm')
|
|
125
|
+
* @returns {Promise<{uploadUrl: string, path: string}>}
|
|
126
|
+
*/
|
|
127
|
+
async getSignedUploadUrl(fileName, contentType) {
|
|
128
|
+
return this._fetch('POST', '/api/v1/storage/signed-upload', {
|
|
129
|
+
file_name: fileName,
|
|
130
|
+
content_type: contentType,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
111
134
|
/**
|
|
112
135
|
* Upload an audio file to ReqVet storage.
|
|
113
136
|
*
|
|
137
|
+
* ⚠️ This method POSTs the file to /api/v1/upload, which runs as a
|
|
138
|
+
* Vercel Serverless Function (~4.5 MB request limit). For server-side
|
|
139
|
+
* proxies (Next.js, Express…) handling files > 4 MB, prefer
|
|
140
|
+
* getSignedUploadUrl() + a direct PUT to avoid this limit.
|
|
141
|
+
*
|
|
114
142
|
* @param {Blob|Buffer|File} audio - The audio file
|
|
115
143
|
* @param {string} [fileName] - File name
|
|
116
144
|
* @returns {Promise<{audio_file: string, size_bytes: number}>}
|