@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.cjs +139 -48
- package/dist/sdk.d.cts +504 -0
- package/dist/sdk.d.ts +504 -0
- package/dist/sdk.esm.d.ts +504 -0
- package/dist/sdk.esm.js +139 -49
- package/dist/sdk.umd.js +139 -48
- package/package.json +10 -11
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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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:
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
|
|
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
|
-
|
|
318
|
-
this.
|
|
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(
|
|
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
|
|
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
|
-
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
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
|
|
1560
|
+
async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
|
|
1485
1561
|
this._ctx.requireTokens();
|
|
1486
|
-
let path = `/api/v1/search/
|
|
1562
|
+
let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
|
|
1487
1563
|
const query = new URLSearchParams();
|
|
1488
|
-
if (
|
|
1489
|
-
if (
|
|
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
|
|
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
|
|
1574
|
+
async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
|
|
1501
1575
|
this._ctx.requireTokens();
|
|
1502
|
-
let path = `/api/v1/search/
|
|
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
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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:
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
|
|
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
|
-
|
|
324
|
-
this.
|
|
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(
|
|
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
|
|
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
|
-
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
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
|
|
1566
|
+
async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
|
|
1491
1567
|
this._ctx.requireTokens();
|
|
1492
|
-
let path = `/api/v1/search/
|
|
1568
|
+
let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
|
|
1493
1569
|
const query = new URLSearchParams();
|
|
1494
|
-
if (
|
|
1495
|
-
if (
|
|
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
|
|
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
|
|
1580
|
+
async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
|
|
1507
1581
|
this._ctx.requireTokens();
|
|
1508
|
-
let path = `/api/v1/search/
|
|
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.
|
|
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":
|
|
18
|
-
|
|
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
|
}
|