@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 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');
@@ -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>;
@@ -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 };
@@ -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.19",
3
+ "version": "1.1.0",
4
4
  "description": "CoreViz SDK",
5
5
  "main": "dist/index.js",
6
6
  "react-native": "dist/index.native.js",