@coreviz/sdk 1.1.0 → 1.1.2

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
@@ -64,162 +64,149 @@ Notes:
64
64
 
65
65
  ## Configuration
66
66
 
67
- To use the AI features, you need to instantiate the `CoreViz` class with your API key.
68
-
69
67
  ```typescript
70
68
  import { CoreViz } from '@coreviz/sdk';
71
69
 
72
- const coreviz = new CoreViz({
73
- apiKey: process.env.COREVIZ_API_KEY // or 'your_api_key_here'
74
- });
70
+ // External use API key
71
+ const coreviz = new CoreViz({ apiKey: process.env.COREVIZ_API_KEY });
72
+
73
+ // CLI / server — session token
74
+ const coreviz = new CoreViz({ token: 'your_session_token' });
75
+
76
+ // Self-hosted or local dev — override base URL
77
+ const coreviz = new CoreViz({ apiKey: '...', baseUrl: 'http://localhost:3000' });
78
+
79
+ // Browser same-origin — no credentials needed; session cookie is used automatically
80
+ const coreviz = new CoreViz({ baseUrl: window.location.origin });
75
81
  ```
76
82
 
77
- ## API Reference
83
+ ---
84
+
85
+ ## Vision AI Methods
78
86
 
79
87
  ### `coreviz.describe(image)`
80
88
 
81
89
  Generates a detailed text description of an image.
82
90
 
83
91
  **Parameters:**
84
- - `image` (string): The image to describe. Can be a base64 string or a URL.
85
-
86
- **Returns:**
87
- - `Promise<string>`: A text description of the image.
92
+ - `image` (string): Base64 data URL or remote URL.
88
93
 
89
- **Example:**
94
+ **Returns:** `Promise<string>`
90
95
 
91
96
  ```typescript
92
97
  const description = await coreviz.describe('https://example.com/image.jpg');
93
- console.log(description);
94
98
  ```
95
99
 
100
+ ---
101
+
96
102
  ### `coreviz.tag(image, options)`
97
103
 
98
- Analyzes an image and returns relevant tags or classifications based on a prompt.
104
+ Analyzes an image and returns tags or classifications based on a prompt.
99
105
 
100
106
  **Parameters:**
101
- - `image` (string): The image to analyze. Can be a base64 string or a URL.
102
- - `options` (object):
103
- - `prompt` (string): The context or question to guide the tagging (e.g., "What objects are in this image?").
104
- - `options` (string[], optional): A specific list of tags to choose from.
105
- - `multiple` (boolean, optional): Whether to allow multiple tags (default: `true`).
106
-
107
- **Returns:**
108
- - `Promise<TagResponse>`: An object containing:
109
- - `tags` (string[]): The list of identified tags.
110
- - `raw` (unknown): The raw API response.
107
+ - `image` (string): Base64 data URL or remote URL.
108
+ - `options`:
109
+ - `prompt` (string): Question to guide tagging (e.g. `"What color is the car?"`).
110
+ - `options` (string[], optional): Restrict answers to this list.
111
+ - `multiple` (boolean, optional): Allow multiple tags (default: `true`).
112
+ - `mode` (`'api' | 'local'`, optional): `'local'` runs fully in-browser via transformers.js.
111
113
 
112
- **Example:**
114
+ **Returns:** `Promise<TagResponse>` — `{ tags: string[], raw }`
113
115
 
114
116
  ```typescript
115
- const result = await coreviz.tag('base64_image_string...', {
116
- prompt: "Is this indoor or outdoor?",
117
- options: ["indoor", "outdoor"],
118
- multiple: false
117
+ const { tags } = await coreviz.tag(imageUrl, {
118
+ prompt: 'Is this indoor or outdoor?',
119
+ options: ['indoor', 'outdoor'],
120
+ multiple: false,
119
121
  });
120
- console.log(result.tags); // ["indoor"]
121
122
  ```
122
123
 
124
+ ---
125
+
123
126
  ### `coreviz.edit(image, options)`
124
127
 
125
- Modifies an image based on a text prompt using generative AI.
128
+ Edits an image using a generative AI prompt.
126
129
 
127
130
  **Parameters:**
128
- - `image` (string): The image to edit. Can be a base64 string or a URL.
129
- - `options` (object):
131
+ - `image` (string): Base64 data URL or remote URL.
132
+ - `options`:
130
133
  - `prompt` (string): Description of the desired edit.
131
- - `aspectRatio` (string, optional): Target aspect ratio (`'match_input_image'`, `'1:1'`, `'16:9'`, `'9:16'`, `'4:3'`, `'3:4'`).
132
- - `outputFormat` (string, optional): `'jpg'` or `'png'`.
133
- - `model` (string, optional): The model to use (default: `'flux-kontext-max'`).
134
-
135
- **Returns:**
136
- - `Promise<string>`: The edited image as a base64 string or URL.
134
+ - `aspectRatio` (string, optional): `'match_input_image'` | `'1:1'` | `'16:9'` | `'9:16'` | `'4:3'` | `'3:4'`.
135
+ - `outputFormat` (`'jpg' | 'png'`, optional).
136
+ - `model` (string, optional): `'flux-kontext-max'` | `'google/nano-banana'` | `'seedream-4'`. Default: `'flux-kontext-max'`.
137
137
 
