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

@transform-kit/sdk

v0.1.0

Published

TransformKit SDK

Readme

@transform-kit/sdk

A pipeline engine for media processing. Define pipelines as JSON graphs, run them anywhere — browser, server, or desktop.

[Input] → [Filter: HEIC] → [Convert: PNG] → [Output]

Instead of writing one-off scripts that are tied to a specific tool, you describe what should happen as a portable pipeline and let the host decide how. The SDK handles graph execution. You provide a Transport — the backend that does the actual file conversion. Same pipeline, any backend.

Installation

npm install @transform-kit/sdk

ESM-only. Node.js 20+ is required — see engines in package.json. The HTTP client uses global fetch, File, FormData, and Blob. Works in browsers (via bundler) and desktop runtimes.

Quick start

Client (recommended)

The client manages a file queue and runs pipelines for you. Two modes:

API mode — sends files to a remote endpoint:

import { createClient } from '@transform-kit/sdk';

const client = createClient({
  baseUrl: 'https://your-api.com',
  apiKey: 'your_api_key',
});

client.addFiles([
  {
    name: 'photo.heic',
    size: 2_400_000,
    type: 'image/heic',
    file: fileFromInput,
  },
]);

// pipeline is a { nodes, edges } object — see "Low-level engine" below
await client.process(pipeline);

client.subscribe((state) => {
  console.log(state.files, state.isProcessing);
});

Transport mode — runs the pipeline in-process:

import { createClient } from '@transform-kit/sdk';

const client = createClient({
  transport: {
    async convert(file, config) {
      // your conversion logic
      return transformedBuffer;
    },
  },
});

Low-level engine

For full control without the client queue:

import { createContext, createDefaultRegistry, runPipeline } from '@transform-kit/sdk';

const registry = createDefaultRegistry();

const transport = {
  async convert(file, config) {
    // your conversion logic
    return transformedBuffer;
  },
};

const pipeline = {
  nodes: [
    { id: '1', type: 'pipeline.input' },
    {
      id: '2',
      type: 'image.convert',
      config: {
        format: { value: 'png', editable: true },
        quality: { value: 90, editable: true },
      },
    },
    { id: '3', type: 'output.console' },
  ],
  edges: [
    { source: '1', target: '2' },
    { source: '2', target: '3' },
  ],
};

const ctx = createContext(fileBuffer, 'heic');
const outputs = await runPipeline(pipeline, registry, transport, ctx);

for (const output of outputs) {
  console.log(output.metadata.outputFileName, output.file.byteLength);
}

Core concepts

Pipeline

A directed acyclic graph (DAG) of nodes connected by edges:

interface Pipeline {
  nodes: NodeInstance[];
  edges: Edge[];
}

Pipelines are plain JSON — store them, share them, version them.

Nodes

Each node is an atomic operation. Every node receives an ExecutionContext and returns one of:

  • continue — pass the (possibly modified) context downstream
  • skip — this branch doesn't apply; downstream nodes inherit the skip
  • output — terminal result; collected as a pipeline output

Built-in nodes are grouped by media: image, video, and audio each have filter, convert, and resize where it applies. Common nodes are pipeline.input (entry) and output.console (terminal — names the file and collects the result). Exact type strings and defaults live in NODE_CATALOG and defaultConfigForPipelineNodeType() in code — no need to duplicate them here.

Transport

The SDK never processes files directly. Nodes call transport.convert() and the host decides how:

interface Transport {
  convert(file: Buffer, config: Record<string, unknown>): Promise<Buffer>;
  convertFromPath?(path: string, config: Record<string, unknown>): Promise<Buffer>;
}

This makes pipelines portable. The same pipeline JSON works with any backend.

For tests and quick experiments without a real encoder, use createMockTransport().

Config fields

Node configuration uses a structured format that supports both locked presets and user-editable values:

interface ConfigField<T = unknown> {
  value: T;
  editable: boolean;
  options?: readonly T[];
  label?: string;
}

Validation

Validate a pipeline without executing it:

import { validatePipeline, createDefaultRegistry } from '@transform-kit/sdk';

const result = validatePipeline(pipeline, createDefaultRegistry());

if (!result.valid) {
  console.log(result.errors);
  // [{ code: 'NO_INPUT', message: 'Pipeline must have at least one Input node.' }]
}

Checks for: missing input/output nodes, duplicate IDs, dangling edges, unknown node types, cycles, and disconnected nodes.

Custom nodes

Register your own node types:

import { createDefaultRegistry } from '@transform-kit/sdk';

const registry = createDefaultRegistry();

registry.register({
  type: 'image.watermark',
  async execute(ctx, config, transport) {
    const text = (config.text?.value as string) ?? 'watermark';
    const buffer = await transport.convert(ctx.file, {
      operation: 'watermark',
      text,
      inputExtension: ctx.metadata.extension,
    });
    return {
      status: 'continue',
      ctx: { file: buffer, metadata: { ...ctx.metadata } },
    };
  },
});

Testing

The SDK exports a mock transport for tests:

import { createMockTransport } from '@transform-kit/sdk';

const transport = createMockTransport(); // returns input buffer unchanged

API reference

Engine

| Export | Description | | ---------------------------------------------------- | ------------------------------------------ | | runPipeline(pipeline, registry, transport, ctx) | Execute a pipeline, return output contexts | | processFiles(files, pipeline, registry, transport) | Run a pipeline once per file | | createContext(buffer, extension) | Create an execution context from a buffer | | createDefaultRegistry() | Registry with all built-in nodes | | validatePipeline(pipeline, registry?) | Structural validation without execution |

Client

| Export | Description | | ----------------------- | ----------------------------------------------- | | createClient(options) | File queue + processing (API or transport mode) |

MIME utilities

| Export | Description | | ------------------------------------------ | -------------------------------- | | IMAGE_MIME / VIDEO_MIME / AUDIO_MIME | Extension → MIME maps | | mimeFromExtension(ext) | Look up MIME by extension | | extensionFromMime(mime) | Reverse lookup | | acceptString(category) | HTML <input accept="…"> string |

Pipeline defaults

| Export | Description | | ---------------------------------------- | --------------------------------------------------- | | defaultConfigForPipelineNodeType(type) | Default config for a node type | | mergePipelineNodeConfig(type, config) | Merge saved config onto defaults | | NODE_CATALOG | Built-in node types + labels/categories for editors |

Architecture decisions

Design notes live in docs/adr/ (numbered ADRs + index).

License

MIT