# ImagineoAI JavaScript SDK

A universal JavaScript/TypeScript SDK for interacting with the ImagineoAI API from both **browser** and **Node.js** environments.

- **Dual entrypoint:** Environment-specific bundles for browser and Node.js
- **Type-safe:** All API types and schemas are shared and validated
- **Modern:** ESM, strict types, async/await, and universal fetch
- **Image Operations:** Generate, upscale, and inpaint images with a unified API
- **AI Models:** Support for Gemini 2.5, Nanobanana, Flux, OpenAI, and more
- **Cloudinary AI:** Advanced object removal, background removal, and extraction
- **Multimodal:** Combine up to 10 images with intelligent AI blending
- **Smart Batch:** AI-powered batch generation of 20+ creative variations from a single image

---

## Installation

```sh
bun add @imagineoai/javascript-sdk
# or
npm install @imagineoai/javascript-sdk
```

---

## Usage

### Describe a Run's Image

```ts
import { ImagineoAIClient } from 'imagineoai-javascript-sdk/server';

const client = new ImagineoAIClient('https://api.imagineo.ai', { apiKey: 'YOUR_API_KEY' });

const result = await client.describe({
  run_id: '123e4567-e89b-12d3-a456-426614174000',
  model: 'llava13b', // or 'clip', optional
});

console.log(result.replicate_output);
```

### Browser

```ts
import { ImagineoAIClient, upload } from "@imagineoai/javascript";

const client = new ImagineoAIClient("https://api.imagineoai.com", { apiKey: "sk-..." });

const file = new File([/* ... */], "image.png");
const result = await client.uploadImage({ file });
```

### Node.js

```ts
import { ImagineoAIClient, upload } from "@imagineoai/javascript/server";
import fs from "fs";

const client = new ImagineoAIClient("https://api.imagineoai.com", { apiKey: "sk-..." });

const buffer = fs.readFileSync("./image.png");
const result = await client.uploadImage({ file: buffer });
```

---

## API Reference

### ImagineoAIClient

#### Constructor
```ts
new ImagineoAIClient(apiUrl: string, authOptions?: ImagineoAIClientAuthOptions, options?: { debug: boolean })
```
- `apiUrl`: The base URL of the ImagineoAI API
- `authOptions`: `{ apiKey: string }` or `{ getToken: () => Promise<string> }`
- `options`: `{ debug: boolean }` (optional)

#### Methods
- `uploadImage(input: UploadImageRequest): Promise<UploadImageResponse>`
  - Uploads an image file (browser: File/Blob, Node: Buffer/Readable)
  - Supports `parent_id` to link uploads to existing runs
  - Example:
    ```ts
    const result = await client.images.upload({
      file: imageFile,
      description: 'My edited image',
      parent_id: 'original-run-uuid' // Links to parent run
    });
    ```
- `upscale(input: UpscaleImageInput): Promise<GenerateImageOutput>`
  - Upscales an existing image using a supported model.
  - Example:
    ```ts
    const result = await client.images.upscale({
      original_run_id: '...',
      upscale_factor: 2,
      use_original_prompt: true
    });
    ```
- `inpaint(input: InpaintImageInput): Promise<GenerateImageOutput>`
  - Inpaints an image using a mask and (optionally) a prompt or original run.
  - Example:
    ```ts
    const result = await client.images.inpaint({
      mask_image_url: 'https://.../mask.png',
      image_url: 'https://.../input.png',
      prompt: 'remove the background'
    });
    ```
- `getRun(runId: string): Promise<RunRelatedResponse>`
  - Fetches details for a specific run, including its associated jobs.
  - Example:
    ```ts
    const runDetails = await client.images.getRun('your-run-id');
    console.log(runDetails.data.jobs); // Array of job objects associated with the run
    ```
- `getRunJobs(runId: string): Promise<RunJobsListResponse>`
  - Fetches all jobs specifically associated with a given run ID.
  - Example:
    ```ts
    const jobsList = await client.images.getRunJobs('your-run-id');
    console.log(jobsList.data); // Array of job objects
    ```
- `backgroundRemove(input: BackgroundRemoveRequest): Promise<BackgroundRemoveResponse>`
  - Removes background from an image using Cloudinary AI
  - Automatically tracks parent-child relationships when using `run_id`
  - Example:
    ```ts
    // Using run_id (automatically sets parent_run_id)
    const result = await client.images.backgroundRemove({
      run_id: 'existing-run-uuid' // Parent run is tracked automatically
    });
    
    // Using direct image URL
    const result = await client.images.backgroundRemove({
      image_url: 'https://example.com/image.jpg'
    });
    ```
- `smartBatch.json(input: SmartBatchRequest): Promise<SmartBatchResponse>`
  - Generate 20+ AI-powered creative variations from a single image
  - Supports automatic creative brief generation or user-guided prompts
  - Example:
    ```ts
    // Automatic creative generation
    const batch = await client.images.smartBatch.json({
      image_id: 'existing-run-id',
      variation_count: 20
    });
    
    // With enhanced user prompt
    const batch = await client.images.smartBatch.json({
      image_url: 'https://example.com/source.jpg',
      prompt: 'cyberpunk style',
      enhance_prompt: true,
      variation_count: 25
    });
    
    // Check status
    const status = await client.images.smartBatch.getStatus(batch.batch_id);
    console.log(`Progress: ${status.completed_variations}/${status.total_variations}`);
    ```
- `generativeRemove(input: GenerativeRemoveRequest): Promise<GenerativeRemoveResponse>`
  - Removes specified objects from images using Cloudinary's generative AI
  - Example:
    ```ts
    const result = await client.images.genRemove({
      run_id: 'existing-run-uuid',
      prompt: 'people', // What to remove
      remove_shadow: true,
      multiple: true // Remove all instances
    });
    ```