138
- **Example:**
138
+ **Returns:** `Promise<string>` — edited image URL or base64.
139
139
 
140
140
  ```typescript
141
- const editedImage = await coreviz.edit('https://example.com/photo.jpg', {
142
- prompt: "Make it look like a painting",
143
- aspectRatio: "1:1"
141
+ const edited = await coreviz.edit('https://example.com/photo.jpg', {
142
+ prompt: 'Make it look like a watercolor painting',
143
+ aspectRatio: '1:1',
144
144
  });
145
145
  ```
146
146
 
147
- ### `coreviz.generate(prompt, options)`
147
+ ---
148
148
 
149
- Generates an image based on a text prompt, optionally using reference images for style/structure guidance.
149
+ ### `coreviz.generate(prompt, options?)`
150
150
 
151
- **Parameters:**
152
- - `prompt` (string): The text description of the image(s) to generate.
153
- - `options` (object, optional):
154
- - `referenceImages` (string[], optional): Array of reference images (URL/base64) to guide generation.
155
- - `aspectRatio` (string, optional): Target aspect ratio (e.g., `'1:1'`, `'16:9'`, `'4:3'`).
156
- - `model` (string, optional): The model to use (default: `'google/nano-banana-pro'`).
151
+ Generates an image from a text prompt, optionally guided by reference images.
157
152
 
158
- **Returns:**
159
- - `string`: The generated images as a URL.
153
+ **Parameters:**
154
+ - `prompt` (string): Text description.
155
+ - `options` (optional):
156
+ - `referenceImages` (string[]): Reference images (URL/base64) to guide style or structure.
157
+ - `aspectRatio` (string): e.g. `'1:1'`, `'16:9'`, `'4:3'`.
158
+ - `model` (string): `'google/nano-banana'` | `'google/nano-banana-pro'` | `'seedream-4'` | `'flux-kontext-max'`. Default: `'google/nano-banana-pro'`.
160
159
 
161
- **Example:**
160
+ **Returns:** `Promise<string>` — generated image URL.
162
161
 
163
162
  ```typescript
164
- const images = await coreviz.generate("A futuristic city skyline", {
165
- aspectRatio: "16:9"
166
- });
163
+ const image = await coreviz.generate('A futuristic city at dusk', { aspectRatio: '16:9' });
167
164
  ```
168
165
 
166
+ ---
167
+
169
168
  ### `coreviz.embed(input, options?)`
170
169
 
171
170
  Generates embeddings for image or text inputs, enabling semantic search and similarity comparison. Use with `coreviz.similarity(embeddingA, embeddingB)` to compare two images or an image and a text.
172
171
 
173
172
  **Parameters:**
174
- - `input` (string): The text string or image (URL/base64) to embed.
175
- - `options` (object, optional):
176
- - `type` ('image' | 'text', optional): Explicitly define the input type.
177
- - `mode` ('api' | 'local', optional): Execution mode (default: `'api'`). `'local'` runs in-browser/node using transformers.js.
178
-
179
- **Returns:**
180
- - `Promise<EmbedResponse>`: An object containing:
181
- - `embedding` (number[]): The high-dimensional vector representation.
173
+ - `input` (string): Text string, image URL, or base64 data URL.
174
+ - `options` (optional):
175
+ - `type` (`'image' | 'text'`): Explicit type hint (auto-detected if omitted).
176
+ - `mode` (`'api' | 'local'`): `'local'` runs in-browser using transformers.js. Default: `'api'`.
182
177
 
183
- **Example:**
178
+ **Returns:** `Promise<EmbedResponse>` — `{ embedding: number[] }`
184
179
 
185
180
  ```typescript
186
- const { embedding } = await coreviz.embed('A photo of a sunset');
181
+ const { embedding } = await coreviz.embed('A photo of a red sneaker');
187
182
  ```
188
183
 
189
- ### `coreviz.similarity(embeddingA, embeddingB)`
184
+ ---
185
+
186
+ ### `coreviz.similarity(vecA, vecB)`
190
187
 
191
188
  Calculates the degree of similarity between two embeddings.
192
189
 
193
190
  **Parameters:**
194
- - `embeddingA` (number[]): The first image/text embedding.
195
- - `embeddingB` (number[]): The second image/text embedding.
196
-
197
- **Returns:**
198
- - `number`: A similarity score between -1 and 1.
191
+ - `vecA`, `vecB` (number[]): Embedding vectors from `embed()`.
199
192
 
200
- **Example:**
193
+ **Returns:** `number` — score between -1 and 1.
201
194
 
202
195
  ```typescript
203
- const similarity = coreviz.similarity(embeddingA, embeddingB);
196
+ const score = coreviz.similarity(embeddingA, embeddingB);
204
197
  ```
205
198
 
206
- ### `coreviz.resize(input, maxWidth?, maxHeight?)`
207
-
208
- Utility function to resize images client-side or server-side before processing. Also available as a standalone import.
199
+ ---
209
200
 
