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

@yetter/client

v0.0.14

Published

The Yetter JS Client provides a convenient way to interact with the Yetter API for image generation. It supports different modes of operation: subscribing to real-time updates, submitting jobs to a queue, and streaming events.

Readme

Yetter JS Client

The Yetter JS Client provides a convenient way to interact with the Yetter API for image generation. It supports different modes of operation: subscribing to real-time updates, submitting jobs to a queue, and streaming events.

Installation

npm install @yetter/client

Testing

Run the local test suite:

npm test

Tests are automatically omitted from npm releases because package.json uses a files whitelist (dist, README.md).

Authentication

The client requires a Yetter API key for authentication. Ensure you have the YTR_API_KEY or REACT_APP_YTR_API_KEY environment variable set:

export YTR_API_KEY="your_api_key_here"
export REACT_APP_YTR_API_KEY="your_api_key_here"

Alternatively, you can configure the API key (and optionally the API endpoint) programmatically using the yetter.configure() method. This is useful if you prefer not to use environment variables or need to set the key at runtime.

import { yetter } from "@yetter/client";

yetter.configure({
  apiKey: "your_api_key_here",
  // endpoint: "https://custom.api.yetter.ai" // if you need to override the default endpoint
});

If yetter.configure() is used, it will override any API key found in environment variables for subsequent API calls. If neither environment variables are set nor yetter.configure() is called with an API key, an error will be thrown when attempting to make an API call.

Instance Client (Recommended for Concurrent Apps)

For server or multi-tenant workloads, prefer creating isolated client instances instead of sharing global static state.

import { YetterClient } from "@yetter/client";

const teamAClient = new YetterClient({ apiKey: process.env.TEAM_A_YTR_API_KEY! });
const teamBClient = new YetterClient({ apiKey: process.env.TEAM_B_YTR_API_KEY! });

const result = await teamAClient.subscribe("ytr-ai/qwen/image-edit/i2i", {
  input: {
    prompt: "Transform this image to watercolor painting style",
    image_url: ["https://example.com/input.jpg"],
    num_inference_steps: 28,
  },
});

Running i2i Example Scripts

For image-to-image models (such as ytr-ai/qwen/image-edit/i2i), provide both a prompt and an input image.

export YTR_API_KEY="your_api_key_here"
export YTR_MODEL="ytr-ai/qwen/image-edit/i2i"
export YTR_IMAGE_PATH="./bowow2.jpeg"
export YTR_PROMPT="Transform this image to watercolor painting style"

Then run one of:

npm run test:submit
npm run test:subscribe
npm run test:stream

Core Functionalities

The client is available via the yetter object imported from yetter-js (or the relevant path to client.js/client.ts if used directly).

import { yetter } from "@yetter/client";

1. yetter.subscribe()

This function submits an image generation request and long-polls for the result. It provides updates on the queue status and logs if requested.

Features:

  • Submits a generation request.
  • Polls for status updates until the job is "COMPLETED" or "ERROR".
  • Timeout after 30 minutes, with an attempt to cancel the job.
  • Optional onQueueUpdate callback for real-time feedback on queue position and status.
  • Optional logs flag to include logs in status updates.
  • Optional pollIntervalMs to control status polling interval (default: 2000ms).

Example (from examples/subscribe.ts):

import { yetter } from "@yetter/client";

async function main() {
    const model = "ytr-ai/flux/dev"; // Replace with your desired model
    try {
        console.log("\n--- Starting Subscribe Test ---");
        const result = await yetter.subscribe(model, {
            input: {
                prompt: "a vibrant coral reef, underwater photography",
                num_inference_steps: 28, // Required
            },
            logs: true,
            onQueueUpdate: (update) => {
                console.log(`[Queue Update] Status: ${update.status}, Position: ${update.queue_position}`);
                if (update.status === "IN_PROGRESS" && update.logs) {
                    console.log("Logs:");
                    update.logs.map((log) => log.message).forEach(logMessage => console.log(`  - ${logMessage}`));
                } else if (update.status === "COMPLETED") {
                    console.log("Processing completed!");
                } else if (update.status === "ERROR") {
                    console.error("Processing failed. Logs:", update.logs);
                }
            },
        });

        console.log("\n--- Subscribe Test Result ---");
        console.log("Results:", result); // Contains image data, prompt, etc.

    } catch (err: any) {
        console.error("\n--- Subscribe Test Failed ---");
        console.error("Error during subscribe:", err.message || err);
        // ... error handling ...
    }
}

