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

@meistrari/document-sdk

v1.18.0

Published

SDK para a API de Processamento de Documentos, com suporte a extração de PDF, templates, conversão para imagem e mais.

Readme

Document Processing SDK

A TypeScript SDK for the Document Processing API that provides methods for PDF extraction, splitting, template generation, PDF-to-image conversion, merging, cropping, document parsing, HTML/Markdown/JSON-to-PDF, and Office document conversion operations.

Installation

npm install @meistrari/document-sdk
# or
pnpm add @meistrari/document-sdk
# or
yarn add @meistrari/document-sdk

Quick Start

Using DataToken Authentication

import { docClient } from '@meistrari/document-sdk'

const client = docClient({
  apiUrl: 'https://your-api-url.com',
  dataToken: 'your-data-token'
})

Using API Key Authentication

import { docClient } from '@meistrari/document-sdk'

const client = docClient({
  apiUrl: 'https://your-api-url.com',
  apiKey: 'Bearer your-api-key'
})

Using DocClient Class Directly

import { DocClient } from '@meistrari/document-sdk'

const client = new DocClient({
  apiUrl: 'https://your-api-url.com',
  dataToken: 'your-data-token'
})

Base URL: the API serves every operation under /v1. You can pass either the bare host (https://your-api-url.com) or an URL that already includes the prefix (https://your-api-url.com/v1) — the client normalizes it so /v1 is never missing or duplicated.

Runtime support

One build, no runtime-specific entry points. The client uses ofetch on top of the runtime's own fetch, so it runs on Node (18+), Bun, Deno, edge runtimes and in the browser — including inside a client bundle, where it must not reference anything Node-only.

Two consequences worth knowing:

  • No custom HTTP agent, and therefore the runtime's default timeouts apply (300s per request on Node). This is safe because no request is long-lived: every operation submits asynchronously and the result arrives through short GET /v1/jobs/:id polls, so the 15-minute ceiling belongs to the polling loop (maxWaitMs), not to one held connection. See Async Processing & Job Status.
  • In a browser, the API must send CORS headers for your origin, and a cross-origin response without them surfaces as NetworkError rather than the typed error, because the browser rejects it before the status is visible.

Operations

Extract Pages from PDF

Extract specific pages from PDF documents. Supports single output or multiple outputs. Ranges that overlap a document are clipped to available page boundaries.

// Single output (all pages merged into one PDF)
const result = await client.extract({
  type: 'page',
  indexes: [1, 3, -1, '2-4'], // Support for ranges and negative indexing
  files: [
    {
      file_url: 'vault://document-123',
      filename: 'document.pdf'
    }
  ]
})

// Multiple outputs (each group creates a separate PDF)
const result = await client.extract({
  type: 'page',
  indexes: [[1, 2], [3, 4], [5, '6-10']], // Each array creates a separate output file
  files: [
    {
      file_url: 'vault://document-123',
      filename: 'document.pdf'
    }
  ]
})

Index formats supported:

  • Positive integers: 1, 2, 3 (1-based indexing)
  • Negative integers: -1, -2 (last page, second to last, etc.)
  • Ranges as strings: '2-4', '1-3'

Split PDF

Split PDF documents into smaller chunks by page count.

const result = await client.split({
  files: [
    {
      file_url: 'vault://document-123',
      filename: 'large-document.pdf'
    }
  ],
  chunk_size: 5, // Pages per chunk (default: 1)
  max_chunk_bytes: 47185920, // Optional byte ceiling per chunk (45 MB here)
  type: 'page'
})

max_chunk_bytes applies on top of chunk_size — a chunk closes on whichever limit it reaches first. Reach for it when the consumer has a hard size limit (an LLM upload cap, say): page count alone cannot bound the output, because a few scanned pages can outweigh hundreds of text ones. Sending it without chunk_size drops the page ceiling entirely. A single page heavier than the ceiling still ships as its own chunk — splitting cannot shrink it.

Generate Document from Template

Generate documents from .docx templates with dynamic data substitution. Supports Mustache templating with loops and conditionals.

const result = await client.template({
  template: {
    // Provide either inline `content` (base64 / data: URI) or a `file_url`.
    content: 'base64-encoded-docx-content',
    filename: 'template.docx'
  },
  data: {
    name: 'John Doe',
    company: 'Acme Corp',
    user: {
      email: '[email protected]'
    },
    // Arrays for loops
    items: [
      { name: 'Item 1', price: 100 },
      { name: 'Item 2', price: 200 }
    ]
  },
  options: {
    outputFormat: 'permalink', // 'base64' | 'vault_url' | 'permalink'
    outputFilename: 'contract.docx', // Custom output filename
    permalink: {
      enabled: true, // Create a public download URL
      expiresIn: 3600 // Optional lifetime, in seconds
    }
  }
})

// Response: { file_url?, base64?, permalink? }

Extract Template Placeholders

List the placeholder variables a template references, without rendering it.

const result = await client.templatePlaceholders({
  template: {
    content: 'base64-encoded-docx-content',
    filename: 'template.docx'
  }
})

// result.placeholders = ['name', 'company', ...]

Convert PDF to Images

Convert specific PDF pages to high-quality JPEG images.

const result = await client.pdfToImage({
  file_url: 'vault://document-123',
  filename: 'document.pdf',
  pages: [1, 2, 5], // Pages to convert (1-indexed)
  quality: 'auto' // optional: 'auto' (default) | 'standard' | 'high'
})

// Response: result.files = [{ file_url, page, width, height }]
// Output dimensions vary per page — always read width/height for downstream cropping.
//
// quality:
//   'auto'     — hybrid: extracts the original embedded image when a page is
//                dominated by one, otherwise rasterizes at an adaptive resolution
//                that preserves the native pixels of any downscaled XObjects
//                (capped at 6× / 4000px). Output dimensions vary per page —
//                always read metadata.width/height for downstream cropping.
//   'standard' — legacy fixed scale (≈192 DPI). Output dimensions match the
//                pre-quality-flag behavior.
//   'high'     — forces adaptive rasterization up to the cap, skipping the
//                direct-extraction gate.

Merge Files

Merge multiple PDFs or audio files into a single file.

// Merge PDFs
const result = await client.merge({
  files: [
    { file_url: 'vault://doc1', filename: 'chapter1.pdf' },
    { file_url: 'vault://doc2', filename: 'chapter2.pdf' }
  ],
  output_filename: 'complete-book.pdf'
})

// Merge audio files (mp3, wav, m4a, aac, ogg, flac, wma, opus, webm)
const result = await client.merge({
  files: [
    { file_url: 'vault://audio1', filename: 'intro.mp3' },
    { file_url: 'vault://audio2', filename: 'main.mp3' }
  ],
  output_filename: 'complete-audio.mp3'
})

Crop Image

Crop images using coordinates.

const result = await client.crop({
  file_url: 'vault://image-123',
  filename: 'image.jpg',
  coordinates: {
    x1: 10, // Left
    y1: 10, // Top
    x2: 200, // Right
    y2: 200 // Bottom
  }
})

// Response includes dimensions
// result.dimensions = { width, height }

Convert HTML to PDF

Render HTML to PDF with headless Chromium. Accepts a whole document or a bare body fragment.

const result = await client.htmlToPdf({
  htmlContent: '<!DOCTYPE html><html><body><h1>Invoice</h1></body></html>',
  filename: 'invoice.pdf',
  options: {
    customCss: 'h1 { color: #003366; }', // appended last, so it overrides the document
    content: {
      logo: 'data:image/png;base64,...', // Optional logo
      title: 'Report Title',
      footerText: 'Confidential'
    },
    align: {
      headerLogo: 'right', // 'left', 'center', 'right', 'none'
      headerTitle: 'center',
      pageNumber: 'center',
      footerText: 'left'
    },
    pdfOptions: {
      format: 'A4',
      orientation: 'portrait',
      preferCSSPageSize: false, // set true to honour the document's own @page rules
      margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' }
    }
  }
})

The rendered page has no network access by default. Remote images, stylesheets and web fonts are refused, so a document must be self-contained (inline <style>, data: URIs) to render as authored. Opt in with allowRemoteContent: true:

const result = await client.htmlToPdf({
  htmlContent: '<img src="https://cdn.example.com/logo.png">',
  options: { allowRemoteContent: true }
})

Even then, requests to loopback, private, carrier-grade-NAT and link-local addresses (including cloud metadata endpoints) stay blocked, as does every non-http(s) scheme — file: above all, and ws:/wss: under both policies.

options.content.logo follows the same policy: it is fetched server-side to be inlined into the PDF header, so with the default it must be a data: URI.

Scripts in the document do NOT run by default; pass javascriptEnabled: true to enable them. The script is yours, but it executes in our Chromium, so it is opt-in — a static document renders identically either way.

Rendering a file instead of inline content

file_url is the other source, and the better one whenever the document is already a file:

const result = await client.htmlToPdf({
  file_url: 'vault://doc-123',
  filename: 'certidao.pdf',
})

It is not bounded by what fits in a JSON body, it never lands in the job's stored input, and the worker detects the encoding from the bytes (BOM, then <meta charset>, then UTF-8) instead of assuming what you decoded it as. That last part matters for any source still emitting windows-1252: reading those bytes as UTF-8 does not fail — it renders where the accents were.

Exactly one of htmlContent and file_url per request.

Page defaults

Unlike markdownToPdf, this operation adds no page setup you did not ask for: margins default to 0 (your own CSS, @page included, is what governs) and the header/footer band appears only once content or align asks for it. printBackground is on and the format is A4, as everywhere else. A margin may be a CSS length or a number read as px — and 0 means zero.

Convert Markdown to PDF

Convert Markdown content to PDF with customizable styling.

const result = await client.markdownToPdf({
  markdownContent: '# My Document\n\nThis is **bold** text.',
  options: {
    theme: 'tela-default', // 'tela-default', 'legal-document', 'invoice'
    content: {
      logo: 'data:image/png;base64,...', // Optional logo
      title: 'Report Title',
      footerText: 'Confidential'
    },
    align: {
      headerLogo: 'right', // 'left', 'center', 'right', 'none'
      headerTitle: 'center',
      pageNumber: 'center',
      footerText: 'left'
    },
    customCss: 'h1 { color: #003366; }',
    pdfOptions: {
      format: 'A4',
      margin: {
        top: '20mm',
        right: '15mm',
        bottom: '20mm',
        left: '15mm'
      }
    }
  }
})

Convert JSON to PDF

Convert JSON data to a formatted PDF document with syntax highlighting.

const result = await client.jsonToPdf({
  jsonData: {
    request: {
      method: 'POST',
      url: '/api/users'
    },
    response: {
      status: 200,
      data: { id: 123 }
    }
  },
  options: {
    content: {
      title: 'API Request Log',
      footerText: 'Generated in DEV'
    },
    align: {
      headerTitle: 'center',
      pageNumber: 'center'
    },
    jsonFormatting: {
      spacing: 2,
      highlightTheme: 'github', // 'github', 'stackoverflow-light', 'atom-one-light', 'googlecode'
      fontFamily: 'Fira Code, monospace',
      fontSize: '12px',
      lineHeight: '1.4'
    },
    pdfOptions: {
      format: 'A4',
      printBackground: true,
      displayHeaderFooter: true
    }
  }
})

Parse Documents

Universal parser that extracts Markdown text from PDFs, images, audio, Office files, emails, CSV and raw text. Omit parser_type to auto-detect from the file's magic bytes.

const result = await client.parse({
  file_url: 'vault://document-123',
  filename: 'document.pdf',
  parser_type: 'pdf', // optional: 'pdf' | 'image' | 'audio' | 'office' | 'email' | 'csv' | 'raw-text'
  // password, language, mime_type and polling_interval_ms are optional hints
})

// result.text        — Markdown content (always present on success)
// result.parser_type — resolved parser
// result.mime_type   — MIME resolved from magic bytes; also persisted on the job,
//                      so webhook `data.mimeType` carries it even when the request
//                      declared none
// result.cost        — provider cost in USD (0 for inline parsers)
// result.pages       — page count (PDF/multi-page)
// result.attachments — extracted files (email only)
// result.assets      — embedded Office images uploaded to Vault and linked in Markdown;
//                      best-effort Vision content is appended to result.text

Positional source layout (PDF)

Markdown flattens tables: a demonstrativo's competência, parcela and valor can end up as three separate runs of text, and nothing in the sequence proves which value belongs to which row. include_source_layout keeps the positional evidence the OCR step already produced.

const result = await client.parse({
  file_url: 'vault://document-id',
  filename: 'demonstrativo.pdf',
  parser_type: 'pdf',
  include_source_layout: true,
})

// result.source_layout — reference to the artifact, never the artifact itself:
// {
//   version: 'v1',
//   coordinate_space: 'normalized',
//   file_url: 'vault://source-layout-id',
//   page_count: 10,
//   byte_size: 284120,
//   sha256: 'e110c8bde…',
// }

Download source_layout.file_url from Vault to get a SourceLayoutDocument: one entry per page, each with its lines and tokens carrying text, confidence, a polygon and (for tokens) the detected break_type. Verify the bytes against sha256 and byte_size before trusting them.

For the demonstrativo table above, the artifact page looks like this (abridged — polygons inlined for readability):

{
  "version": "v1",
  "coordinate_space": "normalized",
  "pages": [
    {
      "page_number": 8,
      "width": 1653,
      "height": 2339,
      "unit": "pixels",
      "lines": [
        {
          "text": "Competência Parcela Valor",
          "confidence": 0.9948,
          "polygon": [{ "x": 0.08, "y": 0.248 }, { "x": 0.64, "y": 0.248 }, { "x": 0.64, "y": 0.2645 }, { "x": 0.08, "y": 0.2645 }]
        },
        {
          "text": "01/2024 12 R$ 132,40",
          "confidence": 0.9887,
          "polygon": [{ "x": 0.08, "y": 0.3105 }, { "x": 0.64, "y": 0.3105 }, { "x": 0.64, "y": 0.327 }, { "x": 0.08, "y": 0.327 }]
        }
      ],
      "tokens": [
        {
          "text": "01/2024",
          "confidence": 0.9934,
          "break_type": "SPACE",
          "polygon": [{ "x": 0.08, "y": 0.3105 }, { "x": 0.255, "y": 0.3105 }, { "x": 0.255, "y": 0.327 }, { "x": 0.08, "y": 0.327 }]
        },
        {
          "text": "12",
          "confidence": 0.9887,
          "break_type": "SPACE",
          "polygon": [{ "x": 0.3, "y": 0.3105 }, { "x": 0.395, "y": 0.3105 }, { "x": 0.395, "y": 0.327 }, { "x": 0.3, "y": 0.327 }]
        },
        {
          "text": "R$ 132,40",
          "confidence": 0.9902,
          "break_type": "HYPHEN",
          "polygon": [{ "x": 0.47, "y": 0.3105 }, { "x": 0.64, "y": 0.3105 }, { "x": 0.64, "y": 0.327 }, { "x": 0.47, "y": 0.327 }]
        }
      ]
    }
  ]
}

That is the whole point of the artifact, and it is worth reading the numbers: the three tokens 01/2024, 12 and R$ 132,40 share the same Y band (0.3105 … 0.327) — they are one row — while sitting in three disjoint X bands (0.08…0.255, 0.30…0.395, 0.47…0.64) — they are three columns. Group tokens by Y to recover rows, cluster their X ranges to recover columns, and R$ 132,40 is provably the Valor of competência 01/2024, not a number that merely appeared nearby in the text. The page_number plus the polygon are also a citation: they point back at the exact region of the source PDF.

Reading it, in short:

  • page_number is global to the document — page 8 is page 8 of the PDF.
  • Every coordinate is normalized to [0, 1] with the origin top-left, so nothing needs width/height to be interpreted; they are informational.
  • lines are the provider's own grouping (useful as a sanity check); tokens are what you want for column reconstruction.
  • break_type is what followed the token (SPACE, WIDE_SPACE, HYPHEN); absent when the provider reported nothing.
  • Vertices are in provider order (top-left, top-right, bottom-right, bottom-left) and are not guaranteed to be axis-aligned on skewed scans — use the min/max of the polygon rather than assuming [0] and [2] are the corners you want.

Notes:

  • Coordinates are always normalized — origin top-left, both axes in [0, 1] — so a consumer never has to branch on page dimensions.
  • Page numbers are global to the document, not per internal chunk.
  • A blank page appears with lines: [] and tokens: []. A page the provider never returned fails the parse instead of being silently omitted.
  • No extra provider call and no per-coordinate price: the layout rides on the same OCR response the Markdown is built from.
  • The flag is part of the cache key. A document parsed earlier without layout is re-parsed the first time it is requested with layout; later identical requests reuse the cached execution as usual.
  • PDF only. Combining it with an explicit non-pdf parser_type is a 400.

Convert Office Documents

Convert Word/Excel/etc. to PDF or HTML.

const result = await client.officeConvert({
  file_url: 'vault://document-123',
  format: 'pdf' // 'pdf' (default) | 'html'
})

// format: 'pdf'  → { file_url, format: 'pdf' }
// format: 'html' → { html, format: 'html', files: [{ name, fileUrl, mimeType? }] }

Async Processing & Job Status

Every operation accepts async: true (with a required webhook_url). The call then resolves to an AsyncAcceptedResponse ({ job_id, status: 'processing', ... }) instead of the synchronous result, and the API delivers the final result to your webhook. You can also poll the job directly:

const job = await client.extract({
  type: 'page',
  indexes: [1, 2],
  files: [{ file_url: 'vault://document-123', filename: 'document.pdf' }],
  async: true,
  webhook_url: 'https://example.com/webhook',
  webhook_headers: { Authorization: 'Bearer my-token' }, // optional
  idempotency_key: 'unique-key', // optional dedup key
  metadata: { completionId: 'c-1' }, // optional; echoed back as the webhook's top-level `metadata`
  no_cache: true // optional; bypass cache and force a fresh run
})

const status = await client.getJobStatus(job.job_id)
// status: { job_id, execution_id, status, lane, operation, filename, progress, result, error, created_at, completed_at }

metadata and no_cache are available on every operation (sync or async). metadata is echoed verbatim as the webhook payload's top-level metadata; no_cache skips execution reuse (cache) and forces a fresh run without changing the dedupe key.

Error Handling

The SDK provides typed error classes for different scenarios:

import {
  AuthenticationError,
  NetworkError,
  NotFoundError,
  ServerError,
  ValidationError
} from '@meistrari/document-sdk'

try {
  const result = await client.extract(request)
}
catch (error) {
  if (error instanceof ValidationError) {
    console.log('Validation failed:', error.message)
  }
  else if (error instanceof AuthenticationError) {
    console.log('Authentication failed:', error.message)
  }
  else if (error instanceof NetworkError) {
    console.log('Network error:', error.message)
  }
  else if (error instanceof ServerError) {
    console.log('Server error:', error.message, error.statusCode)
  }
  else if (error instanceof NotFoundError) {
    console.log('Not found:', error.message)
  }
}

TypeScript Support

The SDK is written in TypeScript and provides comprehensive type definitions:

import type {
  Align,
  // Async & jobs
  AsyncAcceptedResponse,
  AsyncFields,
  Content,
  // Config
  DocConfig,
  ElementPosition,
  ExtractError,
  // Extract
  ExtractRequest,
  ExtractResponse,
  // Common
  FileInput,
  IndexesInput,
  JobStatus,
  // JSON to PDF
  JsonToPdfRequest,
  JsonToPdfResponse,
  // HTML to PDF
  HtmlToPdfRequest,
  HtmlToPdfResponse,

  // Markdown to PDF
  MarkdownToPdfRequest,
  MarkdownToPdfResponse,
  // Merge
  MergeRequest,
  MergeResponse,
  // Metadata
  MetadataRequest,
  MetadataResponse,
  // Office Convert
  OfficeConvertRequest,
  OfficeConvertResponse,
  // Parse
  ParseRequest,
  ParseResponse,
  ParserType,
  // Parse — positional source layout artifact
  SourceLayoutBreakType,
  SourceLayoutDocument,
  SourceLayoutLine,
  SourceLayoutPage,
  SourceLayoutRef,
  SourceLayoutToken,
  SourceLayoutVertex,
  // PDF to Image
  PdfToImageRequest,
  PdfToImageResponse,
  // Template placeholders
  PlaceholdersRequest,
  PlaceholdersResponse,
  SingleIndex,
  // Split
  SplitRequest,
  SplitResponse,
  // Crop
  CropRequest,
  CropResponse,
  TemplateInput,
  TemplateOptions,
  // Template
  TemplateRequest,
  TemplateResponse,
} from '@meistrari/document-sdk'

File URL Formats

The SDK supports multiple file URL formats:

  • Vault URLs: vault://file-id
  • External URLs: https://example.com/file.pdf
  • Base64: For template content (inline base64 encoded data)

Build & Development

# Install dependencies
pnpm install

# Build the SDK
pnpm build

# Run tests
pnpm test

# Lint code
pnpm lint

License

UNLICENSED