210
- **Parameters:**
211
- - `input` (string | File): The image to resize.
212
- - `maxWidth` (number, optional): Maximum width (default: 1920).
213
- - `maxHeight` (number, optional): Maximum height (default: 1080).
201
+ ### `coreviz.resize(input, maxWidth?, maxHeight?)`
214
202
 
215
- **Returns:**
216
- - `Promise<string>`: The resized image as a base64 string.
203
+ Resizes an image client-side (canvas) or server-side (Sharp). Also available as a standalone import.
217
204
 
218
- **Example:**
205
+ **Returns:** `Promise<string>` — base64 data URL.
219
206
 
220
207
  ```typescript
221
- const resized = await coreviz.resize(myFileObject, 800, 600);
222
- // or import { resize } from '@coreviz/sdk';
208
+ const resized = await coreviz.resize(file, 800, 600);
209
+ // or: import { resize } from '@coreviz/sdk';
223
210
  ```
224
211
 
225
212
  ---
@@ -228,32 +215,39 @@ const resized = await coreviz.resize(myFileObject, 800, 600);
228
215
 
229
216
  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
217
 
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
- ```
218
+ ---
219
+
220
+ ## Collections
221
+
222
+ ### `coreviz.collections.list(organizationId?)`
236
223
 
237
- ### `coreviz.collections.list()`
224
+ List all collections in an organization.
238
225
 
239
- List all collections in the user's current organization.
226
+ - `organizationId` (string, optional): Pass explicitly to skip the `/api/me` round-trip.
240
227
 
241
228
  **Returns:** `Promise<Collection[]>`
242
229
 
243
230
  ```typescript
244
231
  const collections = await coreviz.collections.list();
245
- // [{ id, name, icon, type, organizationId }, ...]
246
232
  ```
247
233
 
248
234
  ---
249
235
 
250
- ### `coreviz.collections.create(name, icon?)`
236
+ ### `coreviz.collections.get(collectionId)`
251
237
 
252
- Create a new collection in the user's current organization.
238
+ Get a single collection by ID.
253
239
 
254
- **Parameters:**
255
- - `name` (string): Collection name.
256
- - `icon` (string, optional): Emoji or icon name.
240
+ **Returns:** `Promise<Collection>`
241
+
242
+ ```typescript
243
+ const collection = await coreviz.collections.get('abc123');
244
+ ```
245
+
246
+ ---
247
+
248
+ ### `coreviz.collections.create(name, icon?)`
249
+
250
+ Create a new collection.
257
251
 
258
252
  **Returns:** `Promise<Collection>`
259
253
 
@@ -263,47 +257,78 @@ const collection = await coreviz.collections.create('Product Photos', '📦');
263
257
 
264
258
  ---
265
259
 
266
- ### `coreviz.media.browse(collectionId, options?)`
260
+ ### `coreviz.collections.update(collectionId, updates)`
267
261
 
268
- List media items and folders inside a collection. Navigates the ltree folder hierarchy.
262
+ Update a collection's name or icon.
269
263
 
270
264
  **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.
265
+ - `updates`: `{ name?: string; icon?: string }`
266
+
267
+ **Returns:** `Promise<Collection>`
268
+
269
+ ```typescript
270
+ await coreviz.collections.update('abc123', { name: 'Campaign Assets 2025' });
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Media
276
+
277
+ ### `coreviz.media.browse(collectionId, options?)`
278
+
279
+ List media and folders inside a collection. Supports browsing, filtering, searching, and similarity queries all through the same method.
280
+
281
+ **Options:**
282
+ | Field | Type | Description |
283
+ |---|---|---|
284
+ | `path` | string | ltree path to list (e.g. `"collId.folderId"`). Defaults to collection root. |
285
+ | `limit` / `offset` | number | Pagination. |
286
+ | `type` | `'image' \| 'video' \| 'folder' \| 'all'` | Filter by media type. |
287
+ | `dateFrom` / `dateTo` | string | Date range filter (`YYYY-MM-DD`). |
288
+ | `sortBy` / `sortDirection` | string | Sort field and direction (`'asc' \| 'desc'`). |
289
+ | `tagFilters` | `Record<string, string[]>` | AND between groups, OR within group. |
290
+ | `q` | string | Text/semantic search query (triggers scored mode). |
291
+ | `similarToObjectId` | string | Find visually similar media by detected object ID. |
292
+ | `similarToObjectModel` | string | Vision model for similarity scoring. |
293
+ | `tags` | string | Comma-separated tag label filter. |
294
+ | `mediaId` | string | Filter to a specific media item. |
295
+ | `clusterId` | string | Filter to a specific object cluster. |
296
+ | `recursive` | boolean | List all descendants recursively (flattened view). |
279
297
 
280
298
  **Returns:** `Promise<BrowseResult>` — `{ media: Media[], pagination }`
281
299
 
282
300
  ```typescript
283
- const { media } = await coreviz.media.browse('abc123', { path: 'abc123.folderXyz', limit: 50 });
301
+ // Browse a folder
302
+ const { media } = await coreviz.media.browse('collId', { path: 'collId.folderXyz', limit: 50 });
303
+
304
+ // In-folder semantic search
305
+ const { media: results } = await coreviz.media.browse('collId', { q: 'red shoes' });
306
+
307
+ // Find similar media by object
308
+ const { media: similar } = await coreviz.media.browse('collId', { similarToObjectId: 'objId' });
284
309
  ```
