@coreviz/sdk 1.0.17 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -144,26 +144,24 @@ const editedImage = await coreviz.edit('https://example.com/photo.jpg', {
144
144
  });
145
145
  ```
146
146
 
147
- ### `coreviz.batchGenerate(prompt, options)`
147
+ ### `coreviz.generate(prompt, options)`
148
148
 
149
- Generates multiple images based on a text prompt, optionally using reference images for style/structure guidance.
149
+ Generates an image based on a text prompt, optionally using reference images for style/structure guidance.
150
150
 
151
151
  **Parameters:**
152
152
  - `prompt` (string): The text description of the image(s) to generate.
153
153
  - `options` (object, optional):
154
154
  - `referenceImages` (string[], optional): Array of reference images (URL/base64) to guide generation.
155
- - `count` (number, optional): Number of images to generate (default: 1).
156
155
  - `aspectRatio` (string, optional): Target aspect ratio (e.g., `'1:1'`, `'16:9'`, `'4:3'`).
157
156
  - `model` (string, optional): The model to use (default: `'google/nano-banana-pro'`).
158
157
 
159
158
  **Returns:**
160
- - `Promise<string[]>`: An array of generated images as URLs.
159
+ - `string`: The generated images as a URL.
161
160
 
162
161
  **Example:**
163
162
 
164
163
  ```typescript
