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

@eikonstudio/nano

v0.3.0

Published

Strongly typed image generation helpers for Google's Gemini and Imagen APIs

Readme

@eikonstudio/nano

Strongly typed image-generation helpers for Google's Gemini and Imagen APIs.

@eikonstudio/nano gives you a small API with good defaults while still exposing the important image-generation features from @google/genai: prompt generation, image editing, multi-image composition, chat-based iteration, grounding, and Imagen config passthrough.

Installation

bun add @eikonstudio/nano

Set your API key in a .env file:

GEMINI_API_KEY=your-api-key

@google/genai is installed transitively by @eikonstudio/nano, so you do not need to add it separately.

Quick Start

import { generateImage } from "@eikonstudio/nano";

const result = await generateImage(
  "gemini-2.5-flash-image",
  "A nano banana dish in a Michelin-star restaurant",
);

console.log(result.text);
console.log(result.images[0]?.mimeType);
console.log(result.images[0]?.buffer);

AI SDK Tool

createAiSdkImageTool() returns a plain tool object that matches the AI SDK tools contract (description, inputSchema, and execute), so you can pass it directly to generateText() or streamText().

import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { createAiSdkImageTool } from "@eikonstudio/nano";

const result = await generateText({
  model: openai("gpt-5"),
  prompt: "Create a product image tool call for a luxury banana perfume ad.",
  tools: {
    generateProductImage: createAiSdkImageTool({
      defaultModel: "gemini-3.1-flash-image-preview",
      generationOptions: {
        apiKey: process.env.GEMINI_API_KEY,
        responseModalities: ["TEXT", "IMAGE"],
      },
    }),
  },
});

If you omit defaultModel, the tool input must include both model and prompt.

The tool result is JSON-safe:

  • generated images are returned as base64 + MIME type
  • buffer fields are removed
  • the raw provider response is omitted

That makes it safer to forward through AI SDK multi-step tool calls.

API

generateImage()

Use the simplest entrypoint when you want the package to auto-detect Gemini vs Imagen from the model string.

import { generateImage } from "@eikonstudio/nano";

const result = await generateImage(
  "gemini-3.1-flash-image-preview",
  "A cinematic photo of a glowing banana-shaped observatory at night",
  {
    aspectRatio: "16:9",
    imageSize: "2K",
    responseModalities: ["TEXT", "IMAGE"],
  },
);

generateGeminiImage()

Use the Gemini-specific helper when you want stronger autocomplete for Gemini-only options.

import { generateGeminiImage } from "@eikonstudio/nano";

const result = await generateGeminiImage(
  "gemini-3.1-flash-image-preview",
  "A kids cookbook infographic explaining photosynthesis",
  {
    googleSearch: { webSearch: true, imageSearch: true },
    includeThoughts: true,
    thinkingLevel: "high",
    responseModalities: ["TEXT", "IMAGE"],
  },
);

console.log(result.groundingMetadata);

generateImagenImage()

Use Imagen when you want the dedicated generateImages() flow and Imagen-specific config.

import { generateImagenImage } from "@eikonstudio/nano";

const result = await generateImagenImage(
  "imagen-4.0-generate-001",
  "A premium product photo for banana perfume",
  {
    numberOfImages: 2,
    negativePrompt: "blurry, low contrast",
    outputMimeType: "image/jpeg",
  },
);

editImage()

Edit an existing image with Gemini image models.

import { editImage } from "@eikonstudio/nano";
import { readFileSync } from "node:fs";

const result = await editImage("gemini-3.1-flash-image-preview", {
  images: [
    {
      data: readFileSync("./living-room.png"),
      mimeType: "image/png",
    },
  ],
  prompt: "Change only the blue sofa to a vintage brown leather sofa.",
});

composeImages()

Combine multiple images into a new composition.

import { composeImages } from "@eikonstudio/nano";
import { readFileSync } from "node:fs";

const result = await composeImages("gemini-3.1-flash-image-preview", {
  images: [
    { data: readFileSync("./dress.png"), mimeType: "image/png" },
    { data: readFileSync("./model.png"), mimeType: "image/png" },
  ],
  prompt: "Place the dress onto the model in a polished ecommerce product shot.",
});

startImageChat()

Start a multi-turn image workflow for iterative editing.

import { startImageChat } from "@eikonstudio/nano";

const chat = startImageChat("gemini-3.1-flash-image-preview", {
  responseModalities: ["TEXT", "IMAGE"],
  googleSearch: true,
});

await chat.send("Create an infographic about photosynthesis.");
const updated = await chat.send("Translate the infographic to Spanish.");

createClient()

Reuse a configured client across calls.

import { createClient } from "@eikonstudio/nano";

const nano = createClient({ apiKey: process.env.GEMINI_API_KEY });
const result = await nano.generateImage(
  "gemini-2.5-flash-image",
  "A futuristic banana spacecraft",
);

createAiSdkImageTool()

Create an AI SDK-compatible tool backed by Nano's generateImage() helper.

import { createAiSdkImageTool } from "@eikonstudio/nano";

const generateImageTool = createAiSdkImageTool({
  defaultModel: "imagen-4.0-generate-001",
  generationOptions: {
    apiKey: process.env.GEMINI_API_KEY,
    numberOfImages: 1,
  },
});

Result Shape

All generation paths return a normalized GenerateImageResult:

type GenerateImageResult = {
  images: Array<{
    buffer: Buffer;
    base64: string;
    mimeType: string;
    thought?: boolean;
  }>;
  texts: string[];
  thoughts: Array<{
    text?: string;
    image?: {
      buffer: Buffer;
      base64: string;
      mimeType: string;
      thought?: boolean;
    };
  }>;
  parts: Array<
    | { type: "text"; text: string; thought?: boolean }
    | {
        type: "image";
        image: {
          buffer: Buffer;
          base64: string;
          mimeType: string;
          thought?: boolean;
        };
        thought?: boolean;
      }
  >;
  text?: string;
  model: string;
  provider: "gemini" | "imagen";
  groundingMetadata?: Record<string, unknown>;
  raw: unknown;
};

Real API Smoke Test

The package includes a live integration test that can hit the real API.

Create packages/nano/.env.test:

GEMINI_API_KEY=your-api-key
NANO_LIVE_TEST=1

Then run:

cd packages/nano
bun run test:live

Optional output file:

GEMINI_API_KEY=your-api-key
NANO_LIVE_TEST=1
NANO_LIVE_TEST_OUTPUT=./tmp/nano-live.png

Bun automatically loads .env files. For tests, prefer .env.test. Note: Bun does not load .env.local when NODE_ENV=test.

The live test is skipped unless NANO_LIVE_TEST=1 is set.

Development

cd packages/nano
bun test
bun run lint
bun run build

License

MIT