285
310
 
286
311
  ---
287
312
 
288
313
  ### `coreviz.media.search(query, options?)`
289
314
 
290
- Semantically search across all media in the organization using natural language.
315
+ Semantically search across all media in the organization.
291
316
 
292
- **Parameters:**
293
- - `query` (string): Natural language search query.
294
- - `options.limit` (number, optional): Max results (default 20).
317
+ **Options:**
318
+ - `limit` (number): Max results.
319
+ - `organizationId` (string): Pass explicitly to skip the `/api/me` round-trip.
295
320
 
296
321
  **Returns:** `Promise<SearchResult[]>` — each result includes `mediaId`, `blobUrl`, `objects`, `rank`, `caption`.
297
322
 
298
323
  ```typescript
299
- const results = await coreviz.media.search('red shoes on a white background', { limit: 10 });
324
+ const results = await coreviz.media.search('sunset over water', { limit: 10 });
300
325
  ```
301
326
 
302
327
  ---
303
328
 
304
329
  ### `coreviz.media.get(mediaId)`
305
330
 
306
- Get full details for a media item: blob URL, dimensions, tags, detected objects, and version info.
331
+ Get full details for a media item including blob URL, dimensions, metadata, detected objects, and frames.
307
332
 
308
333
  **Returns:** `Promise<Media>`
309
334
 
@@ -328,100 +353,218 @@ await coreviz.media.rename('mediaId123', 'hero-shot-final.jpg');
328
353
 
329
354
  ### `coreviz.media.move(mediaId, destinationPath)`
330
355
 
331
- Move a media item or folder to a different location within the same collection.
356
+ Move a media item to a different folder within the same collection.
332
357
 
333
- **Parameters:**
334
- - `destinationPath` (string): ltree path of the destination folder (e.g. `"collectionId.targetFolder"`).
358
+ - `destinationPath` (string): ltree path of the destination folder.
335
359
 
336
360
  **Returns:** `Promise<{ id, newPath }>`
337
361
 
338
362
  ```typescript
339
- await coreviz.media.move('mediaId123', 'collectionId.archiveFolder');
363
+ await coreviz.media.move('mediaId123', 'collId.archiveFolder');
364
+ ```
365
+
366
+ ---
367
+
368
+ ### `coreviz.media.delete(mediaId)`
369
+
370
+ Permanently delete a media item.
371
+
372
+ ```typescript
373
+ await coreviz.media.delete('mediaId123');
374
+ ```
375
+
376
+ ---
377
+
378
+ ### `coreviz.media.upload(file, options)`
379
+
380
+ Upload a photo or video.
381
+
382
+ **Parameters:**
383
+ - `file`: Local file path string (Node.js), `File` (browser), or `Blob`.
384
+ - `options`:
385
+ - `collectionId` (string, required)
386
+ - `path` (string, optional): Destination ltree folder path.
387
+ - `name` (string, optional): Override stored file name.
388
+
389
+ **Returns:** `Promise<UploadResult>` — `{ mediaId, url, message }`
390
+
391
+ ```typescript
392
+ // Node.js
393
+ const result = await coreviz.media.upload('/path/to/photo.jpg', { collectionId: 'abc123' });
394
+
395
+ // Browser
396
+ const result = await coreviz.media.upload(file, { collectionId: 'abc123', path: 'abc123.folder' });
340
397
  ```
341
398
 
399
+ > File path strings are not supported on React Native / Expo. Pass a `File` or `Blob` instead.
400
+
342
401
  ---
343
402
 
344
- ### `coreviz.media.addTag(mediaId, label, value)` / `removeTag(...)`
403
+ ## Tags
404
+
405
+ ### `coreviz.media.addTag(mediaId, label, value)`
345
406
 
346
- Add or remove a tag from a media item. Tags are `label` (group) + `value` pairs.
407
+ Add a tag to a media item. Tags are `label` (group) + `value` pairs.
347
408
 
348
409
  ```typescript
349
410
  await coreviz.media.addTag('mediaId123', 'color', 'red');
411
+ ```
412
+
413
+ ---
414
+
415
+ ### `coreviz.media.removeTag(mediaId, label, value)`
416
+
417
+ Remove a specific tag value from a media item.
418
+
419
+ ```typescript
350
420
  await coreviz.media.removeTag('mediaId123', 'color', 'red');
351
421
  ```
352
422
 
353
423
  ---
354
424
 