- `extract(input: ExtractRequest): Promise<ExtractResponse>`
  - Extracts specific objects from images using Cloudinary AI
  - Example:
    ```ts
    const result = await client.images.extract({
      run_id: 'existing-run-uuid',
      prompt: 'cat', // What to extract
      multiple: false // Extract single instance
    });
    ```

#### Gemini 2.5 Features

- **Generate with Gemini 2.5**:
  ```ts
  const result = await client.images.generate({
    prompt: "A futuristic cityscape",
    model_type: "gemini-2.5",
    aspect_ratio: "16:9"
  });
  ```

- **Combine Images with Gemini 2.5**:
  ```ts
  const result = await client.images.combine.gemini25.json({
    prompt: "Blend these into a cohesive scene",
    run_ids: ["run1", "run2", "run3"],
    aspect_ratio: "1:1"
  });
  ```

- **Edit with Gemini 2.5**:
  ```ts
  const result = await client.images.edit.gemini25.json({
    prompt: "Add a sunset sky",
    original_run_id: "run123",
    mask_url: "https://example.com/mask.png",
    aspect_ratio: "16:9"
  });
  ```
  
  - Example:
    ```ts
    const result = await client.images.extract({
      run_id: 'existing-run-uuid',
      prompt: 'car', // What to extract
      mode: 'content', // or 'mask'
      preserve_alpha: true
    });
    ```
- `reframe(input: ReframeRequest): Promise<ReframeResponse>`
  - Reframes images to different aspect ratios using Luma AI
  - Automatically tracks parent-child relationships when using `run_id`
  - Example:
    ```ts
    // JSON method
    const result = await client.images.reframe.json({
      run_id: 'existing-run-uuid', // Automatically becomes the parent
      prompt: 'Expand the scene naturally',
      aspect_ratio: '16:9',
      model: 'photon-flash-1'
    });
    
    // FormData method (for file uploads)
    const result = await client.images.reframe.formData({
      image_file: imageFile,
      prompt: 'Reframe to square',
      aspect_ratio: '1:1'
    });
    ```

### Standalone Upload
- `upload(input: UploadImageRequest): Promise<UploadImageResponse>`
  - (Browser: from `@imagineoai/javascript-sdk`)
  - (Node: from `@imagineoai/javascript-sdk/server`)

### Types
- `ImagineoAIClientAuthOptions`: `{ apiKey: string } | { getToken: () => Promise<string> }`
- `UploadImageRequest`: `{ file: File | Blob | Buffer | Readable, description?: string, parent_id?: string }`
- `UploadImageResponse`: `{ image_url: string; run_id: string; created_at: string; parent_id?: string }`
- `BackgroundRemoveRequest`: `{ image_url?: string; run_id?: string }`
- `BackgroundRemoveResponse`: `{ url: string; runId: string; parentRunId?: string }`
- `GenerativeRemoveRequest`: `{ image_url?: string; run_id?: string; prompt: string; remove_shadow?: boolean; multiple?: boolean }`
- `GenerativeRemoveResponse`: `{ url: string; runId: string; parentRunId?: string }`
- `ExtractRequest`: `{ image_url?: string; run_id?: string; prompt: string; mode?: 'content' | 'mask'; preserve_alpha?: boolean; multiple?: boolean; invert?: boolean }`
- `ExtractResponse`: `{ url: string; runId: string; parentRunId?: string }`
- `ReframeRequest`: `{ prompt: string; image_url?: string; run_id?: string; aspect_ratio?: string; model?: 'photon-flash-1' | 'photon-1'; [positioning props] }` (run_id automatically becomes parent)
- `ReframeFormDataRequest`: Similar to ReframeRequest but accepts `image_file` for direct file uploads
- `RunRelatedResponse`: An object containing run details, including an array of associated `jobs` within its `data` property.
- `RunJobsListResponse`: An object containing an array of `Job` objects within its `data` property.

---

## Parent-Child Relationship Tracking

The SDK supports tracking relationships between images through the `parent_id` field. This helps maintain image lineage across operations:

### Automatic Parent Tracking
When using `run_id` with operations like background removal and reframe, the parent relationship is automatically established:
```ts
// Original image has run_id: 'abc-123'

// Background removal
const bgRemoved = await client.images.backgroundRemove({
  run_id: 'abc-123' // This becomes the parent
});
// bgRemoved.parentRunId === 'abc-123'

// Reframe
const reframed = await client.images.reframe.json({
  run_id: 'abc-123', // This becomes the parent
  prompt: 'Expand to 16:9',
  aspect_ratio: '16:9'
});
// reframed.parent_id === 'abc-123'
```

### Manual Parent Tracking
For upload operations, you can manually specify the parent:
```ts
// Upload with parent
const upload = await client.images.upload({
  file: editedImage,
  parent_id: 'original-run-uuid'
});
```

### Use Cases
- Track editing history and image derivations
- Build image family trees
- Maintain audit trails for generated content
- Link variations back to originals

---

## Environment-specific Details

- **Browser**: Uses native `fetch` and `FormData`. Only accepts `File` or `Blob` for uploads.
- **Node.js**: Uses [`formdata-node`](https://github.com/octet-stream/form-data) and [`undici`](https://github.com/nodejs/undici) for fetch and form uploads. Accepts `Buffer` or `Readable` streams.

---

## Shared Utilities

- `getAuthHeader(authOptions)`: Returns the correct Authorization header for API key or token-based auth.
- `validateUploadInput(input)`: Throws if the upload input is invalid.

---

## Development

- All shared types are in `src/types.ts`.
- Shared helpers in `src/shared/`.
- Environment-specific implementations in `src/browser/` and `src/server/`.

---

## License
MIT