main();

2. yetter.stream()

This function submits an image generation request and returns an async iterable stream of events using Server-Sent Events (SSE). This allows for real-time updates on the job's progress, status, and logs.

Features:

  • Initiates a request and establishes an SSE connection.
  • Automatically falls back to status polling if SSE transport repeatedly fails.
  • Provides an AsyncIterator (Symbol.asyncIterator) to loop through status events (StreamEvent).
  • A done() method: Returns a Promise that resolves with the final GetResponseResponse after the server emits event: done (successful completion), or rejects if the server emits event: error or the final response cannot be fetched.
  • A cancel() method: Requests cancellation on the backend; the stream naturally ends when the server emits data (with status: "CANCELLED") followed by event: done.
  • A getRequestId() method: Returns the request ID for the stream.

Transport options (StreamOptions):

  • disableSse?: boolean - skip SSE and use polling only.
  • pollIntervalMs?: number - polling interval for fallback/forced polling (default: 2000).
  • sseMaxConsecutiveErrors?: number - number of transport errors tolerated before fallback (default: 3).

Events (StreamEvent): Each event pushed by the stream is an object typically including:

  • status: Current status (e.g., "IN_QUEUE", "IN_PROGRESS", "COMPLETED", "CANCELLED", "ERROR").
  • Other model-specific data.

Example (from examples/stream.ts):

import { yetter } from "@yetter/client"; // Adjust path as needed

async function main() {
    const model = "ytr-ai/flux/dev";
    let streamRequestId = "";
    try {
        console.log("\n--- Starting Stream Test ---");
        const streamInstance = await yetter.stream(model, {
            input: {
                prompt: "a bioluminescent forest at night, fantasy art",
                num_inference_steps: 28, // Required
            },
        });
        streamRequestId = streamInstance.getRequestId();
        console.log(`Stream initiated for Request ID: ${streamRequestId}`);

        // Iterate over stream events (informational; termination is driven by server 'done'/'error' events)
        for await (const event of streamInstance) {
            console.log(
              `[STREAM EVENT - ${streamRequestId}] Status: ${event.status}, QPos: ${event.queue_position}`
            );
            if (event.logs && event.logs.length > 0) {
                console.log(`  Logs for ${streamRequestId}:`);
                event.logs.forEach(log => console.log(`    - ${log.message}`));
            }
        }
        console.log(`Stream for ${streamRequestId} finished iterating events.`);

        // Final result becomes available after server emits `event: done`
        const result = await streamInstance.done();
        console.log("\n--- Stream Test Final Result ---");
        console.log("Generated Images:", result.images);

    } catch (err: any) {
        console.error(`\n--- Stream Test Failed (Request ID: ${streamRequestId || 'UNKNOWN'}) ---`);
        console.error("Error during stream test:", err.message || err);
    }
}

main();

3. yetter.queue

This namespace provides methods for managing jobs in a queue-based workflow. This is useful if you want to submit a job and check its status or retrieve its result later, without maintaining a persistent connection.

yetter.queue.submit(model, options)

Submits a job to the queue.

  • model: The model ID (e.g., "ytr-ai/flux/dev").
  • options.input: An object containing the input parameters for the model (e.g., { prompt: "your prompt" }).

Returns a promise that resolves to an object containing request_id, status, and queue_position.

yetter.queue.status(model, options)

Checks the status of a previously submitted job.

  • model: The model ID.
  • options.requestId: The request_id obtained from queue.submit().

Returns a promise that resolves to an object containing the status data and requestId.

