@mediaviz/sdk 1.2.0 → 1.4.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/dist/sdk.esm.js CHANGED
@@ -89,8 +89,15 @@ function requireErrors () {
89
89
  * @returns {OAuthError}
90
90
  */
91
91
  static fromResponse(status, body) {
92
- if (body && typeof body === 'object' && typeof body.error === 'string') {
93
- return new OAuthError(body.error, body.error_description ?? '', status, body);
92
+ // FastAPI wraps HTTPException payloads in a `detail` envelope, so an OAuth
93
+ // error arrives as { detail: { error, error_description } }. Unwrap it
94
+ // before falling back, or every server-side OAuth error reads as server_error.
95
+ const payload =
96
+ body && typeof body === 'object' && typeof body.error !== 'string' && body.detail
97
+ ? body.detail
98
+ : body;
99
+ if (payload && typeof payload === 'object' && typeof payload.error === 'string') {
100
+ return new OAuthError(payload.error, payload.error_description ?? '', status, body);
94
101
  }
95
102
  return new OAuthError('server_error', 'Unexpected server response', status, body);
96
103
  }
@@ -171,6 +178,9 @@ function requireClient () {
171
178
  const { postForm, postJson } = requireHttp();
172
179
  const { OAuthError } = requireErrors();
173
180
 
181
+ // In-flight dedup key for client_credentials re-mints (no refresh_token to key on).
182
+ const CLIENT_CREDENTIALS_KEY = Symbol('client_credentials');
183
+
174
184
  class OAuthClient {
175
185
  /**
176
186
  * @param {import('./types').OAuthClientConfig} config
@@ -186,12 +196,22 @@ function requireClient () {
186
196
  */
187
197
  static async registerClient(params) {
188
198
  const baseUrl = params.baseUrl.replace(/\/+$/, '');
189
- return postJson(`${baseUrl}/oauth/clients`, {
199
+ const body = {
190
200
  client_name: params.clientName,
191
201
  client_type: params.clientType,
192
202
  redirect_uris: params.redirectUris,
193
203
  is_first_party: params.isFirstParty,
194
- });
204
+ };
205
+ // grant_types/company_id are required to provision a confidential
206
+ // client_credentials (machine-to-machine) client; omitted bodies keep the
207
+ // legacy public/authorization_code shape. The endpoint is admin-gated, so a
208
+ // bearer token must be supplied for the request to authenticate.
209
+ if (params.grantTypes !== undefined) body.grant_types = params.grantTypes;
210
+ if (params.companyId !== undefined) body.company_id = params.companyId;
211
+ const headers = params.accessToken
212
+ ? { Authorization: `Bearer ${params.accessToken}` }
213
+ : {};
214
+ return postJson(`${baseUrl}/oauth/clients`, body, headers);
195
215
  }
196
216
 
197
217
  /**
@@ -309,15 +329,27 @@ function requireClient () {
309
329
  return { data: json, updatedTokens: null };
310
330
  }
311
331
 
312
- // 401: attempt refresh — propagate OAuthError if refresh fails.
313
- // Concurrent request() callers with the same refresh_token share one in-flight
314
- // call so the server only sees one rotation (the second would get invalid_grant).
315
- let pending = this._inFlightRefreshes.get(refreshToken);
332
+ // 401: re-authenticate, then retry once.
333
+ // - User sessions (refresh_token present) use the refresh_token grant. The server
334
+ // rotates the token one-time, so concurrent callers share one in-flight call
335
+ // (a second rotation would get invalid_grant).
336
+ // - Machine-to-machine sessions (no refresh_token, client_secret configured) re-mint
337
+ // via client_credentials, which issues no refresh token to rotate (RFC 6749 §4.4).
338
+ // - With neither a refresh_token nor a client_secret there is nothing to retry with.
339
+ if (!refreshToken && !this._config.clientSecret) {
340
+ throw OAuthError.fromResponse(401, await firstResponse.json());
341
+ }
342
+
343
+ const reauthKey = refreshToken || CLIENT_CREDENTIALS_KEY;
344
+ let pending = this._inFlightRefreshes.get(reauthKey);
316
345
  if (!pending) {
317
- pending = this.refreshAccessToken(refreshToken).finally(() => {
318
- this._inFlightRefreshes.delete(refreshToken);
346
+ const reauth = refreshToken
347
+ ? this.refreshAccessToken(refreshToken)
348
+ : this.getClientCredentialsToken();
349
+ pending = reauth.finally(() => {
350
+ this._inFlightRefreshes.delete(reauthKey);
319
351
  });
320
- this._inFlightRefreshes.set(refreshToken, pending);
352
+ this._inFlightRefreshes.set(reauthKey, pending);
321
353
  }
322
354
  const newTokens = await pending;
323
355
 
@@ -399,6 +431,9 @@ function requireTypes () {
399
431
  * @property {string} clientType - "public" or "confidential"
400
432
  * @property {string[]} redirectUris - HTTPS redirect URIs
401
433
  * @property {boolean} isFirstParty - Whether this is a first-party client
434
+ * @property {string[]} [grantTypes] - Allowed grants, e.g. ["client_credentials"]; required for machine-to-machine clients
435
+ * @property {number} [companyId] - Company to bind the client to; required for the client_credentials grant
436
+ * @property {string} [accessToken] - Admin bearer token; the registration endpoint is authorization-gated
402
437
  */
403
438
 
404
439
  /**
@@ -456,6 +491,17 @@ class AiModelCredits {
456
491
  }
457
492
  }
458
493
 
494
+ class Admin {
495
+ constructor(ctx) { this._ctx = ctx; }
496
+
497
+ async getCategoryLabels(category) {
498
+ this._ctx.requireTokens();
499
+ const path = `/api/v1/admin/category_labels/${encodeURIComponent(category)}`;
500
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
501
+ return data;
502
+ }
503
+ }
504
+
459
505
  class Company {
460
506
  constructor(ctx) { this._ctx = ctx; }
461
507
 
@@ -899,6 +945,17 @@ class Keywords {
899
945
  return data;
900
946
  }
901
947
 
948
+ async addCompanyToKeywordFilteringList(keywordListId, { companyId } = {}) {
949
+ this._ctx.requireTokens();
950
+ let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
951
+ const query = new URLSearchParams();
952
+ if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
953
+ const qs = query.toString();
954
+ if (qs) path += '?' + qs;
955
+ const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
956
+ return data;
957
+ }
958
+
902
959
  async requestKeywordListExport(keywordListId) {
903
960
  this._ctx.requireTokens();
904
961
  const path = `/api/v1/keyword/export/${encodeURIComponent(keywordListId)}`;
@@ -931,6 +988,17 @@ class Keywords {
931
988
  return data;
932
989
  }
933
990
 
991
+ async removeCompanyFromKeywordFilteringList(keywordListId, { companyId } = {}) {
992
+ this._ctx.requireTokens();
993
+ let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
994
+ const query = new URLSearchParams();
995
+ if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
996
+ const qs = query.toString();
997
+ if (qs) path += '?' + qs;
998
+ const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
999
+ return data;
1000
+ }
1001
+
934
1002
  async deleteKeywordFilteringListById(keywordListId) {
935
1003
  this._ctx.requireTokens();
936
1004
  const path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}`;
@@ -1090,6 +1158,17 @@ class Person {
1090
1158
  return data;
1091
1159
  }
1092
1160
 
1161
+ async getPhotoIdsByPersons(projectTableName, { personId } = {}) {
1162
+ this._ctx.requireTokens();
1163
+ let path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photos`;
1164
+ const query = new URLSearchParams();
1165
+ if (personId !== undefined) (Array.isArray(personId) ? personId : [personId]).forEach(v => query.append('person_id', v));
1166
+ const qs = query.toString();
1167
+ if (qs) path += '?' + qs;
1168
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1169
+ return data;
1170
+ }
1171
+
1093
1172
  async getAllPersonsFromPhoto(projectTableName, photoId) {
1094
1173
  this._ctx.requireTokens();
1095
1174
  const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photo/${encodeURIComponent(photoId)}`;
@@ -1390,13 +1469,6 @@ class Photos {
1390
1469
  return data;
1391
1470
  }
1392
1471
 
1393
- async getProjectThumbnail(tableName) {
1394
- this._ctx.requireTokens();
1395
- const path = `/api/v1/photos_project/${encodeURIComponent(tableName)}`;
1396
- const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1397
- return data;
1398
- }
1399
-
1400
1472
  async updatePhotoInProject({ tableName, photoId, photoData } = {}) {
1401
1473
  this._ctx.requireTokens();
1402
1474
  let path = `/api/v1/photos_update`;
@@ -1459,50 +1531,56 @@ class Search {
1459
1531
  return data;
1460
1532
  }
1461
1533
 
1462
- async searchProjectPhotosText(projectTableName, searchText, size = undefined) {
1534
+ async searchProjectPhotosParametrized(projectTableName, { andSearchText, orSearchText, notSearchText, city, country, state, albums, dateMin, dateMax, dateNullAnd, dateNullOr, ascOrDesc, bestOfSimilarSetsOnly, splitByTier, lastId, limit } = {}) {
1463
1535
  this._ctx.requireTokens();
1464
- const path = `/api/v1/search/text/${encodeURIComponent(projectTableName)}/`;
1465
- const body = stripUndef$1({
1466
- search_text: searchText,
1467
- size: size,
1468
- });
1469
- const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
1470
- return data;
1471
- }
1472
-
1473
- async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined) {
1474
- this._ctx.requireTokens();
1475
- const path = `/api/v1/search/nl/${encodeURIComponent(projectTableName)}/`;
1476
- const body = stripUndef$1({
1477
- search_text: searchText,
1478
- size: size,
1479
- });
1480
- const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
1536
+ let path = `/api/v1/search/parametrized/${encodeURIComponent(projectTableName)}/`;
1537
+ const query = new URLSearchParams();
1538
+ if (andSearchText !== undefined) (Array.isArray(andSearchText) ? andSearchText : [andSearchText]).forEach(v => query.append('and_search_text', v));
1539
+ if (orSearchText !== undefined) (Array.isArray(orSearchText) ? orSearchText : [orSearchText]).forEach(v => query.append('or_search_text', v));
1540
+ if (notSearchText !== undefined) (Array.isArray(notSearchText) ? notSearchText : [notSearchText]).forEach(v => query.append('not_search_text', v));
1541
+ if (city !== undefined) (Array.isArray(city) ? city : [city]).forEach(v => query.append('city', v));
1542
+ if (country !== undefined) (Array.isArray(country) ? country : [country]).forEach(v => query.append('country', v));
1543
+ if (state !== undefined) (Array.isArray(state) ? state : [state]).forEach(v => query.append('state', v));
1544
+ if (albums !== undefined) (Array.isArray(albums) ? albums : [albums]).forEach(v => query.append('albums', v));
1545
+ if (dateMin !== undefined) (Array.isArray(dateMin) ? dateMin : [dateMin]).forEach(v => query.append('date_min', v));
1546
+ if (dateMax !== undefined) (Array.isArray(dateMax) ? dateMax : [dateMax]).forEach(v => query.append('date_max', v));
1547
+ if (dateNullAnd !== undefined) (Array.isArray(dateNullAnd) ? dateNullAnd : [dateNullAnd]).forEach(v => query.append('date_null_and', v));
1548
+ if (dateNullOr !== undefined) (Array.isArray(dateNullOr) ? dateNullOr : [dateNullOr]).forEach(v => query.append('date_null_or', v));
1549
+ if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
1550
+ if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
1551
+ if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
1552
+ if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
1553
+ if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
1554
+ const qs = query.toString();
1555
+ if (qs) path += '?' + qs;
1556
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1481
1557
  return data;
1482
1558
  }
1483
1559
 
1484
- async searchProjectPhotosNaturalLanguageHybrid(projectTableName, searchText, size = undefined, { blend, minCosine } = {}) {
1560
+ async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
1485
1561
  this._ctx.requireTokens();
1486
- let path = `/api/v1/search/nl_hybrid/${encodeURIComponent(projectTableName)}/`;
1562
+ let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
1487
1563
  const query = new URLSearchParams();
1488
- if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
1489
- if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
1564
+ if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
1565
+ if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
1566
+ if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
1567
+ if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
1490
1568
  const qs = query.toString();
1491
1569
  if (qs) path += '?' + qs;
1492
- const body = stripUndef$1({
1493
- search_text: searchText,
1494
- size: size,
1495
- });
1496
- const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
1570
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1497
1571
  return data;
1498
1572
  }
1499
1573
 
1500
- async searchProjectPhotosNaturalLanguageAuto(projectTableName, searchText, size = undefined, { blend, minCosine } = {}) {
1574
+ async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
1501
1575
  this._ctx.requireTokens();
1502
- let path = `/api/v1/search/auto/${encodeURIComponent(projectTableName)}/`;
1576
+ let path = `/api/v1/search/natural_language/${encodeURIComponent(projectTableName)}/`;
1503
1577
  const query = new URLSearchParams();
1504
- if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
1505
1578
  if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
1579
+ if (labelMinCosine !== undefined) (Array.isArray(labelMinCosine) ? labelMinCosine : [labelMinCosine]).forEach(v => query.append('label_min_cosine', v));
1580
+ if (labelTopK !== undefined) (Array.isArray(labelTopK) ? labelTopK : [labelTopK]).forEach(v => query.append('label_top_k', v));
1581
+ if (labelDelta !== undefined) (Array.isArray(labelDelta) ? labelDelta : [labelDelta]).forEach(v => query.append('label_delta', v));
1582
+ if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
1583
+ if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
1506
1584
  const qs = query.toString();
1507
1585
  if (qs) path += '?' + qs;
1508
1586
  const body = stripUndef$1({
@@ -1654,6 +1732,17 @@ class Users {
1654
1732
  const { data } = await this._ctx.client.request(path, 'PUT', this._ctx.accessToken, this._ctx.refreshToken, body);
1655
1733
  return data;
1656
1734
  }
1735
+
1736
+ async deleteUser(userId, { newCompanyOwnerId } = {}) {
1737
+ this._ctx.requireTokens();
1738
+ let path = `/api/v1/users/delete/${encodeURIComponent(userId)}`;
1739
+ const query = new URLSearchParams();
1740
+ if (newCompanyOwnerId !== undefined) (Array.isArray(newCompanyOwnerId) ? newCompanyOwnerId : [newCompanyOwnerId]).forEach(v => query.append('new_company_owner_id', v));
1741
+ const qs = query.toString();
1742
+ if (qs) path += '?' + qs;
1743
+ const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
1744
+ return data;
1745
+ }
1657
1746
  }
1658
1747
 
1659
1748
  // Auto-generated — do not edit
@@ -1718,6 +1807,7 @@ class MediaViz {
1718
1807
 
1719
1808
  const _ctx = new _Context(this);
1720
1809
  this.aiModelCredits = new AiModelCredits(_ctx);
1810
+ this.admin = new Admin(_ctx);
1721
1811
  this.company = new Company(_ctx);
1722
1812
  this.curatedAlbums = new CuratedAlbums(_ctx);
1723
1813
  this.customAlbums = new CustomAlbums(_ctx);
@@ -1762,4 +1852,4 @@ class MediaViz {
1762
1852
  get refreshToken() { return this._refreshToken; }
1763
1853
  }
1764
1854
 
1765
- export { AiModelCredits, ApiError, Company, CuratedAlbums, CustomAlbums, EmailTokens, Health, Keywords, MediaViz, NotFoundError, OAuthClient, OAuthError, OAuthErrorCode, OauthAuthorization, OauthLogin, OauthToken, Person, Photos, Photoupload, Projects, RateLimitError, Search, ServerError, Users, ValidationError, handleResponse };
1855
+ export { Admin, AiModelCredits, ApiError, Company, CuratedAlbums, CustomAlbums, EmailTokens, Health, Keywords, MediaViz, NotFoundError, OAuthClient, OAuthError, OAuthErrorCode, OauthAuthorization, OauthLogin, OauthToken, Person, Photos, Photoupload, Projects, RateLimitError, Search, ServerError, Users, ValidationError, handleResponse };
package/dist/sdk.umd.js CHANGED
@@ -95,8 +95,15 @@
95
95
  * @returns {OAuthError}
96
96
  */
97
97
  static fromResponse(status, body) {
98
- if (body && typeof body === 'object' && typeof body.error === 'string') {
99
- return new OAuthError(body.error, body.error_description ?? '', status, body);
98
+ // FastAPI wraps HTTPException payloads in a `detail` envelope, so an OAuth
99
+ // error arrives as { detail: { error, error_description } }. Unwrap it
100
+ // before falling back, or every server-side OAuth error reads as server_error.
101
+ const payload =
102
+ body && typeof body === 'object' && typeof body.error !== 'string' && body.detail
103
+ ? body.detail
104
+ : body;
105
+ if (payload && typeof payload === 'object' && typeof payload.error === 'string') {
106
+ return new OAuthError(payload.error, payload.error_description ?? '', status, body);
100
107
  }
101
108
  return new OAuthError('server_error', 'Unexpected server response', status, body);
102
109
  }
@@ -177,6 +184,9 @@
177
184
  const { postForm, postJson } = requireHttp();
178
185
  const { OAuthError } = requireErrors();
179
186
 
187
+ // In-flight dedup key for client_credentials re-mints (no refresh_token to key on).
188
+ const CLIENT_CREDENTIALS_KEY = Symbol('client_credentials');
189
+
180
190
  class OAuthClient {
181
191
  /**
182
192
  * @param {import('./types').OAuthClientConfig} config
@@ -192,12 +202,22 @@
192
202
  */
193
203
  static async registerClient(params) {
194
204
  const baseUrl = params.baseUrl.replace(/\/+$/, '');
195
- return postJson(`${baseUrl}/oauth/clients`, {
205
+ const body = {
196
206
  client_name: params.clientName,
197
207
  client_type: params.clientType,
198
208
  redirect_uris: params.redirectUris,
199
209
  is_first_party: params.isFirstParty,
200
- });
210
+ };
211
+ // grant_types/company_id are required to provision a confidential
212
+ // client_credentials (machine-to-machine) client; omitted bodies keep the
213
+ // legacy public/authorization_code shape. The endpoint is admin-gated, so a
214
+ // bearer token must be supplied for the request to authenticate.
215
+ if (params.grantTypes !== undefined) body.grant_types = params.grantTypes;
216
+ if (params.companyId !== undefined) body.company_id = params.companyId;
217
+ const headers = params.accessToken
218
+ ? { Authorization: `Bearer ${params.accessToken}` }
219
+ : {};
220
+ return postJson(`${baseUrl}/oauth/clients`, body, headers);
201
221
  }
202
222
 
203
223
  /**
@@ -315,15 +335,27 @@
315
335
  return { data: json, updatedTokens: null };
316
336
  }
317
337
 
318
- // 401: attempt refresh — propagate OAuthError if refresh fails.
319
- // Concurrent request() callers with the same refresh_token share one in-flight
320
- // call so the server only sees one rotation (the second would get invalid_grant).
321
- let pending = this._inFlightRefreshes.get(refreshToken);
338
+ // 401: re-authenticate, then retry once.
339
+ // - User sessions (refresh_token present) use the refresh_token grant. The server
340
+ // rotates the token one-time, so concurrent callers share one in-flight call
341
+ // (a second rotation would get invalid_grant).
342
+ // - Machine-to-machine sessions (no refresh_token, client_secret configured) re-mint
343
+ // via client_credentials, which issues no refresh token to rotate (RFC 6749 §4.4).
344
+ // - With neither a refresh_token nor a client_secret there is nothing to retry with.
345
+ if (!refreshToken && !this._config.clientSecret) {
346
+ throw OAuthError.fromResponse(401, await firstResponse.json());
347
+ }
348
+
349
+ const reauthKey = refreshToken || CLIENT_CREDENTIALS_KEY;
350
+ let pending = this._inFlightRefreshes.get(reauthKey);
322
351
  if (!pending) {
323
- pending = this.refreshAccessToken(refreshToken).finally(() => {
324
- this._inFlightRefreshes.delete(refreshToken);
352
+ const reauth = refreshToken
353
+ ? this.refreshAccessToken(refreshToken)
354
+ : this.getClientCredentialsToken();
355
+ pending = reauth.finally(() => {
356
+ this._inFlightRefreshes.delete(reauthKey);
325
357
  });
326
- this._inFlightRefreshes.set(refreshToken, pending);
358
+ this._inFlightRefreshes.set(reauthKey, pending);
327
359
  }
328
360
  const newTokens = await pending;
329
361
 
@@ -405,6 +437,9 @@
405
437
  * @property {string} clientType - "public" or "confidential"
406
438
  * @property {string[]} redirectUris - HTTPS redirect URIs
407
439
  * @property {boolean} isFirstParty - Whether this is a first-party client
440
+ * @property {string[]} [grantTypes] - Allowed grants, e.g. ["client_credentials"]; required for machine-to-machine clients
441
+ * @property {number} [companyId] - Company to bind the client to; required for the client_credentials grant
442
+ * @property {string} [accessToken] - Admin bearer token; the registration endpoint is authorization-gated
408
443
  */
409
444
 
410
445
  /**
@@ -462,6 +497,17 @@
462
497
  }
463
498
  }
464
499
 
500
+ class Admin {
501
+ constructor(ctx) { this._ctx = ctx; }
502
+
503
+ async getCategoryLabels(category) {
504
+ this._ctx.requireTokens();
505
+ const path = `/api/v1/admin/category_labels/${encodeURIComponent(category)}`;
506
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
507
+ return data;
508
+ }
509
+ }
510
+
465
511
  class Company {
466
512
  constructor(ctx) { this._ctx = ctx; }
467
513
 
@@ -905,6 +951,17 @@
905
951
  return data;
906
952
  }
907
953
 
954
+ async addCompanyToKeywordFilteringList(keywordListId, { companyId } = {}) {
955
+ this._ctx.requireTokens();
956
+ let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
957
+ const query = new URLSearchParams();
958
+ if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
959
+ const qs = query.toString();
960
+ if (qs) path += '?' + qs;
961
+ const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
962
+ return data;
963
+ }
964
+
908
965
  async requestKeywordListExport(keywordListId) {
909
966
  this._ctx.requireTokens();
910
967
  const path = `/api/v1/keyword/export/${encodeURIComponent(keywordListId)}`;
@@ -937,6 +994,17 @@
937
994
  return data;
938
995
  }
939
996
 
997
+ async removeCompanyFromKeywordFilteringList(keywordListId, { companyId } = {}) {
998
+ this._ctx.requireTokens();
999
+ let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
1000
+ const query = new URLSearchParams();
1001
+ if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
1002
+ const qs = query.toString();
1003
+ if (qs) path += '?' + qs;
1004
+ const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
1005
+ return data;
1006
+ }
1007
+
940
1008
  async deleteKeywordFilteringListById(keywordListId) {
941
1009
  this._ctx.requireTokens();
942
1010
  const path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}`;
@@ -1096,6 +1164,17 @@
1096
1164
  return data;
1097
1165
  }
1098
1166
 
1167
+ async getPhotoIdsByPersons(projectTableName, { personId } = {}) {
1168
+ this._ctx.requireTokens();
1169
+ let path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photos`;
1170
+ const query = new URLSearchParams();
1171
+ if (personId !== undefined) (Array.isArray(personId) ? personId : [personId]).forEach(v => query.append('person_id', v));
1172
+ const qs = query.toString();
1173
+ if (qs) path += '?' + qs;
1174
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1175
+ return data;
1176
+ }
1177
+
1099
1178
  async getAllPersonsFromPhoto(projectTableName, photoId) {
1100
1179
  this._ctx.requireTokens();
1101
1180
  const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photo/${encodeURIComponent(photoId)}`;
@@ -1396,13 +1475,6 @@
1396
1475
  return data;
1397
1476
  }
1398
1477
 
1399
- async getProjectThumbnail(tableName) {
1400
- this._ctx.requireTokens();
1401
- const path = `/api/v1/photos_project/${encodeURIComponent(tableName)}`;
1402
- const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1403
- return data;
1404
- }
1405
-
1406
1478
  async updatePhotoInProject({ tableName, photoId, photoData } = {}) {
1407
1479
  this._ctx.requireTokens();
1408
1480
  let path = `/api/v1/photos_update`;
@@ -1465,50 +1537,56 @@
1465
1537
  return data;
1466
1538
  }
1467
1539
 
1468
- async searchProjectPhotosText(projectTableName, searchText, size = undefined) {
1540
+ async searchProjectPhotosParametrized(projectTableName, { andSearchText, orSearchText, notSearchText, city, country, state, albums, dateMin, dateMax, dateNullAnd, dateNullOr, ascOrDesc, bestOfSimilarSetsOnly, splitByTier, lastId, limit } = {}) {
1469
1541
  this._ctx.requireTokens();
1470
- const path = `/api/v1/search/text/${encodeURIComponent(projectTableName)}/`;
1471
- const body = stripUndef$1({
1472
- search_text: searchText,
1473
- size: size,
1474
- });
1475
- const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
1476
- return data;
1477
- }
1478
-
1479
- async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined) {
1480
- this._ctx.requireTokens();
1481
- const path = `/api/v1/search/nl/${encodeURIComponent(projectTableName)}/`;
1482
- const body = stripUndef$1({
1483
- search_text: searchText,
1484
- size: size,
1485
- });
1486
- const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
1542
+ let path = `/api/v1/search/parametrized/${encodeURIComponent(projectTableName)}/`;
1543
+ const query = new URLSearchParams();
1544
+ if (andSearchText !== undefined) (Array.isArray(andSearchText) ? andSearchText : [andSearchText]).forEach(v => query.append('and_search_text', v));
1545
+ if (orSearchText !== undefined) (Array.isArray(orSearchText) ? orSearchText : [orSearchText]).forEach(v => query.append('or_search_text', v));
1546
+ if (notSearchText !== undefined) (Array.isArray(notSearchText) ? notSearchText : [notSearchText]).forEach(v => query.append('not_search_text', v));
1547
+ if (city !== undefined) (Array.isArray(city) ? city : [city]).forEach(v => query.append('city', v));
1548
+ if (country !== undefined) (Array.isArray(country) ? country : [country]).forEach(v => query.append('country', v));
1549
+ if (state !== undefined) (Array.isArray(state) ? state : [state]).forEach(v => query.append('state', v));
1550
+ if (albums !== undefined) (Array.isArray(albums) ? albums : [albums]).forEach(v => query.append('albums', v));
1551
+ if (dateMin !== undefined) (Array.isArray(dateMin) ? dateMin : [dateMin]).forEach(v => query.append('date_min', v));
1552
+ if (dateMax !== undefined) (Array.isArray(dateMax) ? dateMax : [dateMax]).forEach(v => query.append('date_max', v));
1553
+ if (dateNullAnd !== undefined) (Array.isArray(dateNullAnd) ? dateNullAnd : [dateNullAnd]).forEach(v => query.append('date_null_and', v));
1554
+ if (dateNullOr !== undefined) (Array.isArray(dateNullOr) ? dateNullOr : [dateNullOr]).forEach(v => query.append('date_null_or', v));
1555
+ if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
1556
+ if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
1557
+ if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
1558
+ if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
1559
+ if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
1560
+ const qs = query.toString();
1561
+ if (qs) path += '?' + qs;
1562
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1487
1563
  return data;
1488
1564
  }
1489
1565
 
1490
- async searchProjectPhotosNaturalLanguageHybrid(projectTableName, searchText, size = undefined, { blend, minCosine } = {}) {
1566
+ async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
1491
1567
  this._ctx.requireTokens();
1492
- let path = `/api/v1/search/nl_hybrid/${encodeURIComponent(projectTableName)}/`;
1568
+ let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
1493
1569
  const query = new URLSearchParams();
1494
- if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
1495
- if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
1570
+ if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
1571
+ if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
1572
+ if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
1573
+ if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
1496
1574
  const qs = query.toString();
1497
1575
  if (qs) path += '?' + qs;
1498
- const body = stripUndef$1({
1499
- search_text: searchText,
1500
- size: size,
1501
- });
1502
- const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
1576
+ const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
1503
1577
  return data;
1504
1578
  }
1505
1579
 
1506
- async searchProjectPhotosNaturalLanguageAuto(projectTableName, searchText, size = undefined, { blend, minCosine } = {}) {
1580
+ async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
1507
1581
  this._ctx.requireTokens();
1508
- let path = `/api/v1/search/auto/${encodeURIComponent(projectTableName)}/`;
1582
+ let path = `/api/v1/search/natural_language/${encodeURIComponent(projectTableName)}/`;
1509
1583
  const query = new URLSearchParams();
1510
- if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
1511
1584
  if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
1585
+ if (labelMinCosine !== undefined) (Array.isArray(labelMinCosine) ? labelMinCosine : [labelMinCosine]).forEach(v => query.append('label_min_cosine', v));
1586
+ if (labelTopK !== undefined) (Array.isArray(labelTopK) ? labelTopK : [labelTopK]).forEach(v => query.append('label_top_k', v));
1587
+ if (labelDelta !== undefined) (Array.isArray(labelDelta) ? labelDelta : [labelDelta]).forEach(v => query.append('label_delta', v));
1588
+ if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
1589
+ if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
1512
1590
  const qs = query.toString();
1513
1591
  if (qs) path += '?' + qs;
1514
1592
  const body = stripUndef$1({
@@ -1660,6 +1738,17 @@
1660
1738
  const { data } = await this._ctx.client.request(path, 'PUT', this._ctx.accessToken, this._ctx.refreshToken, body);
1661
1739
  return data;
1662
1740
  }
1741
+
1742
+ async deleteUser(userId, { newCompanyOwnerId } = {}) {
1743
+ this._ctx.requireTokens();
1744
+ let path = `/api/v1/users/delete/${encodeURIComponent(userId)}`;
1745
+ const query = new URLSearchParams();
1746
+ if (newCompanyOwnerId !== undefined) (Array.isArray(newCompanyOwnerId) ? newCompanyOwnerId : [newCompanyOwnerId]).forEach(v => query.append('new_company_owner_id', v));
1747
+ const qs = query.toString();
1748
+ if (qs) path += '?' + qs;
1749
+ const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
1750
+ return data;
1751
+ }
1663
1752
  }
1664
1753
 
1665
1754
  // Auto-generated — do not edit
@@ -1724,6 +1813,7 @@
1724
1813
 
1725
1814
  const _ctx = new _Context(this);
1726
1815
  this.aiModelCredits = new AiModelCredits(_ctx);
1816
+ this.admin = new Admin(_ctx);
1727
1817
  this.company = new Company(_ctx);
1728
1818
  this.curatedAlbums = new CuratedAlbums(_ctx);
1729
1819
  this.customAlbums = new CustomAlbums(_ctx);
@@ -1768,6 +1858,7 @@
1768
1858
  get refreshToken() { return this._refreshToken; }
1769
1859
  }
1770
1860
 
1861
+ exports.Admin = Admin;
1771
1862
  exports.AiModelCredits = AiModelCredits;
1772
1863
  exports.ApiError = ApiError;
1773
1864
  exports.Company = Company;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mediaviz/sdk",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "MediaViz JavaScript SDK \u2014 auto-generated public endpoint client.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,11 +11,18 @@
11
11
  "main": "./dist/sdk.cjs",
12
12
  "module": "./dist/sdk.esm.js",
13
13
  "browser": "./dist/sdk.umd.js",
14
+ "types": "./dist/sdk.d.ts",
14
15
  "exports": {
15
16
  ".": {
16
17
  "browser": "./dist/sdk.umd.js",
17
- "import": "./dist/sdk.esm.js",
18
- "require": "./dist/sdk.cjs",
18
+ "import": {
19
+ "types": "./dist/sdk.esm.d.ts",
20
+ "default": "./dist/sdk.esm.js"
21
+ },
22
+ "require": {
23
+ "types": "./dist/sdk.d.cts",
24
+ "default": "./dist/sdk.cjs"
25
+ },
19
26
  "default": "./dist/sdk.cjs"
20
27
  }
21
28
  },
@@ -27,15 +34,7 @@
27
34
  "publishConfig": {
28
35
  "access": "public"
29
36
  },
30
- "scripts": {
31
- "build": "rollup -c"
32
- },
33
37
  "optionalDependencies": {
34
38
  "sharp": "^0.33.0"
35
- },
36
- "devDependencies": {
37
- "rollup": "^4.0.0",
38
- "@rollup/plugin-node-resolve": "^16.0.0",
39
- "@rollup/plugin-commonjs": "^29.0.0"
40
39
  }
41
40
  }