425
+ ### `coreviz.media.removeTagGroup(mediaId, label)`
426
+
427
+ Remove an entire tag group (all values under that label) from a media item.
428
+
429
+ ```typescript
430
+ await coreviz.media.removeTagGroup('mediaId123', 'color');
431
+ ```
432
+
433
+ ---
434
+
435
+ ### `coreviz.media.renameTagGroup(mediaId, oldLabel, newLabel)`
436
+
437
+ Rename a tag group on a media item, preserving all its values.
438
+
439
+ ```typescript
440
+ await coreviz.media.renameTagGroup('mediaId123', 'colour', 'color');
441
+ ```
442
+
443
+ ---
444
+
445
+ ### `coreviz.tags.list(collectionId)`
446
+
447
+ Aggregate all tag groups and values across an entire collection.
448
+
449
+ **Returns:** `Promise<Record<string, string[]>>`
450
+
451
+ ```typescript
452
+ const tags = await coreviz.tags.list('collId');
453
+ // { color: ['red', 'blue'], category: ['product', 'lifestyle'] }
454
+ ```
455
+
456
+ ---
457
+
458
+ ## Versions
459
+
460
+ Media items in CoreViz track edit history as versions. Each AI edit or bulk operation creates a new version linked to the original.
461
+
462
+ ### `coreviz.media.listVersions(mediaId)`
463
+
464
+ List all versions of a media item (original + all AI-edited derivatives).
465
+
466
+ **Returns:** `Promise<Media[]>`
467
+
468
+ ```typescript
469
+ const versions = await coreviz.media.listVersions('mediaId123');
470
+ ```
471
+
472
+ ---
473
+
474
+ ### `coreviz.media.selectVersion(versionId)`
475
+
476
+ Mark a version as the active/current version.
477
+
478
+ ```typescript
479
+ await coreviz.media.selectVersion('versionId456');
480
+ ```
481
+
482
+ ---
483
+
484
+ ### `coreviz.media.deleteVersion(rootMediaId, versionId)`
485
+
486
+ Delete a specific version. If the deleted version was active, the server promotes another version automatically.
487
+
488
+ **Returns:** `Promise<{ deletedId: string; promotedId: string | null }>`
489
+
490
+ ```typescript
491
+ const { promotedId } = await coreviz.media.deleteVersion('rootMediaId', 'versionId456');
492
+ if (promotedId) {
493
+ // navigate to promoted version
494
+ }
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Similarity Search
500
+
355
501
  ### `coreviz.media.findSimilar(collectionId, objectId, options?)`
356
502
 
357
503
  Find visually similar media using a detected object ID (from `media.get()` frames).
358
504
 
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'`.
505
+ **Options:**
506
+ - `limit` (number)
507
+ - `model` (string): e.g. `'faces'`, `'objects'`, `'shoeprints'`.
363
508
 
364
509
  **Returns:** `Promise<BrowseResult>`
365
510
 
366
511
  ```typescript
367
- const similar = await coreviz.media.findSimilar('collectionId', 'objectId456', { model: 'faces' });
512
+ const { media } = await coreviz.media.findSimilar('collId', 'objectId456', { model: 'faces' });
368
513
  ```
369
514
 
370
515
  ---
371
516
 
372
- ### `coreviz.folders.create(collectionId, name, path?)`
517
+ ## Folders
518
+
519
+ ### `coreviz.folders.create(collectionId, name, path?, reuse?)`
373
520
 
374
521
  Create a new folder inside a collection.
375
522
 
523
+ - `path` (string, optional): Parent ltree path. Defaults to collection root.
524
+ - `reuse` (boolean, optional): When `true`, returns the existing folder if one with the same name already exists at that path (upsert behavior).
525
+
376
526
  **Returns:** `Promise<Folder>`
377
527
 
378
528
  ```typescript
379
- const folder = await coreviz.folders.create('collectionId', 'Spring 2025', 'collectionId.campaigns');
529
+ const folder = await coreviz.folders.create('collId', 'Spring 2025', 'collId.campaigns');
530
+
531
+ // Upsert — safe to call repeatedly
532
+ const folder = await coreviz.folders.create('collId', 'Imports', undefined, true);
380
533
  ```
381
534
 
382
535
  ---
383
536
 
384
- ### `coreviz.tags.list(collectionId)`
537
+ ### `coreviz.folders.get(folderId)`
385
538
 
386
- Aggregate all tag groups and values across an entire collection.
539
+ Get a folder by ID.
387
540
 
388
- **Returns:** `Promise<Record<string, string[]>>`
541
+ **Returns:** `Promise<Folder>`
389
542
 
390
543
  ```typescript
391
- const tags = await coreviz.tags.list('collectionId');
392
- // { color: ['red', 'blue'], category: ['product', 'lifestyle'] }
544
+ const folder = await coreviz.folders.get('folderId123');
393
545
  ```
394
546
 
395
547
  ---
396
548
 
397
- ### `coreviz.media.upload(file, options)`
549
+ ### `coreviz.folders.update(folderId, updates)`
398
550
 
399
- Upload a photo or video to CoreViz.
551
+ Update a folder's name or metadata.
400
552
 
401
553
  **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 }`
554
+ - `updates`: `{ name?: string; metadata?: Record<string, unknown> }`
409
555
 
410
- **Supported formats:** JPEG, PNG, GIF, WebP, HEIC, MP4, WebM, MOV, AVI
556
+ **Returns:** `Promise<Folder>`
411
557
 
412
558
  ```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