165
- const images = await coreviz.batchGenerate("A futuristic city skyline", {
166
- count: 4,
164
+ const images = await coreviz.generate("A futuristic city skyline", {
167
165
  aspectRatio: "16:9"
168
166
  });
169
167
  ```
@@ -223,3 +221,207 @@ Utility function to resize images client-side or server-side before processing.
223
221
  const resized = await coreviz.resize(myFileObject, 800, 600);
224
222
  // or import { resize } from '@coreviz/sdk';
225
223
  ```
224
+
225
+ ---
226
+
227
+ ## Library Management API
228
+
229
+ The SDK also exposes namespaced methods for programmatically managing your CoreViz visual library — browsing collections, searching media, organizing folders, and managing tags. These require authentication via a user token (from `coreviz login`) or an API key.
230
+
231
+ ```typescript
232
+ const coreviz = new CoreViz({ token: 'your_session_token' });
233
+ // or: new CoreViz({ apiKey: 'your_api_key' })
234
+ // or: new CoreViz({ token, baseUrl: 'http://localhost:3000' }) // for local dev
235
+ ```
236
+
237
+ ### `coreviz.collections.list()`
238
+
239
+ List all collections in the user's current organization.
240
+
241
+ **Returns:** `Promise<Collection[]>`
242
+
243
+ ```typescript
244
+ const collections = await coreviz.collections.list();
245
+ // [{ id, name, icon, type, organizationId }, ...]
246
+ ```
247
+
248
+ ---
249
+
250
+ ### `coreviz.collections.create(name, icon?)`
251
+
252
+ Create a new collection in the user's current organization.
253
+
254
+ **Parameters:**
255
+ - `name` (string): Collection name.
256
+ - `icon` (string, optional): Emoji or icon name.
257
+
258
+ **Returns:** `Promise<Collection>`
259
+
260
+ ```typescript
261
+ const collection = await coreviz.collections.create('Product Photos', '📦');
262
+ ```
263
+
264
+ ---
265
+
266
+ ### `coreviz.media.browse(collectionId, options?)`
267
+
268
+ List media items and folders inside a collection. Navigates the ltree folder hierarchy.
269
+
270
+ **Parameters:**
271
+ - `collectionId` (string): The collection to browse.
272
+ - `options` (object, optional):
273
+ - `path` (string): ltree path to list (e.g. `"collectionId.folderId"`). Defaults to collection root.
274
+ - `limit` / `offset` (number): Pagination.
275
+ - `type` (`'image' | 'video' | 'folder' | 'all'`): Filter by type.
276
+ - `dateFrom` / `dateTo` (string): Filter by creation date (`YYYY-MM-DD`).
277
+ - `sortBy` / `sortDirection`: Sort options.
278
+ - `tagFilters` (`Record<string, string[]>`): Filter by tag groups.
279
+
280
+ **Returns:** `Promise<BrowseResult>` — `{ media: Media[], pagination }`
281
+
282
+ ```typescript
283
+ const { media } = await coreviz.media.browse('abc123', { path: 'abc123.folderXyz', limit: 50 });
284
+ ```
285
+
286
+ ---
287
+
288
+ ### `coreviz.media.search(query, options?)`
289
+
290
+ Semantically search across all media in the organization using natural language.
291
+
292
+ **Parameters:**
293
+ - `query` (string): Natural language search query.
294
+ - `options.limit` (number, optional): Max results (default 20).
295
+
296
+ **Returns:** `Promise<SearchResult[]>` — each result includes `mediaId`, `blobUrl`, `objects`, `rank`, `caption`.
297
+
298
+ ```typescript
299
+ const results = await coreviz.media.search('red shoes on a white background', { limit: 10 });
300
+ ```
301
+
302
+ ---
303
+
304
+ ### `coreviz.media.get(mediaId)`
305
+
306
+ Get full details for a media item: blob URL, dimensions, tags, detected objects, and version info.
307
+
308
+ **Returns:** `Promise<Media>`
309
+
310
+ ```typescript
311
+ const item = await coreviz.media.get('mediaId123');
312
+ console.log(item.blob, item.metadata?.tags, item.frames);
313
+ ```
314
+
315
+ ---
316
+
317
+ ### `coreviz.media.rename(mediaId, name)`
318
+
319
+ Rename a media item.
320
+
321
+ **Returns:** `Promise<Media>`
322
+
323
+ ```typescript
324
+ await coreviz.media.rename('mediaId123', 'hero-shot-final.jpg');
325
+ ```
326
+
327
+ ---
328
+
329
+ ### `coreviz.media.move(mediaId, destinationPath)`
330
+
331
+ Move a media item or folder to a different location within the same collection.
332
+
333
+ **Parameters:**
334
+ - `destinationPath` (string): ltree path of the destination folder (e.g. `"collectionId.targetFolder"`).
335
+
336
+ **Returns:** `Promise<{ id, newPath }>`
337
+
338
+ ```typescript
339
+ await coreviz.media.move('mediaId123', 'collectionId.archiveFolder');
340
+ ```
341
+
342
+ ---
343
+
344
+ ### `coreviz.media.addTag(mediaId, label, value)` / `removeTag(...)`
345
+
346
+ Add or remove a tag from a media item. Tags are `label` (group) + `value` pairs.
347
+
348
+ ```typescript
349
+ await coreviz.media.addTag('mediaId123', 'color', 'red');
350
+ await coreviz.media.removeTag('mediaId123', 'color', 'red');
351
+ ```
352
+
353
+ ---
354
+
355
+ ### `coreviz.media.findSimilar(collectionId, objectId, options?)`
356
+
357
+ Find visually similar media using a detected object ID (from `media.get()` frames).
358
+
359
+ **Parameters:**
360
+ - `collectionId` (string): The collection to search within.
361
+ - `objectId` (string): ID of a detected object to use as the similarity query.
362
+ - `options.model` (string): `'faces'`, `'objects'`, or `'shoeprints'`.
363
+
364
+ **Returns:** `Promise<BrowseResult>`
365
+
366
+ ```typescript
367
+ const similar = await coreviz.media.findSimilar('collectionId', 'objectId456', { model: 'faces' });
368
+ ```
369
+
370
+ ---
371
+
372
+ ### `coreviz.folders.create(collectionId, name, path?)`
373
+
374
+ Create a new folder inside a collection.
375
+
376
+ **Returns:** `Promise<Folder>`
377
+
378
+ ```typescript
379
+ const folder = await coreviz.folders.create('collectionId', 'Spring 2025', 'collectionId.campaigns');
380
+ ```
381
+
382
+ ---
383
+
384
+ ### `coreviz.tags.list(collectionId)`
385
+
386
+ Aggregate all tag groups and values across an entire collection.
387
+
388
+ **Returns:** `Promise<Record<string, string[]>>`
389
+
390
+ ```typescript
391
+ const tags = await coreviz.tags.list('collectionId');
392
+ // { color: ['red', 'blue'], category: ['product', 'lifestyle'] }
393
+ ```
394
+
395
+ ---
396
+
397
+ ### `coreviz.media.upload(file, options)`
398
+
399
+ Upload a photo or video to CoreViz.
400
+
401
+ **Parameters:**
402
+ - `file`: Local file path string (Node.js), `File` object (browser), or `Blob`
403
+ - `options`:
404
+ - `collectionId` (string, required): Target collection
405
+ - `path` (string, optional): ltree folder path (e.g. `"collectionId.folderId"`). Defaults to collection root.
406
+ - `name` (string, optional): Override the file name stored in CoreViz
407
+
408
+ **Returns:** `Promise<UploadResult>` — `{ mediaId, url, message }`
409
+
410
+ **Supported formats:** JPEG, PNG, GIF, WebP, HEIC, MP4, WebM, MOV, AVI
411
+
412
+ ```typescript
413
+ // Node.js — local file path
414
+ const result = await coreviz.media.upload('/path/to/photo.jpg', {
415
+ collectionId: 'abc123',
416
+ path: 'abc123.campaignFolder',
417
+ name: 'hero-shot.jpg',
418
+ });
419
+ console.log(result.mediaId, result.url);
420
+
421
+ // Browser — File object
422
+ const result = await coreviz.media.upload(fileInputEvent.target.files[0], {
423
+ collectionId: 'abc123',
424
+ });
425
+ ```
426
+
427
+ > **Note:** File path strings are not supported on React Native / Expo. Pass a `File` or `Blob` object instead.
package/dist/coreviz.d.ts CHANGED
@@ -1,6 +1,143 @@
1
1
  export interface CoreVizConfig {
2
2
  apiKey?: string;
3
3
  token?: string;
4
+ /** Override the API base URL (default: https://lab.coreviz.io) */
5
+ baseUrl?: string;
6
+ }
7
+ export interface UserContext {
8
+ userId: string;
9
+ email: string;
10
+ name: string;
11
+ organizationId: string;
12
+ organizationName: string | null;
13
+ }
14
+ export interface Collection {
15
+ id: string;
16
+ name: string;
17
+ icon?: string;
18
+ type: string;
19
+ organizationId: string;
20
+ }
21
+ export interface MediaObject {
22
+ id: string;
23
+ type: string;
24
+ label: string;
25
+ }
26
+ export interface MediaFrame {
27
+ id: string;
28
+ timestamp: number;
29
+ blob: string;
30
+ objects: MediaObject[];
31
+ }
32
+ export interface Media {
33
+ id: string;
34
+ name: string;
35
+ type: 'image' | 'video' | 'folder';
36
+ blob: string | null;
37
+ path: string;
38
+ width?: number;
39
+ height?: number;
40
+ sizeBytes?: number;
41
+ metadata?: Record<string, unknown>;
42
+ frames?: MediaFrame[];
43
+ createdAt?: string;
44
+ _score?: number;
45
+ }
46
+ export interface Folder {
47
+ id: string;
48
+ name: string;
49
+ path: string;
50
+ collectionId: string;
51
+ }
52
+ export interface BrowseOptions {
53
+ path?: string;
54
+ limit?: number;
55
+ offset?: number;
56
+ type?: 'image' | 'video' | 'folder' | 'all';
57
+ dateFrom?: string;
58
+ dateTo?: string;
59
+ sortBy?: string;
60
+ sortDirection?: 'asc' | 'desc';
61
+ tagFilters?: Record<string, string[]>;
62
+ }
63
+ export interface BrowseResult {
64
+ media: Media[];
65
+ pagination: {
66
+ total: number;
67
+ limit: number;
68
+ offset: number;
69
+ hasMore: boolean;
70
+ };
71
+ }
72
+ export interface SearchResult {
73
+ mediaId: string;
74
+ mediaName: string;
75
+ mediaType: string;
76
+ blobUrl: string;
77
+ objects: MediaObject[];
78
+ rank: number;
79
+ caption?: string;
80
+ }
81
+ export interface SearchOptions {
82
+ limit?: number;
83
+ }
84
+ export interface SimilarityOptions {
85
+ limit?: number;
86
+ model?: string;
87
+ }
88
+ export interface UploadOptions {
89
+ /** Target collection ID */
90
+ collectionId: string;
91
+ /** ltree folder path to upload into (e.g. "collectionId.folderId"). Defaults to collection root. */
92
+ path?: string;
93
+ /** Override the file name stored in CoreViz */
94
+ name?: string;
95
+ }
96
+ export interface UploadResult {
97
+ mediaId: string;
98
+ url: string;
99
+ message: string;
100
+ }
101
+ export interface CollectionsNamespace {
102
+ /** List all collections in the user's current organization */
103
+ list(): Promise<Collection[]>;
104
+ /** Create a new collection in the user's current organization */
105
+ create(name: string, icon?: string): Promise<Collection>;
106
+ }
107
+ export interface MediaNamespace {
108
+ /** Browse/list media items in a collection folder */
109
+ browse(collectionId: string, options?: BrowseOptions): Promise<BrowseResult>;
110
+ /** Semantic search across all org media */
111
+ search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
112
+ /** Get full details for a media item */
113
+ get(mediaId: string): Promise<Media>;
114
+ /** Rename a media item */
115
+ rename(mediaId: string, name: string): Promise<Media>;
116
+ /** Move a media item to a new ltree destination path */
117
+ move(mediaId: string, destinationPath: string): Promise<{
118
+ id: string;
119
+ newPath: string;
120
+ }>;
121
+ /** Add a tag group+value to a media item */
122
+ addTag(mediaId: string, label: string, value: string): Promise<void>;
123
+ /** Remove a tag group+value from a media item */
124
+ removeTag(mediaId: string, label: string, value: string): Promise<void>;
125
+ /** Find visually similar media using an object ID */
126
+ findSimilar(collectionId: string, objectId: string, options?: SimilarityOptions): Promise<BrowseResult>;
127
+ /**
128
+ * Upload a photo or video to CoreViz.
129
+ * - Node.js: pass a local file path string
130
+ * - Browser: pass a File or Blob object
131
+ */
132
+ upload(file: string | File | Blob, options: UploadOptions): Promise<UploadResult>;
133
+ }
134
+ export interface FoldersNamespace {
135
+ /** Create a folder inside a collection */
136
+ create(collectionId: string, name: string, path?: string): Promise<Folder>;
137
+ }
138
+ export interface TagsNamespace {
139
+ /** Aggregate all tag groups + values across a collection */
140
+ list(collectionId: string): Promise<Record<string, string[]>>;
4
141
  }
5
142
  export interface DescribeOptions {
6
143
  }
@@ -27,21 +164,32 @@ export interface EmbedOptions {
27
164
  export interface EmbedResponse {
28
165
  embedding: number[];
29
166
  }
30
- export interface BatchGenerateOptions {
167
+ export interface GenerateOptions {
31
168
  referenceImages?: string[];
32
- count?: number;
33
169
  aspectRatio?: '1:1' | '2:3' | '3:2' | '3:4' | '4:3' | '4:5' | '5:4' | '9:16' | '16:9' | '21:9';
34
- model?: 'google/nano-banana' | 'google/nano-banana-pro';
170
+ model?: 'google/nano-banana' | 'google/nano-banana-pro' | 'seedream-4' | 'flux-kontext-max';
35
171
  }
36
172
  export declare class CoreViz {
37
173
  private apiKey?;
38
174
  private token?;
175
+ private _baseUrl;
176
+ private _orgIdCache;
177
+ collections: CollectionsNamespace;
178
+ media: MediaNamespace;
179
+ folders: FoldersNamespace;
180
+ tags: TagsNamespace;
39
181
  constructor(config?: CoreVizConfig);
40
182
  private getHeaders;
183
+ /** Resolve the current user's org ID (cached after first call) */
184
+ private _me;
185
+ /** GET request helper for management endpoints */
186
+ private _fetch;
187
+ /** Non-GET request helper for management endpoints */
188
+ private _fetchMethod;
41
189
  private handleResponse;
42
190
  describe(image: string, options?: DescribeOptions): Promise<string>;
43
191
  edit(image: string, options: EditOptions): Promise<string>;
44
- batchGenerate(prompt: string, options?: BatchGenerateOptions): Promise<string[]>;
192
+ generate(prompt: string, options?: GenerateOptions): Promise<string>;
45
193
  tag(image: string, options: TagOptions): Promise<TagResponse>;
46
194
  private tagLocal;
47
195
  embed(input: string, options?: EmbedOptions): Promise<EmbedResponse>;
package/dist/coreviz.js CHANGED
@@ -37,9 +37,154 @@ exports.CoreViz = void 0;
37
37
  const resize_1 = require("./resize");
38
38
  class CoreViz {
39
39
  constructor(config = {}) {
40
+ this._orgIdCache = null;
40
41
  this.apiKey = config.apiKey || (typeof process !== 'undefined' ? process.env.COREVIZ_API_KEY : undefined);
41
42
  this.token = config.token;
43
+ this._baseUrl = config.baseUrl || 'https://lab.coreviz.io';
44
+ // ── Management namespaces ────────────────────────────────────────────
45
+ this.collections = {
46
+ list: async () => {
47
+ const { organizationId } = await this._me();
48
+ const data = await this._fetch(`/api/organization/${organizationId}/datasets`);
49
+ return Array.isArray(data) ? data : data.datasets ?? [];
50
+ },
51
+ create: async (name, icon) => {
52
+ const { organizationId } = await this._me();
53
+ const data = await this._fetchMethod('POST', `/api/organization/${organizationId}/datasets`, {
54
+ name,
55
+ ...(icon ? { icon } : {}),
56
+ });
57
+ return data.dataset;
58
+ },
59
+ };
60
+ this.media = {
61
+ browse: async (collectionId, options = {}) => {
62
+ const params = new URLSearchParams();
63
+ if (options.path)
64
+ params.set('path', options.path);
65
+ if (options.limit != null)
66
+ params.set('limit', String(options.limit));
67
+ if (options.offset != null)
68
+ params.set('offset', String(options.offset));
69
+ if (options.type)
70
+ params.set('type', options.type);
71
+ if (options.dateFrom)
72
+ params.set('dateFrom', options.dateFrom);
73
+ if (options.dateTo)
74
+ params.set('dateTo', options.dateTo);
75
+ if (options.sortBy)
76
+ params.set('sortBy', options.sortBy);
77
+ if (options.sortDirection)
78
+ params.set('sortDirection', options.sortDirection);
79
+ if (options.tagFilters)
80
+ params.set('tagFilters', JSON.stringify(options.tagFilters));
81
+ const qs = params.toString();
82
+ return this._fetch(`/api/dataset/${collectionId}/media${qs ? `?${qs}` : ''}`);
83
+ },
84
+ search: async (query, options = {}) => {
85
+ const { organizationId } = await this._me();
86
+ const params = new URLSearchParams({ q: query, organizationId });
87
+ if (options.limit != null)
88
+ params.set('limit', String(options.limit));
89
+ const data = await this._fetch(`/api/search?${params.toString()}`);
90
+ return (data.results || []).map((r) => ({
91
+ mediaId: r.media?.id,
92
+ mediaName: r.media?.name,
93
+ mediaType: r.media?.type,
94
+ blobUrl: r.blob,
95
+ objects: (r.objects || []).map((o) => ({ id: o.id, type: o.type, label: o.label })),
96
+ rank: r.rank,
97
+ caption: r.captions?.[0]?.text,
98
+ }));
99
+ },
100
+ get: async (mediaId) => {
101
+ const data = await this._fetch(`/api/media/${mediaId}`);
102
+ return data.media;
103
+ },
104
+ rename: async (mediaId, name) => {
105
+ const data = await this._fetchMethod('PATCH', `/api/media/${mediaId}`, { name });
106
+ return data.media;
107
+ },
108
+ move: async (mediaId, destinationPath) => {
109
+ return this._fetchMethod('PATCH', `/api/media/${mediaId}/move`, { destinationPath });
110
+ },
111
+ addTag: async (mediaId, label, value) => {
112
+ await this._fetchMethod('POST', `/api/media/${mediaId}/tags`, { label, value });
113
+ },
114
+ removeTag: async (mediaId, label, value) => {
115
+ await this._fetchMethod('DELETE', `/api/media/${mediaId}/tags`, { label, value });
116
+ },
117
+ findSimilar: async (collectionId, objectId, options = {}) => {
118
+ const params = new URLSearchParams({ similarToObjectId: objectId });
119
+ if (options.limit != null)
120
+ params.set('limit', String(options.limit));
121
+ if (options.model)
122
+ params.set('similarToObjectModel', options.model);
123
+ return this._fetch(`/api/dataset/${collectionId}/media?${params.toString()}`);
124
+ },
125
+ upload: async (file, options) => {
126
+ const formData = new FormData();
127
+ formData.append('datasetId', options.collectionId);
128
+ if (options.path)
129
+ formData.append('path', options.path);
130
+ if (typeof file === 'string') {
131
+ // Node.js: treat as a file path — dynamic import fs to stay browser-compatible
132
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
133
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
134
+ const buffer = fs.readFileSync(file);
135
+ const ext = path.extname(file).slice(1).toLowerCase();
136
+ const mimeTypes = {
137
+ jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png',
138
+ gif: 'image/gif', webp: 'image/webp', heic: 'image/heic',
139
+ mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime',
140
+ };
141
+ const contentType = mimeTypes[ext] || 'application/octet-stream';
142
+ const fileName = options.name || path.basename(file);
143
+ const blob = new Blob([buffer], { type: contentType });
144
+ formData.append('file', blob, fileName);
145
+ if (!options.name)
146
+ formData.append('name', fileName);
147
+ }
148
+ else {
149
+ const fileName = options.name || (file instanceof File ? file.name : 'upload');
150
+ formData.append('file', file, fileName);
151
+ if (options.name)
152
+ formData.append('name', options.name);
153
+ }
154
+ // Build headers without Content-Type (let fetch set the multipart boundary)
155
+ const authHeaders = {};
156
+ if (this.token) {
157
+ authHeaders['Authorization'] = `Bearer ${this.token}`;
158
+ }
159
+ else {
160
+ authHeaders['x-api-key'] = this.apiKey || '';
161
+ }
162
+ const response = await fetch(`${this._baseUrl}/api/upload/multipart`, {
163
+ method: 'POST',
164
+ headers: authHeaders,
165
+ body: formData,
166
+ });
167
+ return this.handleResponse(response);
168
+ },
169
+ };
170
+ this.folders = {
171
+ create: async (collectionId, name, path) => {
172
+ const data = await this._fetchMethod('POST', '/api/folder', {
173
+ datasetId: collectionId,
174
+ name,
175
+ ...(path ? { path } : {}),
176
+ });
177
+ return data.folder;
178
+ },
179
+ };
180
+ this.tags = {
181
+ list: async (collectionId) => {
182
+ const data = await this._fetch(`/api/dataset/${collectionId}/tags`);
183
+ return data.tags;
184
+ },
185
+ };
42
186
  }
187
+ // ── Private helpers ──────────────────────────────────────────────────────
43
188
  getHeaders() {
44
189
  const headers = {
45
190
  'Content-Type': 'application/json',
@@ -52,6 +197,32 @@ class CoreViz {
52
197
  }
53
198
  return headers;
54
199
  }
200
+ /** Resolve the current user's org ID (cached after first call) */
201
+ async _me() {
202
+ const data = await this._fetch('/api/me');
203
+ if (!this._orgIdCache)
204
+ this._orgIdCache = data.organizationId;
205
+ return data;
206
+ }
207
+ /** GET request helper for management endpoints */
208
+ async _fetch(path) {
209
+ const response = await fetch(`${this._baseUrl}${path}`, {
210
+ method: 'GET',
211
+ headers: this.getHeaders(),
212
+ });
213
+ return this.handleResponse(response);
214
+ }
215
+ /** Non-GET request helper for management endpoints */
216
+ async _fetchMethod(method, path, body) {
217
+ const response = await fetch(`${this._baseUrl}${path}`, {
218
+ method,
219
+ headers: this.getHeaders(),
220
+ body: body !== undefined ? JSON.stringify(body) : undefined,
221
+ });
222
+ if (response.status === 204)
223
+ return undefined;
224
+ return this.handleResponse(response);
225
+ }
55
226
  async handleResponse(response) {
56
227
  if (response.status === 402) {
57
228
  throw new Error('Insufficient credits');
@@ -103,26 +274,25 @@ class CoreViz {
103
274
  throw err instanceof Error ? err : new Error("An unexpected error occurred.");
104
275
  }
105
276
  }
106
- async batchGenerate(prompt, options = {}) {
277
+ async generate(prompt, options = {}) {
107
278
  try {
108
279
  const headers = this.getHeaders();
109
280
  let resizedImages = [];
110
281
  if (options.referenceImages && options.referenceImages.length > 0) {
111
282
  resizedImages = await Promise.all(options.referenceImages.map(img => (0, resize_1.resize)(img, 1024, 1024)));
112
283
  }
113
- const response = await fetch(`https://lab.coreviz.io/api/ai/batch-generate`, {
284
+ const response = await fetch(`https://lab.coreviz.io/api/ai/generate`, {
114
285
  method: 'POST',
115
286
  headers,
116
287
  body: JSON.stringify({
117
288
  prompt,
118
289
  image: resizedImages.length > 0 ? resizedImages : undefined,
119
- count: options.count || 1,
120
290
  aspectRatio: options.aspectRatio,
121
291
  model: options.model || 'google/nano-banana-pro',
122
292
  }),
123
293
  });
124
294
  const data = await this.handleResponse(response);
125
- return data.results || [];
295
+ return data.result;
126
296
  }
127
297
  catch (err) {
128
298
  throw err instanceof Error ? err : new Error("An unexpected error occurred.");
@@ -1,6 +1,123 @@
1
1
  export interface CoreVizConfig {
2
2
  apiKey?: string;
3
3
  token?: string;
4
+ /** Override the API base URL (default: https://lab.coreviz.io) */
5
+ baseUrl?: string;
6
+ }
7
+ export interface UserContext {
8
+ userId: string;
9
+ email: string;
10
+ name: string;
11
+ organizationId: string;
12
+ organizationName: string | null;
13
+ }
14
+ export interface Collection {
15
+ id: string;
16
+ name: string;
17
+ icon?: string;
18
+ type: string;
19
+ organizationId: string;
20
+ }
21
+ export interface MediaObject {
22
+ id: string;
23
+ type: string;
24
+ label: string;
25
+ }
26
+ export interface MediaFrame {
27
+ id: string;
28
+ timestamp: number;
29
+ blob: string;
30
+ objects: MediaObject[];
31
+ }
32
+ export interface Media {
33
+ id: string;
34
+ name: string;
35
+ type: 'image' | 'video' | 'folder';
36
+ blob: string | null;
37
+ path: string;
38
+ width?: number;
39
+ height?: number;
40
+ sizeBytes?: number;
41
+ metadata?: Record<string, unknown>;
42
+ frames?: MediaFrame[];
43
+ createdAt?: string;
44
+ _score?: number;
45
+ }
46
+ export interface Folder {
47
+ id: string;
48
+ name: string;
49
+ path: string;
50
+ datasetId: string;
51
+ }
52
+ export interface BrowseOptions {
53
+ path?: string;
54
+ limit?: number;
55
+ offset?: number;
56
+ type?: 'image' | 'video' | 'folder' | 'all';
57
+ dateFrom?: string;
58
+ dateTo?: string;
59
+ sortBy?: string;
60
+ sortDirection?: 'asc' | 'desc';
61
+ tagFilters?: Record<string, string[]>;
62
+ }
63
+ export interface BrowseResult {
64
+ media: Media[];
65
+ pagination: {
66
+ total: number;
67
+ limit: number;
68
+ offset: number;
69
+ hasMore: boolean;
70
+ };
71
+ }
72
+ export interface SearchResult {
73
+ mediaId: string;
74
+ mediaName: string;
75
+ mediaType: string;
76
+ blobUrl: string;
77
+ objects: MediaObject[];
78
+ rank: number;
79
+ caption?: string;
80
+ }
81
+ export interface SearchOptions {
82
+ limit?: number;
83
+ }
84
+ export interface SimilarityOptions {
85
+ limit?: number;
86
+ model?: string;
87
+ }
88
+ export interface UploadOptions {
89
+ collectionId: string;
90
+ path?: string;
91
+ name?: string;
92
+ }
93
+ export interface UploadResult {
94
+ mediaId: string;
95
+ url: string;
96
+ message: string;
97
+ }
98
+ export interface CollectionsNamespace {
99
+ list(): Promise<Collection[]>;
100
+ create(name: string, icon?: string): Promise<Collection>;
101
+ }
102
+ export interface MediaNamespace {
103
+ browse(collectionId: string, options?: BrowseOptions): Promise<BrowseResult>;
104
+ search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
105
+ get(mediaId: string): Promise<Media>;
106
+ rename(mediaId: string, name: string): Promise<Media>;
107
+ move(mediaId: string, destinationPath: string): Promise<{
108
+ id: string;
109
+ newPath: string;
110
+ }>;
111
+ addTag(mediaId: string, label: string, value: string): Promise<void>;
112
+ removeTag(mediaId: string, label: string, value: string): Promise<void>;
113
+ findSimilar(collectionId: string, objectId: string, options?: SimilarityOptions): Promise<BrowseResult>;
114
+ upload(file: string | File | Blob, options: UploadOptions): Promise<UploadResult>;
115
+ }
116
+ export interface FoldersNamespace {
117
+ create(collectionId: string, name: string, path?: string): Promise<Folder>;
118
+ }
119
+ export interface TagsNamespace {
120
+ list(collectionId: string): Promise<Record<string, string[]>>;
4
121
  }
5
122
  export interface DescribeOptions {
6
123
  }
@@ -27,21 +144,29 @@ export interface EmbedOptions {
27
144
  export interface EmbedResponse {
28
145
  embedding: number[];
29
146
  }
30
- export interface BatchGenerateOptions {
147
+ export interface GenerateOptions {
31
148
  referenceImages?: string[];
32
- count?: number;
33
149
  aspectRatio?: '1:1' | '2:3' | '3:2' | '3:4' | '4:3' | '4:5' | '5:4' | '9:16' | '16:9' | '21:9';
34
- model?: 'google/nano-banana' | 'google/nano-banana-pro';
150
+ model?: 'google/nano-banana' | 'google/nano-banana-pro' | 'seedream-4' | 'flux-kontext-max';
35
151
  }
36
152
  export declare class CoreViz {
37
153
  private apiKey?;
38
154
  private token?;
155
+ private _baseUrl;
156
+ private _orgIdCache;
157
+ collections: CollectionsNamespace;
158
+ media: MediaNamespace;
159
+ folders: FoldersNamespace;
160
+ tags: TagsNamespace;
39
161
  constructor(config?: CoreVizConfig);
40
162
  private getHeaders;
163
+ private _me;
164
+ private _fetch;
165
+ private _fetchMethod;
41
166
  private handleResponse;
42
167
  describe(image: string, _options?: DescribeOptions): Promise<string>;
43
168
  edit(image: string, options: EditOptions): Promise<string>;
44
- batchGenerate(prompt: string, options?: BatchGenerateOptions): Promise<string[]>;
169
+ generate(prompt: string, options?: GenerateOptions): Promise<string>;
45
170
  tag(image: string, options: TagOptions): Promise<TagResponse>;
46
171
  embed(input: string, options?: EmbedOptions): Promise<EmbedResponse>;
47
172
  resize(input: string | File, maxWidth?: number, maxHeight?: number): Promise<string>;
@@ -4,9 +4,125 @@ exports.CoreViz = void 0;
4
4
  const resize_native_1 = require("./resize.native");
5
5
  class CoreViz {
6
6
  constructor(config = {}) {
7
+ this._orgIdCache = null;
7
8
  // React Native / Expo doesn't provide `process.env` in the same way; keep config explicit.
8
9
  this.apiKey = config.apiKey;
9
10
  this.token = config.token;
11
+ this._baseUrl = config.baseUrl || 'https://lab.coreviz.io';
12
+ this.collections = {
13
+ list: async () => {
14
+ const { organizationId } = await this._me();
15
+ const data = await this._fetch(`/api/organization/${organizationId}/datasets`);
16
+ return Array.isArray(data) ? data : data.datasets ?? [];
17
+ },
18
+ create: async (name, icon) => {
19
+ const { organizationId } = await this._me();
20
+ const data = await this._fetchMethod('POST', `/api/organization/${organizationId}/datasets`, { name, icon });
21
+ return data.dataset;
22
+ },
23
+ };
24
+ this.media = {
25
+ browse: async (collectionId, options = {}) => {
26
+ const params = new URLSearchParams();
27
+ if (options.path)
28
+ params.set('path', options.path);
29
+ if (options.limit != null)
30
+ params.set('limit', String(options.limit));
31
+ if (options.offset != null)
32
+ params.set('offset', String(options.offset));
33
+ if (options.type)
34
+ params.set('type', options.type);
35
+ if (options.dateFrom)
36
+ params.set('dateFrom', options.dateFrom);
37
+ if (options.dateTo)
38
+ params.set('dateTo', options.dateTo);
39
+ if (options.sortBy)
40
+ params.set('sortBy', options.sortBy);
41
+ if (options.sortDirection)
42
+ params.set('sortDirection', options.sortDirection);
43
+ if (options.tagFilters)
44
+ params.set('tagFilters', JSON.stringify(options.tagFilters));
45
+ const qs = params.toString();
46
+ return this._fetch(`/api/dataset/${collectionId}/media${qs ? `?${qs}` : ''}`);
47
+ },
48
+ search: async (query, options = {}) => {
49
+ const { organizationId } = await this._me();
50
+ const params = new URLSearchParams({ q: query, organizationId });
51
+ if (options.limit != null)
52
+ params.set('limit', String(options.limit));
53
+ const data = await this._fetch(`/api/search?${params.toString()}`);
54
+ return (data.results || []).map((r) => ({
55
+ mediaId: r.media?.id, mediaName: r.media?.name, mediaType: r.media?.type,
56
+ blobUrl: r.blob, objects: (r.objects || []).map((o) => ({ id: o.id, type: o.type, label: o.label })),
57
+ rank: r.rank, caption: r.captions?.[0]?.text,
58
+ }));
59
+ },
60
+ get: async (mediaId) => {
61
+ const data = await this._fetch(`/api/media/${mediaId}`);
62
+ return data.media;
63
+ },
64
+ rename: async (mediaId, name) => {
65
+ const data = await this._fetchMethod('PATCH', `/api/media/${mediaId}`, { name });
66
+ return data.media;
67
+ },
68
+ move: async (mediaId, destinationPath) => {
69
+ return this._fetchMethod('PATCH', `/api/media/${mediaId}/move`, { destinationPath });
70
+ },
71
+ addTag: async (mediaId, label, value) => {
72
+ await this._fetchMethod('POST', `/api/media/${mediaId}/tags`, { label, value });
73
+ },
74
+ removeTag: async (mediaId, label, value) => {
75
+ await this._fetchMethod('DELETE', `/api/media/${mediaId}/tags`, { label, value });
76
+ },
77
+ findSimilar: async (collectionId, objectId, options = {}) => {
78
+ const params = new URLSearchParams({ similarToObjectId: objectId });
79
+ if (options.limit != null)
80
+ params.set('limit', String(options.limit));
81
+ if (options.model)
82
+ params.set('similarToObjectModel', options.model);
83
+ return this._fetch(`/api/dataset/${collectionId}/media?${params.toString()}`);
84
+ },
85
+ upload: async (file, options) => {
86
+ if (typeof file === 'string') {
87
+ throw new Error('File path strings are not supported on React Native. Pass a File or Blob object instead.');
88
+ }
89
+ const formData = new FormData();
90
+ formData.append('datasetId', options.collectionId);
91
+ if (options.path)
92
+ formData.append('path', options.path);
93
+ const fileName = options.name || (file instanceof File ? file.name : 'upload');
94
+ formData.append('file', file, fileName);
95
+ if (options.name)
96
+ formData.append('name', options.name);
97
+ const authHeaders = {};
98
+ if (this.token) {
99
+ authHeaders['Authorization'] = `Bearer ${this.token}`;
100
+ }
101
+ else {
102
+ authHeaders['x-api-key'] = this.apiKey || '';
103
+ }
104
+ const response = await fetch(`${this._baseUrl}/api/upload/multipart`, {
105
+ method: 'POST',
106
+ headers: authHeaders,
107
+ body: formData,
108
+ });
109
+ return this.handleResponse(response);
110
+ },
111
+ };
112
+ this.folders = {
113
+ create: async (collectionId, name, path) => {
114
+ const data = await this._fetchMethod('POST', '/api/folder', {
115
+ datasetId: collectionId, name, ...(path ? { path } : {}),
116
+ });
117
+ return data.folder;
118
+ },
119
+ };
120
+ this.tags = {
121
+ list: async (collectionId) => {
122
+ const data = await this._fetch(`/api/dataset/${collectionId}/tags`);
123
+ return data.tags;
124
+ },
125
+ };
10
126
  }
11
127
  getHeaders() {
12
128
  const headers = {
@@ -20,6 +136,29 @@ class CoreViz {
20
136
  }
21
137
  return headers;
22
138
  }
139
+ async _me() {
140
+ const data = await this._fetch('/api/me');
141
+ if (!this._orgIdCache)
142
+ this._orgIdCache = data.organizationId;
143
+ return data;
144
+ }
145
+ async _fetch(path) {
146
+ const response = await fetch(`${this._baseUrl}${path}`, {
147
+ method: 'GET',
148
+ headers: this.getHeaders(),
149
+ });
150
+ return this.handleResponse(response);
151
+ }
152
+ async _fetchMethod(method, path, body) {
153
+ const response = await fetch(`${this._baseUrl}${path}`, {
154
+ method,
155
+ headers: this.getHeaders(),
156
+ body: body !== undefined ? JSON.stringify(body) : undefined,
157
+ });
158
+ if (response.status === 204)
159
+ return undefined;
160
+ return this.handleResponse(response);
161
+ }
23
162
  async handleResponse(response) {
24
163
  if (response.status === 402) {
25
164
  throw new Error('Insufficient credits');
@@ -61,25 +200,24 @@ class CoreViz {
61
200
  const data = await this.handleResponse(response);
62
201
  return data.result;
63
202
  }
64
- async batchGenerate(prompt, options = {}) {
203
+ async generate(prompt, options = {}) {
65
204
  const headers = this.getHeaders();
66
205
  let resizedImages = [];
67
206
  if (options.referenceImages && options.referenceImages.length > 0) {
68
207
  resizedImages = await Promise.all(options.referenceImages.map((img) => (0, resize_native_1.resize)(img, 1024, 1024)));
69
208
  }
70
- const response = await fetch(`https://lab.coreviz.io/api/ai/batch-generate`, {
209
+ const response = await fetch(`https://lab.coreviz.io/api/ai/generate`, {
71
210
  method: 'POST',
72
211
  headers,
73
212
  body: JSON.stringify({
74
213
  prompt,
75
214
  image: resizedImages.length > 0 ? resizedImages : undefined,
76
- count: options.count || 1,
77
215
  aspectRatio: options.aspectRatio,
78
216
  model: options.model || 'google/nano-banana-pro',
79
217
  }),
80
218
  });
81
219
  const data = await this.handleResponse(response);
82
- return data.results || [];
220
+ return data.result;
83
221
  }
84
222
  async tag(image, options) {
85
223
  const mode = options?.mode || 'api';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CoreViz, CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse } from './coreviz';
1
+ import { CoreViz, CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse, GenerateOptions, UserContext, Collection, Media, MediaObject, MediaFrame, Folder, BrowseOptions, BrowseResult, SearchResult, SearchOptions, SimilarityOptions, UploadOptions, UploadResult, CollectionsNamespace, MediaNamespace, FoldersNamespace, TagsNamespace } from './coreviz';
2
2
  import { resize } from './resize';
3
3
  export { CoreViz, resize };
4
- export type { CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse };
4
+ export type { CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse, GenerateOptions, UserContext, Collection, Media, MediaObject, MediaFrame, Folder, BrowseOptions, BrowseResult, SearchResult, SearchOptions, SimilarityOptions, UploadOptions, UploadResult, CollectionsNamespace, MediaNamespace, FoldersNamespace, TagsNamespace };
@@ -1,4 +1,4 @@
1
- import { CoreViz, CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse } from './coreviz.native';
1
+ import { CoreViz, CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse, GenerateOptions, UserContext, Collection, Media, MediaObject, MediaFrame, Folder, BrowseOptions, BrowseResult, SearchResult, SearchOptions, SimilarityOptions, UploadOptions, UploadResult, CollectionsNamespace, MediaNamespace, FoldersNamespace, TagsNamespace } from './coreviz.native';
2
2
  import { resize } from './resize.native';
3
3
  export { CoreViz, resize };
4
- export type { CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse };
4
+ export type { CoreVizConfig, DescribeOptions, EditOptions, TagOptions, TagResponse, EmbedOptions, EmbedResponse, GenerateOptions, UserContext, Collection, Media, MediaObject, MediaFrame, Folder, BrowseOptions, BrowseResult, SearchResult, SearchOptions, SimilarityOptions, UploadOptions, UploadResult, CollectionsNamespace, MediaNamespace, FoldersNamespace, TagsNamespace };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreviz/sdk",
3
- "version": "1.0.17",
3
+ "version": "1.1.0",
4
4
  "description": "CoreViz SDK",
5
5
  "main": "dist/index.js",
6
6
  "react-native": "dist/index.native.js",