@ulvio/utilities
v0.1.0
Published
Official TypeScript SDK for the Ulvio Utilities service (MJML, LiquidJS, Markdown, image transform).
Readme
@ulvio/utilities
Official TypeScript SDK for the standalone Ulvio Utilities service — MJML compilation, LiquidJS template rendering, Markdown-to-HTML conversion, and image transformation.
This is a thin, direct client for the Utilities HTTP service (the endpoints under /api/*). The service has no authentication, so the SDK only needs a baseUrl. Requests and responses are validated at runtime with zod.
Talking to the Ulvio platform instead of a standalone service? Use
@ulvio/client, whose.utilitiessub-client proxies these same operations through the gateway with an API key.
Install
npm install @ulvio/utilitiesRequires Node.js 24+.
Usage
import { Utilities } from '@ulvio/utilities';
const utilities = new Utilities({ baseUrl: 'http://localhost:3002' });
// MJML → HTML
const { html, errors } = await utilities.compileMjml({
mjml: '<mjml><mj-body><mj-text>Hi</mj-text></mj-body></mjml>',
options: { minify: true },
});
// Liquid render + variable extraction
const rendered = await utilities.renderLiquid({ template: 'Hi {{ name }}', data: { name: 'Ada' } });
const { variables } = await utilities.extractLiquidVariables({ template: 'Hi {{ name }}' });
// Markdown → HTML
const md = await utilities.renderMarkdown({ markdown: '# Title' });
// Combined: Liquid interpolation → MJML compile, in one call
const email = await utilities.renderEmail({
mjml: '<mjml><mj-body><mj-text>Hi {{ name }}</mj-text></mj-body></mjml>',
data: { name: 'Ada' },
});Image transform (streaming)
POST /api/image/transform streams progress over Server-Sent Events. transformImage drives that stream, forwards progress to optional callbacks, and resolves with the final result.
const result = await utilities.transformImage(
{
source: { url: 'https://cdn.example.com/img.jpg' }, // or { base64: '...' }
outputMode: 'base64', // or 'upload'
variants: [
{ name: 'thumbnail', resize: { width: 400 }, format: { type: 'avif', quality: 70 } },
],
},
{
onQueued: ({ position }) => console.log('queued at', position),
onProcessing: ({ step, progress }) => console.log(step, `${progress}%`),
},
);
// base64 mode → each variant has { name, contentType, bytes, width, height, base64 }
// upload mode → each variant has { name, contentType, bytes, width, height, uploaded: true }
// (in 'upload' mode, give every variant a presigned `uploadUrl`)Errors
Every failure is a UtilitiesError with a stable string code, the HTTP status (when the service responded), and the parsed error response:
import { UtilitiesError, NOT_CONFIGURED_CODE } from '@ulvio/utilities';
try {
await utilities.compileMjml({ mjml: '' });
} catch (err) {
if (err instanceof UtilitiesError) {
console.error(err.code, err.status, err.message);
}
}code is not_configured (missing baseUrl), network_error (fetch failed), or a status-derived code such as bad_request / server_error. Image-transform failures surface as image_transform_failed (error event) or truncated (stream ended early).
API
| Method | Endpoint |
| --- | --- |
| health() | GET /api/health |
| compileMjml(req) | POST /api/mjml/compile |
| renderLiquid(req) | POST /api/liquidjs/render |
| extractLiquidVariables(req) | POST /api/liquidjs/variables |
| renderMarkdown(req) | POST /api/markdown/render |
| renderEmail(req) | POST /api/render-email |
| transformImage(req, callbacks?) | POST /api/image/transform (SSE) |
All request/response types are exported (MjmlCompileRequest, ImageTransformResponse, …).
License
UNLICENSED — internal Ulvio package.
