npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cloudlayerio/sdk

v0.2.0

Published

Official TypeScript/JavaScript SDK for the CloudLayer.io document generation API

Readme

@cloudlayerio/sdk

Official TypeScript/JavaScript SDK for the CloudLayer.io document generation API.

Convert URLs, HTML, and templates to PDF and images with a simple, type-safe API.

Installation

npm install @cloudlayerio/sdk

Quick Start

import { CloudLayer } from "@cloudlayerio/sdk";

const client = new CloudLayer({
  apiKey: process.env.CLOUDLAYER_API_KEY!,
  apiVersion: "v2",
});

// Generate a PDF from a URL (v2 returns a Job object)
const result = await client.urlToPdf({
  url: "https://example.com",
  format: "a4",
  margin: { top: "20px", bottom: "20px" },
});

// Download the binary PDF from the Job's asset URL
const job = result.data; // Job object
const pdfBuffer = await client.downloadJobResult(job);

Requirements

  • Node.js >= 18.13.0 (for native fetch and FormData support)
  • Or any modern browser

API Version Differences

The SDK requires you to choose an API version:

| Feature | v1 | v2 | |---------|----|----| | Default mode | Synchronous (returns binary) | Asynchronous (returns Job) | | Sync response | Raw binary (PDF/image buffer) | JSON Job object | | Async response | JSON Job object | JSON Job object | | Binary access | Direct from response | Via downloadJobResult() |

// v1: Synchronous, returns binary directly
const v1Client = new CloudLayer({ apiKey: "...", apiVersion: "v1" });
const { data: pdfBuffer } = await v1Client.urlToPdf({ url: "https://example.com" });

// v2: Async by default, returns Job
const v2Client = new CloudLayer({ apiKey: "...", apiVersion: "v2" });
const { data: job } = await v2Client.urlToPdf({ url: "https://example.com" });
const pdfBuffer = await v2Client.downloadJobResult(job);

Configuration

const client = new CloudLayer({
  apiKey: "your-api-key",       // Required
  apiVersion: "v2",             // Required: "v1" or "v2"
  baseUrl: "https://api.cloudlayer.io", // Optional (default)
  timeout: 30000,               // Optional: request timeout in ms (default 30000)
  maxRetries: 2,                // Optional: retries for data endpoints (default 2, max 5)
  headers: {},                  // Optional: additional headers
});

Conversion Methods

URL to PDF/Image

const result = await client.urlToPdf({
  url: "https://example.com",
  format: "letter",
  margin: { top: "1in", bottom: "1in", left: "0.5in", right: "0.5in" },
  printBackground: true,
  viewPort: { width: 1920, height: 1080, deviceScaleFactor: 1, isMobile: false, hasTouch: false, isLandscape: false },
  waitUntil: "networkidle0",
});

const imageResult = await client.urlToImage({
  url: "https://example.com",
  imageType: "png",
  quality: 90,
});

HTML to PDF/Image

HTML content must be Base64-encoded:

const html = btoa("<html><body><h1>Hello World</h1></body></html>");

const result = await client.htmlToPdf({ html });
const imageResult = await client.htmlToImage({ html, imageType: "webp" });

Template Rendering

// By template ID
const result = await client.templateToPdf({
  templateId: "your-template-id",
  data: { name: "John Doe", items: [{ qty: 2, price: 9.99 }] },
});

// By base64-encoded template
const result = await client.templateToImage({
  template: btoa("<html><body>{{name}}</body></html>"),
  data: { name: "Jane" },
  imageType: "png",
});

File Conversion

import { readFileSync } from "node:fs";

// DOCX to PDF
const docxBuffer = readFileSync("document.docx");
const result = await client.docxToPdf({ file: docxBuffer });

// DOCX to HTML
const htmlResult = await client.docxToHtml({ file: docxBuffer });

// PDF to DOCX
const pdfBuffer = readFileSync("document.pdf");
const docxResult = await client.pdfToDocx({ file: pdfBuffer });

PDF Merge

const result = await client.mergePdfs({
  batch: {
    urls: [
      "https://example.com/page1.pdf",
      "https://example.com/page2.pdf",
    ],
  },
});

Batch Processing

Process up to 20 URLs in one request (always async):

const result = await client.urlToPdf({
  batch: { urls: ["https://a.com", "https://b.com", "https://c.com"] },
  storage: true,
});

Async Mode & Job Polling

// v2 is async by default — use waitForJob to poll for completion
const { data: job } = await client.urlToPdf({
  url: "https://example.com",
  storage: true,
});

// Poll until complete (default: 5s interval, 5 min timeout)
const completedJob = await client.waitForJob(job.id, {
  interval: 5000,  // min 2000ms
  maxWait: 300000,
});

// Download the binary result
const pdf = await client.downloadJobResult(completedJob);

Data Management

// List all jobs
const jobs = await client.listJobs();

// Get specific job
const job = await client.getJob("job-id");

// List all assets
const assets = await client.listAssets();

// Get specific asset
const asset = await client.getAsset("asset-id");

// Storage management
const storages = await client.listStorage();
const created = await client.addStorage({
  title: "My S3 Bucket",
  region: "us-east-1",
  accessKeyId: "AKIA...",
  secretAccessKey: "...",
  bucket: "my-bucket",
});
await client.deleteStorage("storage-id");

// Account info
const account = await client.getAccount();
const status = await client.getStatus();

// Public templates (no auth required)
const templates = await client.listTemplates({ type: "invoice" });
const template = await client.getTemplate("template-id");

Error Handling

import {
  CloudLayerApiError,
  CloudLayerAuthError,
  CloudLayerRateLimitError,
  CloudLayerTimeoutError,
  CloudLayerNetworkError,
  CloudLayerValidationError,
} from "@cloudlayerio/sdk";

try {
  await client.urlToPdf({ url: "https://example.com" });
} catch (error) {
  if (error instanceof CloudLayerAuthError) {
    console.error("Invalid API key");
  } else if (error instanceof CloudLayerRateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter}s`);
  } else if (error instanceof CloudLayerTimeoutError) {
    console.error(`Request timed out after ${error.timeout}ms`);
  } else if (error instanceof CloudLayerApiError) {
    console.error(`API error ${error.status}: ${error.message}`);
  } else if (error instanceof CloudLayerValidationError) {
    console.error(`Invalid input: ${error.field} — ${error.message}`);
  } else if (error instanceof CloudLayerNetworkError) {
    console.error("Network error:", error.message);
  }
}

Performance Notes

  • listJobs() and listAssets() return ALL records (no server-side pagination). For accounts with many jobs/assets, these responses can be large.
  • waitForJob() polls every 5 seconds by default. Each poll reads a Firestore document on the server. Minimum interval is 2 seconds.
  • Conversion endpoints are NOT retried automatically — they are expensive operations. Data endpoints (jobs, assets, storage, account) are retried on 429/5xx with exponential backoff.

TypeScript

Full type safety for all request options and responses:

import type {
  UrlToPdfOptions,
  Job,
  AccountInfo,
  CloudLayerResponseHeaders,
} from "@cloudlayerio/sdk";

License

Apache-2.0