@veroai/transcribe 1.0.0

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.mjs ADDED
@@ -0,0 +1,471 @@
1
+ // src/types.ts
2
+ var VeroAPIError = class extends Error {
3
+ constructor(message, code, status) {
4
+ super(message);
5
+ this.name = "VeroAPIError";
6
+ this.code = code;
7
+ this.status = status;
8
+ }
9
+ };
10
+
11
+ // src/client.ts
12
+ var DEFAULT_BASE_URL = "https://api.verotranscribe.com";
13
+ var DEFAULT_TIMEOUT = 3e4;
14
+ var HttpClient = class {
15
+ constructor(config) {
16
+ if (!config.apiKey) {
17
+ throw new Error("API key is required");
18
+ }
19
+ this.apiKey = config.apiKey;
20
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
21
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
22
+ }
23
+ async request(method, path, body) {
24
+ const url = `${this.baseUrl}${path}`;
25
+ const controller = new AbortController();
26
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
27
+ try {
28
+ const response = await fetch(url, {
29
+ method,
30
+ headers: {
31
+ Authorization: `Bearer ${this.apiKey}`,
32
+ "Content-Type": "application/json"
33
+ },
34
+ body: body ? JSON.stringify(body) : void 0,
35
+ signal: controller.signal
36
+ });
37
+ clearTimeout(timeoutId);
38
+ if (!response.ok) {
39
+ let errorData;
40
+ try {
41
+ errorData = await response.json();
42
+ } catch {
43
+ throw new VeroAPIError("An unknown error occurred", "unknown_error", response.status);
44
+ }
45
+ throw new VeroAPIError(errorData.error.message, errorData.error.code, response.status);
46
+ }
47
+ return response.json();
48
+ } catch (error) {
49
+ clearTimeout(timeoutId);
50
+ if (error instanceof VeroAPIError) {
51
+ throw error;
52
+ }
53
+ if (error instanceof Error) {
54
+ if (error.name === "AbortError") {
55
+ throw new VeroAPIError("Request timed out", "timeout", 408);
56
+ }
57
+ throw new VeroAPIError(error.message, "network_error", 0);
58
+ }
59
+ throw new VeroAPIError("An unknown error occurred", "unknown_error", 0);
60
+ }
61
+ }
62
+ async get(path) {
63
+ return this.request("GET", path);
64
+ }
65
+ async post(path, body) {
66
+ return this.request("POST", path, body);
67
+ }
68
+ async patch(path, body) {
69
+ return this.request("PATCH", path, body);
70
+ }
71
+ async delete(path) {
72
+ return this.request("DELETE", path);
73
+ }
74
+ async postFormData(path, formData) {
75
+ const url = `${this.baseUrl}${path}`;
76
+ const controller = new AbortController();
77
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
78
+ try {
79
+ const response = await fetch(url, {
80
+ method: "POST",
81
+ headers: {
82
+ Authorization: `Bearer ${this.apiKey}`
83
+ },
84
+ body: formData,
85
+ signal: controller.signal
86
+ });
87
+ clearTimeout(timeoutId);
88
+ if (!response.ok) {
89
+ let errorData;
90
+ try {
91
+ errorData = await response.json();
92
+ } catch {
93
+ throw new VeroAPIError("An unknown error occurred", "unknown_error", response.status);
94
+ }
95
+ throw new VeroAPIError(errorData.error.message, errorData.error.code, response.status);
96
+ }
97
+ return response.json();
98
+ } catch (error) {
99
+ clearTimeout(timeoutId);
100
+ if (error instanceof VeroAPIError) {
101
+ throw error;
102
+ }
103
+ if (error instanceof Error) {
104
+ if (error.name === "AbortError") {
105
+ throw new VeroAPIError("Request timed out", "timeout", 408);
106
+ }
107
+ throw new VeroAPIError(error.message, "network_error", 0);
108
+ }
109
+ throw new VeroAPIError("An unknown error occurred", "unknown_error", 0);
110
+ }
111
+ }
112
+ };
113
+
114
+ // src/resources/transcriptions.ts
115
+ var DEFAULT_POLL_INTERVAL = 2e3;
116
+ var DEFAULT_MAX_WAIT_TIME = 3e5;
117
+ var TranscriptionsResource = class {
118
+ constructor(client) {
119
+ this.client = client;
120
+ }
121
+ /**
122
+ * Create a new transcription.
123
+ * If audioUrl is provided, transcription starts immediately.
124
+ * Otherwise, use upload() to provide the audio file.
125
+ */
126
+ async create(params) {
127
+ return this.client.post("/v1/transcriptions", params);
128
+ }
129
+ /**
130
+ * Get a transcription by ID
131
+ */
132
+ async get(id) {
133
+ return this.client.get(`/v1/transcriptions/${id}`);
134
+ }
135
+ /**
136
+ * List all transcriptions
137
+ */
138
+ async list(params) {
139
+ const query = new URLSearchParams();
140
+ if (params?.limit) query.set("limit", String(params.limit));
141
+ if (params?.offset) query.set("offset", String(params.offset));
142
+ if (params?.status) query.set("status", params.status);
143
+ if (params?.aiAnalysis) query.set("aiAnalysis", params.aiAnalysis);
144
+ if (params?.from) {
145
+ const fromDate = params.from instanceof Date ? params.from.toISOString() : params.from;
146
+ query.set("from", fromDate);
147
+ }
148
+ if (params?.to) {
149
+ const toDate = params.to instanceof Date ? params.to.toISOString() : params.to;
150
+ query.set("to", toDate);
151
+ }
152
+ const queryString = query.toString();
153
+ const path = queryString ? `/v1/transcriptions?${queryString}` : "/v1/transcriptions";
154
+ return this.client.get(path);
155
+ }
156
+ /**
157
+ * Delete a transcription
158
+ */
159
+ async delete(id) {
160
+ return this.client.delete(`/v1/transcriptions/${id}`);
161
+ }
162
+ /**
163
+ * Upload an audio file for an existing transcription
164
+ * This enqueues the transcription for processing
165
+ */
166
+ async upload(id, audio, params) {
167
+ const formData = new FormData();
168
+ formData.append("audio", audio);
169
+ const query = params?.provider ? `?provider=${params.provider}` : "";
170
+ return this.client.postFormData(
171
+ `/v1/transcriptions/${id}/upload${query}`,
172
+ formData
173
+ );
174
+ }
175
+ /**
176
+ * Create a transcription, upload audio, and wait for completion
177
+ * Convenience method that combines create(), upload(), and waitForCompletion()
178
+ */
179
+ async transcribeFile(audio, params, options) {
180
+ const { provider, ...createParams } = params || {};
181
+ const transcription = await this.create(createParams);
182
+ await this.upload(transcription.id, audio, { provider });
183
+ return this.waitForCompletion(transcription.id, options);
184
+ }
185
+ /**
186
+ * Wait for a transcription to complete
187
+ * Polls the API at regular intervals until the transcription is completed or failed
188
+ */
189
+ async waitForCompletion(id, options) {
190
+ const pollInterval = options?.pollInterval || DEFAULT_POLL_INTERVAL;
191
+ const maxWaitTime = options?.maxWaitTime || DEFAULT_MAX_WAIT_TIME;
192
+ const startTime = Date.now();
193
+ while (true) {
194
+ const transcription = await this.get(id);
195
+ if (options?.onProgress) {
196
+ options.onProgress(transcription);
197
+ }
198
+ if (transcription.status === "completed") {
199
+ return transcription;
200
+ }
201
+ if (transcription.status === "failed") {
202
+ throw new Error(transcription.errorMessage || "Transcription failed");
203
+ }
204
+ if (Date.now() - startTime > maxWaitTime) {
205
+ throw new Error("Timeout waiting for transcription to complete");
206
+ }
207
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
208
+ }
209
+ }
210
+ /**
211
+ * Create a transcription from a URL and wait for it to complete.
212
+ * When audioUrl is provided, transcription starts immediately.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * const result = await vero.transcriptions.transcribe({
217
+ * audioUrl: 'https://example.com/audio.mp3',
218
+ * language: 'en',
219
+ * })
220
+ * ```
221
+ */
222
+ async transcribe(params, options) {
223
+ const transcription = await this.create(params);
224
+ return this.waitForCompletion(transcription.id, options);
225
+ }
226
+ /**
227
+ * Upload multiple audio files for transcription in a single batch.
228
+ * Returns immediately after files are queued for processing.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * const result = await vero.transcriptions.bulkUpload(
233
+ * [file1, file2, file3],
234
+ * { language: 'en', aiAnalysis: 'basic' }
235
+ * )
236
+ * console.log(`Queued ${result.summary.queued} files`)
237
+ * ```
238
+ */
239
+ async bulkUpload(files, params) {
240
+ const formData = new FormData();
241
+ if (params) {
242
+ formData.append("options", JSON.stringify(params));
243
+ }
244
+ for (const item of files) {
245
+ if (item instanceof File || item instanceof Blob) {
246
+ formData.append("audio[]", item);
247
+ } else {
248
+ const blob = item.file;
249
+ const name = item.name || (blob instanceof File ? blob.name : "audio.mp3");
250
+ formData.append("audio[]", blob, name);
251
+ }
252
+ }
253
+ return this.client.postFormData("/v1/transcriptions/bulk", formData);
254
+ }
255
+ /**
256
+ * Upload multiple files and wait for all to complete.
257
+ * Handles partial failures gracefully - returns both completed and failed transcriptions.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * const result = await vero.transcriptions.bulkTranscribe(
262
+ * [file1, file2, file3],
263
+ * { language: 'en' },
264
+ * {
265
+ * onBatchProgress: (completed, total) => {
266
+ * console.log(`Progress: ${completed}/${total}`)
267
+ * }
268
+ * }
269
+ * )
270
+ * console.log(`Completed: ${result.completed.length}, Failed: ${result.failed.length}`)
271
+ * ```
272
+ */
273
+ async bulkTranscribe(files, params, options) {
274
+ const uploadResult = await this.bulkUpload(files, params);
275
+ const queuedItems = uploadResult.results.filter((r) => r.status === "queued" && r.transcriptionId);
276
+ const results = await this.waitForBatch(
277
+ queuedItems.map((item) => ({
278
+ id: item.transcriptionId,
279
+ filename: item.filename
280
+ })),
281
+ options
282
+ );
283
+ const completed = [];
284
+ const failed = [];
285
+ for (const item of uploadResult.results) {
286
+ if (item.status === "failed") {
287
+ failed.push({
288
+ filename: item.filename,
289
+ error: new Error(item.error?.message || "Upload failed")
290
+ });
291
+ }
292
+ }
293
+ for (const [id, result] of results) {
294
+ const item = queuedItems.find((q) => q.transcriptionId === id);
295
+ if (result instanceof Error) {
296
+ failed.push({
297
+ filename: item?.filename || "unknown",
298
+ transcriptionId: id,
299
+ error: result
300
+ });
301
+ } else {
302
+ completed.push(result);
303
+ }
304
+ }
305
+ return {
306
+ batchId: uploadResult.batchId,
307
+ completed,
308
+ failed
309
+ };
310
+ }
311
+ /**
312
+ * Wait for multiple transcriptions to complete.
313
+ * Polls in parallel with configurable concurrency.
314
+ */
315
+ async waitForBatch(items, options) {
316
+ const pollInterval = options?.pollInterval || DEFAULT_POLL_INTERVAL;
317
+ const maxWaitTime = options?.maxWaitTime || DEFAULT_MAX_WAIT_TIME;
318
+ const concurrency = options?.concurrency || 5;
319
+ const results = /* @__PURE__ */ new Map();
320
+ const pending = new Set(items.map((item) => item.id));
321
+ const idToFilename = new Map(items.map((item) => [item.id, item.filename || "unknown"]));
322
+ const startTime = Date.now();
323
+ while (pending.size > 0) {
324
+ if (Date.now() - startTime > maxWaitTime) {
325
+ for (const id of pending) {
326
+ results.set(id, new Error("Timeout waiting for transcription to complete"));
327
+ }
328
+ break;
329
+ }
330
+ const batch = Array.from(pending).slice(0, concurrency);
331
+ const promises = batch.map(async (id) => {
332
+ try {
333
+ const transcription = await this.get(id);
334
+ if (options?.onFileProgress) {
335
+ options.onFileProgress(idToFilename.get(id) || "unknown", transcription);
336
+ }
337
+ if (transcription.status === "completed") {
338
+ results.set(id, transcription);
339
+ pending.delete(id);
340
+ } else if (transcription.status === "failed") {
341
+ results.set(id, new Error(transcription.errorMessage || "Transcription failed"));
342
+ pending.delete(id);
343
+ }
344
+ } catch (error) {
345
+ results.set(id, error instanceof Error ? error : new Error("Unknown error"));
346
+ pending.delete(id);
347
+ }
348
+ });
349
+ await Promise.all(promises);
350
+ if (options?.onBatchProgress) {
351
+ options.onBatchProgress(results.size, items.length, results);
352
+ }
353
+ if (pending.size > 0) {
354
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
355
+ }
356
+ }
357
+ return results;
358
+ }
359
+ };
360
+
361
+ // src/resources/webhooks.ts
362
+ var WebhooksResource = class {
363
+ constructor(client) {
364
+ this.client = client;
365
+ }
366
+ /**
367
+ * Create a new webhook endpoint
368
+ */
369
+ async create(params) {
370
+ return this.client.post("/v1/webhooks", params);
371
+ }
372
+ /**
373
+ * Get a webhook by ID
374
+ */
375
+ async get(id) {
376
+ return this.client.get(`/v1/webhooks/${id}`);
377
+ }
378
+ /**
379
+ * List all webhooks
380
+ */
381
+ async list() {
382
+ return this.client.get("/v1/webhooks");
383
+ }
384
+ /**
385
+ * Update a webhook
386
+ */
387
+ async update(id, params) {
388
+ return this.client.patch(`/v1/webhooks/${id}`, params);
389
+ }
390
+ /**
391
+ * Delete a webhook
392
+ */
393
+ async delete(id) {
394
+ return this.client.delete(`/v1/webhooks/${id}`);
395
+ }
396
+ /**
397
+ * Get delivery history for a webhook (last 10)
398
+ */
399
+ async deliveries(id) {
400
+ return this.client.get(`/v1/webhooks/${id}/deliveries`);
401
+ }
402
+ };
403
+ function createWebhookVerifier(secret) {
404
+ return {
405
+ verify: async (body, signature, timestamp) => {
406
+ const crypto = await import("crypto");
407
+ const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
408
+ return `sha256=${expected}` === signature;
409
+ }
410
+ };
411
+ }
412
+
413
+ // src/resources/usage.ts
414
+ var UsageResource = class {
415
+ constructor(client) {
416
+ this.client = client;
417
+ }
418
+ /**
419
+ * Get usage statistics for the current billing period
420
+ */
421
+ async get() {
422
+ return this.client.get("/v1/usage");
423
+ }
424
+ /**
425
+ * Get usage history (daily breakdown)
426
+ * @param params.days - Number of days to look back (default: 30)
427
+ */
428
+ async history(params) {
429
+ const query = params?.days ? `?days=${params.days}` : "";
430
+ return this.client.get(`/v1/usage/history${query}`);
431
+ }
432
+ };
433
+
434
+ // src/index.ts
435
+ var VeroTranscribe = class {
436
+ /**
437
+ * Create a new VeroTranscribe client
438
+ *
439
+ * @param config - Configuration options
440
+ * @param config.apiKey - Your VeroTranscribe API key (required)
441
+ * @param config.baseUrl - Custom API base URL (optional, defaults to https://verotranscribe-api.siply.workers.dev)
442
+ * @param config.timeout - Request timeout in milliseconds (optional, defaults to 30000)
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * const client = new VeroTranscribe({
447
+ * apiKey: process.env.VERO_API_KEY,
448
+ * });
449
+ *
450
+ * const transcription = await client.transcriptions.create({
451
+ * language: 'en',
452
+ * aiAnalysis: 'basic',
453
+ * });
454
+ *
455
+ * await client.transcriptions.upload(transcription.id, audioFile);
456
+ * ```
457
+ */
458
+ constructor(config) {
459
+ const client = new HttpClient(config);
460
+ this.transcriptions = new TranscriptionsResource(client);
461
+ this.webhooks = new WebhooksResource(client);
462
+ this.usage = new UsageResource(client);
463
+ }
464
+ };
465
+ var index_default = VeroTranscribe;
466
+ export {
467
+ VeroAPIError,
468
+ VeroTranscribe,
469
+ createWebhookVerifier,
470
+ index_default as default
471
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@veroai/transcribe",
3
+ "version": "1.0.0",
4
+ "description": "Official Node.js/TypeScript SDK for VeroTranscribe API",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --format cjs,esm --dts",
20
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
21
+ "typecheck": "tsc --noEmit"
22
+ },
23
+ "keywords": [
24
+ "verotranscribe",
25
+ "transcription",
26
+ "speech-to-text",
27
+ "ai",
28
+ "audio"
29
+ ],
30
+ "author": "VeroAI",
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "tsup": "^8.0.0",
34
+ "typescript": "^5.0.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/veroai/transcribe-sdk.git"
39
+ },
40
+ "dependencies": {
41
+ "@types/node": "^25.0.10"
42
+ }
43
+ }