@coreviz/sdk 1.0.19 → 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 +204 -0
- package/dist/coreviz.d.ts +149 -0
- package/dist/coreviz.js +171 -0
- package/dist/coreviz.native.d.ts +126 -0
- package/dist/coreviz.native.js +139 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.native.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -221,3 +221,207 @@ Utility function to resize images client-side or server-side before processing.
|
|
|
221
221
|
const resized = await coreviz.resize(myFileObject, 800, 600);
|
|
222
222
|
// or import { resize } from '@coreviz/sdk';
|
|
223
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
|
}
|
|
@@ -35,8 +172,20 @@ export interface GenerateOptions {
|
|
|
35
172
|
export declare class CoreViz {
|
|
36
173
|
private apiKey?;
|
|
37
174
|
private token?;
|
|
175
|
+
private _baseUrl;
|
|
176
|
+
private _orgIdCache;
|
|
177
|
+
collections: CollectionsNamespace;
|
|
178
|
+
media: MediaNamespace;
|
|
179
|
+
folders: FoldersNamespace;
|
|
180
|
+
tags: TagsNamespace;
|
|
38
181
|
constructor(config?: CoreVizConfig);
|
|
39
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;
|
|
40
189
|
private handleResponse;
|
|
41
190
|
describe(image: string, options?: DescribeOptions): Promise<string>;
|
|
42
191
|
edit(image: string, options: EditOptions): Promise<string>;
|
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');
|
package/dist/coreviz.native.d.ts
CHANGED
|
@@ -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
|
}
|
|
@@ -35,8 +152,17 @@ export interface GenerateOptions {
|
|
|
35
152
|
export declare class CoreViz {
|
|
36
153
|
private apiKey?;
|
|
37
154
|
private token?;
|
|
155
|
+
private _baseUrl;
|
|
156
|
+
private _orgIdCache;
|
|
157
|
+
collections: CollectionsNamespace;
|
|
158
|
+
media: MediaNamespace;
|
|
159
|
+
folders: FoldersNamespace;
|
|
160
|
+
tags: TagsNamespace;
|
|
38
161
|
constructor(config?: CoreVizConfig);
|
|
39
162
|
private getHeaders;
|
|
163
|
+
private _me;
|
|
164
|
+
private _fetch;
|
|
165
|
+
private _fetchMethod;
|
|
40
166
|
private handleResponse;
|
|
41
167
|
describe(image: string, _options?: DescribeOptions): Promise<string>;
|
|
42
168
|
edit(image: string, options: EditOptions): Promise<string>;
|
package/dist/coreviz.native.js
CHANGED
|
@@ -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');
|
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 };
|
package/dist/index.native.d.ts
CHANGED
|
@@ -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 };
|