yetter.queue.result(model, options)

Retrieves the result of a completed job.

  • model: The model ID.
  • options.requestId: The request_id of the completed job.

Returns a promise that resolves to an object containing the result data (e.g., images, prompt) and requestId.

Example (from examples/submit.ts):

import { yetter } from "@yetter/client";

async function main() {
    const model = "ytr-ai/flux/dev";
    try {
        console.log("\n--- Starting Queue Submit Test ---");
        const { request_id } = await yetter.queue.submit(model, {
            input: {
                prompt: "a fluffy white kitten playing with a yarn ball",
                num_inference_steps: 28, // Required
            },
        });
        console.log("Request ID:", request_id);

        if (request_id) {
            console.log(`\n--- Polling for status of Request ID: ${request_id} (10-minute timeout) ---`);
            // Polling logic:
            let success = false;
            const startTime = Date.now();
            const timeoutMilliseconds = 10 * 60 * 1000; // 10 minutes
            const pollIntervalMilliseconds = 10 * 1000; // Poll every 10 seconds

            while (Date.now() - startTime < timeoutMilliseconds) {
                const statusResult = await yetter.queue.status(model, { requestId: request_id });
                const currentStatus = statusResult.data.status;
                console.log(`[${new Date().toLocaleTimeString()}] Status: ${currentStatus}, QPos: ${statusResult.data.queue_position}`);

                if (currentStatus === "COMPLETED") {
                    const finalResult = await yetter.queue.result(model, { requestId: request_id });
                    console.log("\n--- Get Result Test Succeeded ---");
                    console.log("Image Data:", finalResult.data.images);
                    success = true;
                    break;
                } else if (currentStatus === "ERROR") {
                    console.error(`Request ${request_id} FAILED. Logs:`, statusResult.data.logs);
                    break;
                }
                await new Promise(resolve => setTimeout(resolve, pollIntervalMilliseconds));
            }
            if (!success) console.error(`Polling Timed Out or Failed for ${request_id}`);
        }
    } catch (err: any) {
        console.error("\n--- Queue Submit Test Failed ---", err.message);
    }
}

main();

4. Uploading Input Images

For models that require an input image (e.g., image-to-image, style transfer), you can upload your image using yetter.uploadFile() (Node.js) or yetter.uploadBlob() (browser).

Features:

  • Support for both Node.js filesystem paths and browser File/Blob objects
  • Automatic single-part or multipart upload based on file size
  • Progress tracking with optional callback
  • Returns a public URL to use in generation requests

Node.js Example

import { yetter } from "@yetter/client";

yetter.configure({ apiKey: process.env.YTR_API_KEY });

// Upload image
const uploadResult = await yetter.uploadFile("./input-image.jpg", {
    onProgress: (percent) => console.log(`Upload: ${percent}%`),
});

// Use in generation
const result = await yetter.subscribe("ytr-ai/model/i2i", {
    input: {
        prompt: "Transform to anime style",
        image_url: [uploadResult.url],
    },
});

Browser Example

// HTML: <input type="file" id="imageInput" accept="image/*">

const fileInput = document.getElementById('imageInput') as HTMLInputElement;
fileInput.addEventListener('change', async () => {
    const file = fileInput.files![0];

    const uploadResult = await yetter.uploadBlob(file, {
        onProgress: (pct) => updateProgressBar(pct),
    });

    console.log("Uploaded:", uploadResult.url);
});

Upload Options:

  • onProgress?: (progress: number) => void - Progress callback (0-100)
  • timeout?: number - Timeout in milliseconds (default: 5 minutes)
  • filename?: string - Custom filename (browser uploads)

Error Handling

The client functions generally throw errors for API issues, network problems, or failed generation requests. Ensure you wrap API calls in try...catch blocks to handle potential errors gracefully. Specific error messages or details (like logs for failed jobs) are often included in the thrown error object or the final status response. The stream() function's done() promise will reject on failure or premature closure, and iterating the stream might also throw if connection errors occur.