- });
559
+ await coreviz.folders.update('folderId123', { name: 'Archived Campaign' });
425
560
  ```
426
561
 
427
- > **Note:** File path strings are not supported on React Native / Expo. Pass a `File` or `Blob` object instead.
562
+ ---
563
+
564
+ ### `coreviz.folders.delete(folderId)`
565
+
566
+ Delete a folder and all its contents.
567
+
568
+ ```typescript
569
+ await coreviz.folders.delete('folderId123');
570
+ ```
package/dist/coreviz.d.ts CHANGED
@@ -59,6 +59,20 @@ export interface BrowseOptions {
59
59
  sortBy?: string;
60
60
  sortDirection?: 'asc' | 'desc';
61
61
  tagFilters?: Record<string, string[]>;
62
+ /** Text / semantic search query (triggers scored mode on the server) */
63
+ q?: string;
64
+ /** Object ID to find visually similar media within this collection */
65
+ similarToObjectId?: string;
66
+ /** Vision model used for similarity scoring */
67
+ similarToObjectModel?: string;
68
+ /** Comma-separated tag label filter */
69
+ tags?: string;
70
+ /** Filter to a specific media item ID */
71
+ mediaId?: string;
72
+ /** Filter to a specific cluster ID */
73
+ clusterId?: string;
74
+ /** When true, list all descendants recursively (flattened view) */
75
+ recursive?: boolean;
62
76
  }
63
77
  export interface BrowseResult {
64
78
  media: Media[];
@@ -80,6 +94,20 @@ export interface SearchResult {
80
94
  }
81
95
  export interface SearchOptions {
82
96
  limit?: number;
97
+ /** Pass the organization ID directly to skip the /api/me round-trip. */
98
+ organizationId?: string;
99
+ }
100
+ export interface FolderUpdateOptions {
101
+ name?: string;
102
+ metadata?: Record<string, unknown>;
103
+ }
104
+ export interface CollectionUpdateOptions {
105
+ name?: string;
106
+ icon?: string;
107
+ }
108
+ export interface DeleteVersionResult {
109
+ deletedId: string;
110
+ promotedId: string | null;
83
111
  }
84
112
  export interface SimilarityOptions {
85
113
  limit?: number;
@@ -99,10 +127,17 @@ export interface UploadResult {
99
127
  message: string;
100
128
  }
101
129
  export interface CollectionsNamespace {
102
- /** List all collections in the user's current organization */
103
- list(): Promise<Collection[]>;
130
+ /**
131
+ * List all collections in an organization.
132
+ * @param organizationId - If omitted the SDK resolves it via /api/me (one extra round-trip).
133
+ */
134
+ list(organizationId?: string): Promise<Collection[]>;
135
+ /** Get a single collection by ID */
136
+ get(collectionId: string): Promise<Collection>;
104
137
  /** Create a new collection in the user's current organization */
105
138
  create(name: string, icon?: string): Promise<Collection>;
139
+ /** Update a collection's name or icon */
140
+ update(collectionId: string, updates: CollectionUpdateOptions): Promise<Collection>;
106
141
  }
107
142
  export interface MediaNamespace {
108
143
  /** Browse/list media items in a collection folder */
@@ -118,10 +153,22 @@ export interface MediaNamespace {
118
153
  id: string;
119
154
  newPath: string;
120
155
  }>;
156
+ /** Permanently delete a media item */
157
+ delete(mediaId: string): Promise<void>;
121
158
  /** Add a tag group+value to a media item */
122
159
  addTag(mediaId: string, label: string, value: string): Promise<void>;
123
- /** Remove a tag group+value from a media item */
160
+ /** Remove a specific tag value from a media item */
124
161
  removeTag(mediaId: string, label: string, value: string): Promise<void>;
162
+ /** Remove an entire tag group (all values) from a media item */
163
+ removeTagGroup(mediaId: string, label: string): Promise<void>;
164
+ /** Rename a tag group across a media item */
165
+ renameTagGroup(mediaId: string, oldLabel: string, newLabel: string): Promise<void>;
166
+ /** List all versions of a media item */
167
+ listVersions(mediaId: string): Promise<Media[]>;
168
+ /** Delete a specific version; returns the promoted active version ID if applicable */
169
+ deleteVersion(rootMediaId: string, versionId: string): Promise<DeleteVersionResult>;
170
+ /** Mark a version as the active/current version */
171
+ selectVersion(versionId: string): Promise<void>;
125
172
  /** Find visually similar media using an object ID */
126
173
  findSimilar(collectionId: string, objectId: string, options?: SimilarityOptions): Promise<BrowseResult>;
127
174
  /**
@@ -132,8 +179,17 @@ export interface MediaNamespace {
132
179
  upload(file: string | File | Blob, options: UploadOptions): Promise<UploadResult>;
133
180
  }
134
181
  export interface FoldersNamespace {
135
- /** Create a folder inside a collection */
136
- create(collectionId: string, name: string, path?: string): Promise<Folder>;
182
+ /**
183
+ * Create a folder inside a collection.
184
+ * @param reuse - When true, return the existing folder if one with the same name already exists at that path (upsert).
185
+ */
186
+ create(collectionId: string, name: string, path?: string, reuse?: boolean): Promise<Folder>;
187
+ /** Get a folder by ID */
188
+ get(folderId: string): Promise<Folder>;
189
+ /** Update a folder's name or metadata */
190
+ update(folderId: string, updates: FolderUpdateOptions): Promise<Folder>;
191
+ /** Delete a folder and all its contents */
192
+ delete(folderId: string): Promise<void>;
137
193
  }
138
194
  export interface TagsNamespace {
139
195
  /** Aggregate all tag groups + values across a collection */
package/dist/coreviz.js CHANGED
@@ -43,11 +43,15 @@ class CoreViz {
43
43
  this._baseUrl = config.baseUrl || 'https://lab.coreviz.io';
44
44
  // ── Management namespaces ────────────────────────────────────────────
45
45
  this.collections = {
46
- list: async () => {
47
- const { organizationId } = await this._me();
48
- const data = await this._fetch(`/api/organization/${organizationId}/datasets`);
46
+ list: async (organizationId) => {
47
+ const orgId = organizationId || (await this._me()).organizationId;
48
+ const data = await this._fetch(`/api/organization/${orgId}/datasets`);
49
49
  return Array.isArray(data) ? data : data.datasets ?? [];
50
50
  },
51
+ get: async (collectionId) => {
52
+ const data = await this._fetch(`/api/dataset/${collectionId}`);
53
+ return data.dataset;
54
+ },
51
55
  create: async (name, icon) => {
52
56
  const { organizationId } = await this._me();
53
57
  const data = await this._fetchMethod('POST', `/api/organization/${organizationId}/datasets`, {
@@ -56,6 +60,10 @@ class CoreViz {
56
60
  });
57
61
  return data.dataset;
58
62
  },
63
+ update: async (collectionId, updates) => {
64
+ const data = await this._fetchMethod('PATCH', `/api/dataset/${collectionId}`, updates);
65
+ return data.dataset;
66
+ },
59
67
  };
60
68
  this.media = {
61
69
  browse: async (collectionId, options = {}) => {
@@ -78,12 +86,26 @@ class CoreViz {
78
86
  params.set('sortDirection', options.sortDirection);
79
87
  if (options.tagFilters)
80
88
  params.set('tagFilters', JSON.stringify(options.tagFilters));
89
+ if (options.q)
90
+ params.set('q', options.q);
91
+ if (options.similarToObjectId)
92
+ params.set('similarToObjectId', options.similarToObjectId);
93
+ if (options.similarToObjectModel)
94
+ params.set('similarToObjectModel', options.similarToObjectModel);
95
+ if (options.tags)
96
+ params.set('tags', options.tags);
97
+ if (options.mediaId)
98
+ params.set('mediaId', options.mediaId);
99
+ if (options.clusterId)
100
+ params.set('clusterId', options.clusterId);
101
+ if (options.recursive)
102
+ params.set('recursive', 'true');
81
103
  const qs = params.toString();
82
104
  return this._fetch(`/api/dataset/${collectionId}/media${qs ? `?${qs}` : ''}`);
83
105
  },
84
106
  search: async (query, options = {}) => {
85
- const { organizationId } = await this._me();
86
- const params = new URLSearchParams({ q: query, organizationId });
107
+ const orgId = options.organizationId || (await this._me()).organizationId;
108
+ const params = new URLSearchParams({ q: query, organizationId: orgId });
87
109
  if (options.limit != null)
88
110
  params.set('limit', String(options.limit));
89
111
  const data = await this._fetch(`/api/search?${params.toString()}`);
@@ -108,12 +130,31 @@ class CoreViz {
108
130
  move: async (mediaId, destinationPath) => {
109
131
  return this._fetchMethod('PATCH', `/api/media/${mediaId}/move`, { destinationPath });
110
132
  },
133
+ delete: async (mediaId) => {
134
+ await this._fetchMethod('DELETE', `/api/media/${mediaId}`);
135
+ },
111
136
  addTag: async (mediaId, label, value) => {
112
137
  await this._fetchMethod('POST', `/api/media/${mediaId}/tags`, { label, value });
113
138
  },
114
139
  removeTag: async (mediaId, label, value) => {
115
140
  await this._fetchMethod('DELETE', `/api/media/${mediaId}/tags`, { label, value });
116
141
  },
142
+ removeTagGroup: async (mediaId, label) => {
143
+ await this._fetchMethod('DELETE', `/api/media/${mediaId}/tags`, { label });
144
+ },
145
+ renameTagGroup: async (mediaId, oldLabel, newLabel) => {
146
+ await this._fetchMethod('PATCH', `/api/media/${mediaId}/tags`, { oldLabel, newLabel });
147
+ },
148
+ listVersions: async (mediaId) => {
149
+ const data = await this._fetch(`/api/media/${mediaId}/versions`);
150
+ return data.versions;
151
+ },
152
+ deleteVersion: async (rootMediaId, versionId) => {
153
+ return this._fetchMethod('DELETE', `/api/media/${rootMediaId}/versions?versionId=${versionId}`);
154
+ },
155
+ selectVersion: async (versionId) => {
156
+ await this._fetchMethod('PATCH', `/api/media/${versionId}/select-version`);
157
+ },
117
158
  findSimilar: async (collectionId, objectId, options = {}) => {
118
159
  const params = new URLSearchParams({ similarToObjectId: objectId });
119
160
  if (options.limit != null)
@@ -168,14 +209,26 @@ class CoreViz {
168
209
  },
169
210
  };
170
211
  this.folders = {
171
- create: async (collectionId, name, path) => {
212
+ create: async (collectionId, name, path, reuse) => {
172
213
  const data = await this._fetchMethod('POST', '/api/folder', {
173
214
  datasetId: collectionId,
174
215
  name,
175
216
  ...(path ? { path } : {}),
217
+ ...(reuse ? { reuse: true } : {}),
176
218
  });
177
219
  return data.folder;
178
220
  },
221
+ get: async (folderId) => {
222
+ const data = await this._fetch(`/api/folder/${folderId}`);
223
+ return data.folder;
224
+ },
225
+ update: async (folderId, updates) => {
226
+ const data = await this._fetchMethod('PATCH', `/api/folder/${folderId}`, updates);
227
+ return data.folder;
228
+ },
229
+ delete: async (folderId) => {
230
+ await this._fetchMethod('DELETE', `/api/folder/${folderId}`);
231
+ },
179
232
  };
180
233
  this.tags = {
181
234
  list: async (collectionId) => {
@@ -192,9 +245,10 @@ class CoreViz {
192
245
  if (this.token) {
193
246
  headers['Authorization'] = `Bearer ${this.token}`;
194
247
  }
195
- else {
196
- headers['x-api-key'] = this.apiKey || "";
248
+ else if (this.apiKey) {
249
+ headers['x-api-key'] = this.apiKey;
197
250
  }
251
+ // If neither is set, rely on session cookies (browser same-origin requests).
198
252
  return headers;
199
253
  }
200
254
  /** Resolve the current user's org ID (cached after first call) */
@@ -228,7 +282,10 @@ class CoreViz {
228
282
  throw new Error('Insufficient credits');
229
283
  }
230
284
  if (!response.ok) {
231
- throw new Error(`Request failed (${response.status})`);
285
+ // Try to surface the server's error message from the response body.
286
+ const body = await response.json().catch(() => null);
287
+ const serverMessage = body?.error || body?.message;
288
+ throw new Error(serverMessage || `Request failed (${response.status})`);
232
289
  }
233
290
  const data = await response.json();
234
291
  if (data.error) {
@@ -240,7 +297,7 @@ class CoreViz {
240
297
  try {
241
298
  const resizedImage = await (0, resize_1.resize)(image, 512, 512);
242
299
  const headers = this.getHeaders();
243
- const response = await fetch(`https://lab.coreviz.io/api/ai/describe`, {
300
+ const response = await fetch(`${this._baseUrl}/api/ai/describe`, {
244
301
  method: 'POST',
245
302
  headers,
246
303
  body: JSON.stringify({ image: resizedImage }),
@@ -256,7 +313,7 @@ class CoreViz {
256
313
  try {
257
314
  const resizedImage = await (0, resize_1.resize)(image, 1024, 1024);
258
315
  const headers = this.getHeaders();
259
- const response = await fetch(`https://lab.coreviz.io/api/ai/edit`, {
316
+ const response = await fetch(`${this._baseUrl}/api/ai/edit`, {
260
317
  method: 'POST',
261
318
  headers,
262
319
  body: JSON.stringify({
@@ -281,7 +338,7 @@ class CoreViz {
281
338
  if (options.referenceImages && options.referenceImages.length > 0) {
282
339
  resizedImages = await Promise.all(options.referenceImages.map(img => (0, resize_1.resize)(img, 1024, 1024)));
283
340
  }
284
- const response = await fetch(`https://lab.coreviz.io/api/ai/generate`, {
341
+ const response = await fetch(`${this._baseUrl}/api/ai/generate`, {
285
342
  method: 'POST',
286
343
  headers,
287
344
  body: JSON.stringify({
@@ -306,7 +363,7 @@ class CoreViz {
306
363
  try {
307
364
  const resizedImage = await (0, resize_1.resize)(image, 512, 512);
308
365
  const headers = this.getHeaders();
309
- const response = await fetch("https://lab.coreviz.io/api/ai/tag", {
366
+ const response = await fetch(`${this._baseUrl}/api/ai/tag`, {
310
367
  method: 'POST',
311
368
  headers,
312
369
  body: JSON.stringify({
@@ -460,7 +517,7 @@ Output:
460
517
  else {
461
518
  body.text = input;
462
519
  }
463
- const response = await fetch("https://lab.coreviz.io/api/ai/embed", {
520
+ const response = await fetch(`${this._baseUrl}/api/ai/embed`, {
464
521
  method: 'POST',
465
522
  headers,
466
523
  body: JSON.stringify(body),
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
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';
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, FolderUpdateOptions, CollectionUpdateOptions, DeleteVersionResult } from './coreviz';
2
2
  import { resize } from './resize';
3
3
  export { CoreViz, resize };
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 };
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, FolderUpdateOptions, CollectionUpdateOptions, DeleteVersionResult };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreviz/sdk",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "CoreViz SDK",
5
5
  "main": "dist/index.js",
6
6
  "react-native": "dist/index.native.js",
@@ -11,10 +11,10 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "default": "./dist/index.native.js"
13
13
  },
14
- "default": {
15
- "types": "./dist/index.d.ts",
16
- "default": "./dist/index.js"
17
- }
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
18
  }
19
19
  },
20
20
  "files": [