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