@ulvio/client 0.1.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.cjs ADDED
@@ -0,0 +1,642 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AiClient: () => AiClient,
24
+ CONNECTOR_UNHEALTHY_CODE: () => CONNECTOR_UNHEALTHY_CODE,
25
+ FilesClient: () => FilesClient,
26
+ HTML_TO_PDF_NOT_CONFIGURED_CODE: () => HTML_TO_PDF_NOT_CONFIGURED_CODE,
27
+ HtmlToPdfClient: () => HtmlToPdfClient,
28
+ MailClient: () => MailClient,
29
+ MailboxClient: () => MailboxClient,
30
+ PLATFORM_NOT_CONFIGURED_CODE: () => PLATFORM_NOT_CONFIGURED_CODE,
31
+ SmsClient: () => SmsClient,
32
+ UTILITIES_NOT_CONFIGURED_CODE: () => UTILITIES_NOT_CONFIGURED_CODE,
33
+ Ulvio: () => Ulvio,
34
+ UlvioError: () => UlvioError,
35
+ UtilitiesClient: () => UtilitiesClient,
36
+ VoiceClient: () => VoiceClient,
37
+ WhatsAppClient: () => WhatsAppClient,
38
+ isConnectorUnhealthyError: () => isConnectorUnhealthyError
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/errors.ts
43
+ var CONNECTOR_UNHEALTHY_CODE = "CONNECTOR_UNHEALTHY";
44
+ var PLATFORM_NOT_CONFIGURED_CODE = "platform_not_configured";
45
+ var HTML_TO_PDF_NOT_CONFIGURED_CODE = "html_to_pdf_not_configured";
46
+ var UTILITIES_NOT_CONFIGURED_CODE = "utilities_not_configured";
47
+ var UlvioError = class extends Error {
48
+ code;
49
+ status;
50
+ response;
51
+ constructor(code, message, options = {}) {
52
+ super(message, { cause: options.cause });
53
+ this.name = "UlvioError";
54
+ this.code = code;
55
+ this.status = options.status;
56
+ this.response = options.response;
57
+ }
58
+ };
59
+ function isConnectorUnhealthyError(err) {
60
+ if (err instanceof UlvioError) return err.code === CONNECTOR_UNHEALTHY_CODE;
61
+ const response = err?.response;
62
+ return response?.error?.code === CONNECTOR_UNHEALTHY_CODE;
63
+ }
64
+
65
+ // src/http.ts
66
+ function trimSlash(url) {
67
+ return url.replace(/\/$/, "");
68
+ }
69
+ async function parseError(res, fallbackCode = "request_failed") {
70
+ let body;
71
+ try {
72
+ body = await res.json();
73
+ } catch {
74
+ body = void 0;
75
+ }
76
+ const code = body?.error?.code ?? fallbackCode;
77
+ const message = body?.error?.message ?? res.statusText;
78
+ return new UlvioError(code, message, { status: res.status, response: body });
79
+ }
80
+ var HttpClient = class {
81
+ constructor(config) {
82
+ this.config = config;
83
+ }
84
+ config;
85
+ requirePlatform(method) {
86
+ const url = this.config.platformApiUrl;
87
+ const key = this.config.platformApiKey;
88
+ if (!url || !key) {
89
+ throw new UlvioError(
90
+ PLATFORM_NOT_CONFIGURED_CODE,
91
+ `${method} requires platformApiUrl and platformApiKey to be set on the Ulvio client`
92
+ );
93
+ }
94
+ return { url: trimSlash(url), key };
95
+ }
96
+ requireHtmlToPdf(method) {
97
+ const url = this.config.htmlToPdfApiUrl;
98
+ if (!url) {
99
+ throw new UlvioError(
100
+ HTML_TO_PDF_NOT_CONFIGURED_CODE,
101
+ `${method} requires htmlToPdfApiUrl to be set on the Ulvio client`
102
+ );
103
+ }
104
+ return trimSlash(url);
105
+ }
106
+ requireUtilities(method) {
107
+ const url = this.config.utilitiesApiUrl;
108
+ if (!url) {
109
+ throw new UlvioError(
110
+ UTILITIES_NOT_CONFIGURED_CODE,
111
+ `${method} requires utilitiesApiUrl to be set on the Ulvio client`
112
+ );
113
+ }
114
+ return trimSlash(url);
115
+ }
116
+ // ── Platform (authenticated) ──────────────────────────────────────────
117
+ async platformRequest(callerName, method, path, options = {}) {
118
+ const { url, key } = this.requirePlatform(callerName);
119
+ const res = await fetch(`${url}${path}`, {
120
+ method,
121
+ headers: {
122
+ "Content-Type": "application/json",
123
+ Authorization: `Bearer ${key}`
124
+ },
125
+ body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
126
+ });
127
+ if (!res.ok) throw await parseError(res);
128
+ return await res.json();
129
+ }
130
+ async platformRequestRaw(callerName, method, path, options = {}) {
131
+ const { url, key } = this.requirePlatform(callerName);
132
+ const headers = { ...options.headers };
133
+ if (options.contentType) headers["Content-Type"] = options.contentType;
134
+ headers["Authorization"] = `Bearer ${key}`;
135
+ const res = await fetch(`${url}${path}`, {
136
+ method,
137
+ headers,
138
+ body: options.body
139
+ });
140
+ if (!res.ok) throw await parseError(res);
141
+ return res;
142
+ }
143
+ // ── HTML-to-PDF (unauthenticated) ─────────────────────────────────────
144
+ async htmlToPdfFetch(callerName, path, init) {
145
+ const url = this.requireHtmlToPdf(callerName);
146
+ return fetch(`${url}${path}`, init);
147
+ }
148
+ // ── Utilities (unauthenticated) ───────────────────────────────────────
149
+ async utilitiesRequest(callerName, method, path, options = {}) {
150
+ const url = this.requireUtilities(callerName);
151
+ const res = await fetch(`${url}${path}`, {
152
+ method,
153
+ headers: { "Content-Type": "application/json" },
154
+ body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
155
+ });
156
+ const data = await res.json().catch(() => void 0);
157
+ if (!res.ok) {
158
+ const errBody = data;
159
+ throw new UlvioError(
160
+ errBody?.error?.code ?? "request_failed",
161
+ errBody?.error?.message ?? res.statusText,
162
+ { status: res.status, response: data }
163
+ );
164
+ }
165
+ return data;
166
+ }
167
+ };
168
+
169
+ // src/clients/mail.ts
170
+ var MailClient = class {
171
+ constructor(http) {
172
+ this.http = http;
173
+ }
174
+ http;
175
+ sendTransactional(params) {
176
+ return this.http.platformRequest(
177
+ "client.mail.sendTransactional()",
178
+ "POST",
179
+ "/v1/transactional-mail/send",
180
+ { body: params }
181
+ );
182
+ }
183
+ };
184
+
185
+ // src/clients/mailbox.ts
186
+ var MailboxClient = class {
187
+ constructor(http) {
188
+ this.http = http;
189
+ }
190
+ http;
191
+ send(params) {
192
+ return this.http.platformRequest(
193
+ "client.mailbox.send()",
194
+ "POST",
195
+ "/v1/mailbox/send",
196
+ { body: params }
197
+ );
198
+ }
199
+ list(limit) {
200
+ const qs = limit !== void 0 ? `?limit=${limit}` : "";
201
+ return this.http.platformRequest(
202
+ "client.mailbox.list()",
203
+ "GET",
204
+ `/v1/mailbox/messages${qs}`
205
+ );
206
+ }
207
+ get(id) {
208
+ return this.http.platformRequest(
209
+ "client.mailbox.get()",
210
+ "GET",
211
+ `/v1/mailbox/messages/${encodeURIComponent(id)}`
212
+ );
213
+ }
214
+ markProcessed(id) {
215
+ return this.http.platformRequest(
216
+ "client.mailbox.markProcessed()",
217
+ "POST",
218
+ `/v1/mailbox/messages/${encodeURIComponent(id)}/mark-processed`
219
+ );
220
+ }
221
+ /**
222
+ * Download a mailbox message attachment as a Buffer.
223
+ */
224
+ async getAttachment(messageId, attachmentId) {
225
+ const res = await this.http.platformRequestRaw(
226
+ "client.mailbox.getAttachment()",
227
+ "GET",
228
+ `/v1/mailbox/messages/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}`
229
+ );
230
+ const arrayBuffer = await res.arrayBuffer();
231
+ return Buffer.from(arrayBuffer);
232
+ }
233
+ getConnectorStatus() {
234
+ return this.http.platformRequest(
235
+ "client.mailbox.getConnectorStatus()",
236
+ "GET",
237
+ "/v1/mailbox/connector-status"
238
+ );
239
+ }
240
+ /**
241
+ * Dev-only: toggle the mock connector's health state.
242
+ */
243
+ setConnectorStatus(healthy) {
244
+ return this.http.platformRequest(
245
+ "client.mailbox.setConnectorStatus()",
246
+ "PATCH",
247
+ "/v1/mailbox/connector-status",
248
+ { body: { healthy } }
249
+ );
250
+ }
251
+ };
252
+
253
+ // src/clients/sms.ts
254
+ var SmsClient = class {
255
+ constructor(http) {
256
+ this.http = http;
257
+ }
258
+ http;
259
+ send(params) {
260
+ return this.http.platformRequest(
261
+ "client.sms.send()",
262
+ "POST",
263
+ "/v1/sms/send",
264
+ { body: params }
265
+ );
266
+ }
267
+ };
268
+
269
+ // src/clients/whatsapp.ts
270
+ var WhatsAppClient = class {
271
+ constructor(http) {
272
+ this.http = http;
273
+ }
274
+ http;
275
+ send(params) {
276
+ return this.http.platformRequest(
277
+ "client.whatsapp.send()",
278
+ "POST",
279
+ "/v1/whatsapp/send",
280
+ { body: params }
281
+ );
282
+ }
283
+ };
284
+
285
+ // src/clients/voice.ts
286
+ var VoiceClient = class {
287
+ constructor(http) {
288
+ this.http = http;
289
+ }
290
+ http;
291
+ transcribe(params) {
292
+ return this.http.platformRequest(
293
+ "client.voice.transcribe()",
294
+ "POST",
295
+ "/v1/voice/send",
296
+ { body: params }
297
+ );
298
+ }
299
+ };
300
+
301
+ // src/clients/files.ts
302
+ var FilesClient = class {
303
+ constructor(http) {
304
+ this.http = http;
305
+ }
306
+ http;
307
+ async upload(key, file, contentType = "application/octet-stream") {
308
+ const res = await this.http.platformRequestRaw(
309
+ "client.files.upload()",
310
+ "PUT",
311
+ `/v1/files/${encodeURI(key)}`,
312
+ { body: file, contentType }
313
+ );
314
+ return await res.json();
315
+ }
316
+ /**
317
+ * Download a file. Returns the raw Response for streaming.
318
+ */
319
+ get(key) {
320
+ return this.http.platformRequestRaw(
321
+ "client.files.get()",
322
+ "GET",
323
+ `/v1/files/${encodeURI(key)}`
324
+ );
325
+ }
326
+ list(prefix, limit, cursor) {
327
+ const params = new URLSearchParams();
328
+ if (prefix) params.set("prefix", prefix);
329
+ if (limit !== void 0) params.set("limit", String(limit));
330
+ if (cursor) params.set("cursor", cursor);
331
+ const qs = params.toString();
332
+ return this.http.platformRequest(
333
+ "client.files.list()",
334
+ "GET",
335
+ `/v1/files${qs ? `?${qs}` : ""}`
336
+ );
337
+ }
338
+ delete(key) {
339
+ return this.http.platformRequest(
340
+ "client.files.delete()",
341
+ "DELETE",
342
+ `/v1/files/${encodeURI(key)}`
343
+ );
344
+ }
345
+ deleteMany(prefix) {
346
+ return this.http.platformRequest(
347
+ "client.files.deleteMany()",
348
+ "DELETE",
349
+ `/v1/files?prefix=${encodeURIComponent(prefix)}`
350
+ );
351
+ }
352
+ /**
353
+ * Generate a time-limited download URL for a file.
354
+ * @param expiresIn TTL in seconds (default 3600, min 60, max 86400)
355
+ */
356
+ presignedDownloadUrl(key, expiresIn) {
357
+ return this.http.platformRequest(
358
+ "client.files.presignedDownloadUrl()",
359
+ "POST",
360
+ "/v1/files/presigned-download",
361
+ { body: { key, expires_in: expiresIn } }
362
+ );
363
+ }
364
+ /**
365
+ * Generate a time-limited upload URL for a file.
366
+ * @param expiresIn TTL in seconds (default 3600, min 60, max 86400)
367
+ * @param maxSize Max file size in bytes (default 50MB)
368
+ */
369
+ presignedUploadUrl(key, contentType, expiresIn, maxSize) {
370
+ return this.http.platformRequest(
371
+ "client.files.presignedUploadUrl()",
372
+ "POST",
373
+ "/v1/files/presigned-upload",
374
+ {
375
+ body: { key, content_type: contentType, expires_in: expiresIn, max_size: maxSize }
376
+ }
377
+ );
378
+ }
379
+ };
380
+
381
+ // src/clients/ai.ts
382
+ var import_zod = require("zod");
383
+ function buildPayload(params) {
384
+ return {
385
+ model: params.model,
386
+ input: params.input,
387
+ schema: import_zod.z.toJSONSchema(params.schema)
388
+ };
389
+ }
390
+ function parseSseEvents(chunk) {
391
+ const events = [];
392
+ let currentEvent = "";
393
+ let currentData = "";
394
+ for (const line of chunk.split("\n")) {
395
+ if (line.startsWith("event: ")) {
396
+ currentEvent = line.slice(7);
397
+ } else if (line.startsWith("data: ")) {
398
+ currentData = line.slice(6);
399
+ } else if (line === "" && currentEvent) {
400
+ events.push({ event: currentEvent, data: currentData });
401
+ currentEvent = "";
402
+ currentData = "";
403
+ }
404
+ }
405
+ return events;
406
+ }
407
+ var AiClient = class {
408
+ constructor(http) {
409
+ this.http = http;
410
+ }
411
+ http;
412
+ async parse(params) {
413
+ const wire = buildPayload(params);
414
+ const response = await this.http.platformRequest(
415
+ "client.ai.parse()",
416
+ "POST",
417
+ "/v1/ai/parse",
418
+ { body: wire }
419
+ );
420
+ return params.schema.parse(response.data);
421
+ }
422
+ async *stream(params) {
423
+ const wire = buildPayload(params);
424
+ const res = await this.http.platformRequestRaw(
425
+ "client.ai.stream()",
426
+ "POST",
427
+ "/v1/ai/stream",
428
+ {
429
+ body: JSON.stringify(wire),
430
+ contentType: "application/json",
431
+ headers: { Accept: "text/event-stream" }
432
+ }
433
+ );
434
+ if (!res.body) {
435
+ throw new UlvioError("stream_no_body", "AI stream returned no response body", {
436
+ status: res.status
437
+ });
438
+ }
439
+ const reader = res.body.getReader();
440
+ const decoder = new TextDecoder();
441
+ let buffer = "";
442
+ for (; ; ) {
443
+ const { done, value } = await reader.read();
444
+ if (done) break;
445
+ buffer += decoder.decode(value, { stream: true });
446
+ const events = parseSseEvents(buffer);
447
+ const lastBoundary = buffer.lastIndexOf("\n\n");
448
+ buffer = lastBoundary >= 0 ? buffer.slice(lastBoundary + 2) : buffer;
449
+ for (const { event, data } of events) {
450
+ switch (event) {
451
+ case "partial":
452
+ yield { type: "partial", data: JSON.parse(data) };
453
+ break;
454
+ case "complete": {
455
+ const parsed = params.schema.parse(JSON.parse(data));
456
+ yield { type: "complete", data: parsed };
457
+ return;
458
+ }
459
+ case "error": {
460
+ const errBody = JSON.parse(data);
461
+ throw new UlvioError(
462
+ "code" in errBody && errBody.code || "ai_stream_error",
463
+ errBody.message
464
+ );
465
+ }
466
+ }
467
+ }
468
+ }
469
+ }
470
+ };
471
+
472
+ // src/clients/html-to-pdf.ts
473
+ function parseSseEvents2(chunk) {
474
+ const events = [];
475
+ let currentEvent = "";
476
+ let currentData = "";
477
+ for (const line of chunk.split("\n")) {
478
+ if (line.startsWith("event: ")) {
479
+ currentEvent = line.slice(7);
480
+ } else if (line.startsWith("data: ")) {
481
+ currentData = line.slice(6);
482
+ } else if (line === "" && currentEvent) {
483
+ events.push({ event: currentEvent, data: currentData });
484
+ currentEvent = "";
485
+ currentData = "";
486
+ }
487
+ }
488
+ return events;
489
+ }
490
+ var HtmlToPdfClient = class {
491
+ constructor(http) {
492
+ this.http = http;
493
+ }
494
+ http;
495
+ async convert(params, callbacks) {
496
+ const res = await this.http.htmlToPdfFetch("client.htmlToPdf.convert()", "/api/html-to-pdf", {
497
+ method: "POST",
498
+ headers: { "Content-Type": "application/json" },
499
+ body: JSON.stringify(params)
500
+ });
501
+ if (!res.ok) {
502
+ const data = await res.json().catch(() => void 0);
503
+ throw new UlvioError(
504
+ data?.error?.code ?? "html_to_pdf_failed",
505
+ data?.error?.message ?? res.statusText,
506
+ { status: res.status, response: data }
507
+ );
508
+ }
509
+ if (!res.body) {
510
+ throw new UlvioError("html_to_pdf_no_body", "html-to-pdf service returned no body", {
511
+ status: res.status
512
+ });
513
+ }
514
+ const reader = res.body.getReader();
515
+ const decoder = new TextDecoder();
516
+ let buffer = "";
517
+ for (; ; ) {
518
+ const { done, value } = await reader.read();
519
+ if (done) break;
520
+ buffer += decoder.decode(value, { stream: true });
521
+ const events = parseSseEvents2(buffer);
522
+ const lastBoundary = buffer.lastIndexOf("\n\n");
523
+ buffer = lastBoundary >= 0 ? buffer.slice(lastBoundary + 2) : buffer;
524
+ for (const { event, data } of events) {
525
+ switch (event) {
526
+ case "queued":
527
+ callbacks?.onQueued?.(JSON.parse(data));
528
+ break;
529
+ case "processing":
530
+ callbacks?.onProcessing?.(JSON.parse(data));
531
+ break;
532
+ case "complete":
533
+ return JSON.parse(data);
534
+ case "error": {
535
+ const errBody = JSON.parse(data);
536
+ throw new UlvioError(
537
+ errBody.code ?? "html_to_pdf_error",
538
+ errBody.message
539
+ );
540
+ }
541
+ }
542
+ }
543
+ }
544
+ throw new UlvioError(
545
+ "html_to_pdf_truncated",
546
+ "SSE stream ended without a complete event"
547
+ );
548
+ }
549
+ };
550
+
551
+ // src/clients/utilities.ts
552
+ var UtilitiesClient = class {
553
+ constructor(http) {
554
+ this.http = http;
555
+ }
556
+ http;
557
+ compileMjml(params) {
558
+ return this.http.utilitiesRequest(
559
+ "client.utilities.compileMjml()",
560
+ "POST",
561
+ "/api/mjml/compile",
562
+ { body: params }
563
+ );
564
+ }
565
+ renderLiquid(params) {
566
+ return this.http.utilitiesRequest(
567
+ "client.utilities.renderLiquid()",
568
+ "POST",
569
+ "/api/liquidjs/render",
570
+ { body: params }
571
+ );
572
+ }
573
+ extractLiquidVariables(params) {
574
+ return this.http.utilitiesRequest(
575
+ "client.utilities.extractLiquidVariables()",
576
+ "POST",
577
+ "/api/liquidjs/variables",
578
+ { body: params }
579
+ );
580
+ }
581
+ renderMarkdown(params) {
582
+ return this.http.utilitiesRequest(
583
+ "client.utilities.renderMarkdown()",
584
+ "POST",
585
+ "/api/markdown/render",
586
+ { body: params }
587
+ );
588
+ }
589
+ renderEmail(params) {
590
+ return this.http.utilitiesRequest(
591
+ "client.utilities.renderEmail()",
592
+ "POST",
593
+ "/api/render-email",
594
+ { body: params }
595
+ );
596
+ }
597
+ };
598
+
599
+ // src/index.ts
600
+ var Ulvio = class {
601
+ mail;
602
+ mailbox;
603
+ sms;
604
+ whatsapp;
605
+ voice;
606
+ files;
607
+ ai;
608
+ htmlToPdf;
609
+ utilities;
610
+ constructor(config = {}) {
611
+ const http = new HttpClient(config);
612
+ this.mail = new MailClient(http);
613
+ this.mailbox = new MailboxClient(http);
614
+ this.sms = new SmsClient(http);
615
+ this.whatsapp = new WhatsAppClient(http);
616
+ this.voice = new VoiceClient(http);
617
+ this.files = new FilesClient(http);
618
+ this.ai = new AiClient(http);
619
+ this.htmlToPdf = new HtmlToPdfClient(http);
620
+ this.utilities = new UtilitiesClient(http);
621
+ }
622
+ };
623
+ // Annotate the CommonJS export names for ESM import in node:
624
+ 0 && (module.exports = {
625
+ AiClient,
626
+ CONNECTOR_UNHEALTHY_CODE,
627
+ FilesClient,
628
+ HTML_TO_PDF_NOT_CONFIGURED_CODE,
629
+ HtmlToPdfClient,
630
+ MailClient,
631
+ MailboxClient,
632
+ PLATFORM_NOT_CONFIGURED_CODE,
633
+ SmsClient,
634
+ UTILITIES_NOT_CONFIGURED_CODE,
635
+ Ulvio,
636
+ UlvioError,
637
+ UtilitiesClient,
638
+ VoiceClient,
639
+ WhatsAppClient,
640
+ isConnectorUnhealthyError
641
+ });
642
+ //# sourceMappingURL=index.cjs.map