frameio 3.2.3 → 4.1.1
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 +159 -4
- package/dist/cjs/BaseClient.d.ts +4 -0
- package/dist/cjs/BaseClient.js +4 -2
- package/dist/cjs/api/resources/accountPermissions/client/Client.js +6 -6
- package/dist/cjs/api/resources/accounts/client/Client.js +13 -13
- package/dist/cjs/api/resources/comments/client/Client.js +48 -48
- package/dist/cjs/api/resources/files/client/Client.js +77 -77
- package/dist/cjs/api/resources/folders/client/Client.js +56 -56
- package/dist/cjs/api/resources/metadata/client/Client.d.ts +4 -4
- package/dist/cjs/api/resources/metadata/client/Client.js +18 -18
- package/dist/cjs/api/resources/metadata/client/requests/BulkUpdateMetadataParams.d.ts +4 -4
- package/dist/cjs/api/resources/metadataFields/client/Client.js +28 -28
- package/dist/cjs/api/resources/projectPermissions/client/Client.js +20 -20
- package/dist/cjs/api/resources/projects/client/Client.js +34 -34
- package/dist/cjs/api/resources/shares/client/Client.d.ts +4 -4
- package/dist/cjs/api/resources/shares/client/Client.js +72 -72
- package/dist/cjs/api/resources/shares/client/requests/AddAssetParams.d.ts +1 -1
- package/dist/cjs/api/resources/shares/client/requests/CreateShareParams.d.ts +2 -2
- package/dist/cjs/api/resources/shares/client/requests/UpdateShareParams.d.ts +1 -1
- package/dist/cjs/api/resources/users/client/Client.js +7 -7
- package/dist/cjs/api/resources/versionStacks/client/Client.d.ts +1 -1
- package/dist/cjs/api/resources/versionStacks/client/Client.js +43 -43
- package/dist/cjs/api/resources/versionStacks/client/requests/VersionStackCreateParams.d.ts +1 -1
- package/dist/cjs/api/resources/webhooks/client/Client.js +34 -34
- package/dist/cjs/api/resources/workspacePermissions/client/Client.js +20 -20
- package/dist/cjs/api/resources/workspaces/client/Client.js +34 -34
- package/dist/cjs/api/types/Account.d.ts +0 -2
- package/dist/cjs/api/types/AuditLogwithIncludes.d.ts +4 -0
- package/dist/cjs/api/types/AuditLogwithIncludes.js +4 -0
- package/dist/cjs/api/types/Filters.d.ts +4 -0
- package/dist/cjs/api/types/Filters.js +4 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +21 -1
- package/dist/cjs/oauth/BaseAuth.d.ts +66 -0
- package/dist/cjs/oauth/BaseAuth.js +113 -0
- package/dist/cjs/oauth/NativeAppAuth.d.ts +32 -0
- package/dist/cjs/oauth/NativeAppAuth.js +35 -0
- package/dist/cjs/oauth/SPAAuth.d.ts +38 -0
- package/dist/cjs/oauth/SPAAuth.js +96 -0
- package/dist/cjs/oauth/ServerToServerAuth.d.ts +17 -0
- package/dist/cjs/oauth/ServerToServerAuth.js +49 -0
- package/dist/cjs/oauth/TokenManager.d.ts +83 -0
- package/dist/cjs/oauth/TokenManager.js +174 -0
- package/dist/cjs/oauth/WebAppAuth.d.ts +29 -0
- package/dist/cjs/oauth/WebAppAuth.js +88 -0
- package/dist/cjs/oauth/errors.d.ts +41 -0
- package/dist/cjs/oauth/errors.js +83 -0
- package/dist/cjs/oauth/http.d.ts +70 -0
- package/dist/cjs/oauth/http.js +280 -0
- package/dist/cjs/oauth/index.d.ts +34 -0
- package/dist/cjs/oauth/index.js +47 -0
- package/dist/cjs/oauth/logger.d.ts +17 -0
- package/dist/cjs/oauth/logger.js +18 -0
- package/dist/cjs/oauth/pkce.d.ts +30 -0
- package/dist/cjs/oauth/pkce.js +102 -0
- package/dist/cjs/oauth/validation.d.ts +17 -0
- package/dist/cjs/oauth/validation.js +55 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/BaseClient.d.mts +4 -0
- package/dist/esm/BaseClient.mjs +4 -2
- package/dist/esm/api/resources/accountPermissions/client/Client.mjs +7 -7
- package/dist/esm/api/resources/accounts/client/Client.mjs +13 -13
- package/dist/esm/api/resources/comments/client/Client.mjs +49 -49
- package/dist/esm/api/resources/files/client/Client.mjs +78 -78
- package/dist/esm/api/resources/folders/client/Client.mjs +57 -57
- package/dist/esm/api/resources/metadata/client/Client.d.mts +4 -4
- package/dist/esm/api/resources/metadata/client/Client.mjs +19 -19
- package/dist/esm/api/resources/metadata/client/requests/BulkUpdateMetadataParams.d.mts +4 -4
- package/dist/esm/api/resources/metadataFields/client/Client.mjs +29 -29
- package/dist/esm/api/resources/projectPermissions/client/Client.mjs +21 -21
- package/dist/esm/api/resources/projects/client/Client.mjs +35 -35
- package/dist/esm/api/resources/shares/client/Client.d.mts +4 -4
- package/dist/esm/api/resources/shares/client/Client.mjs +73 -73
- package/dist/esm/api/resources/shares/client/requests/AddAssetParams.d.mts +1 -1
- package/dist/esm/api/resources/shares/client/requests/CreateShareParams.d.mts +2 -2
- package/dist/esm/api/resources/shares/client/requests/UpdateShareParams.d.mts +1 -1
- package/dist/esm/api/resources/users/client/Client.mjs +8 -8
- package/dist/esm/api/resources/versionStacks/client/Client.d.mts +1 -1
- package/dist/esm/api/resources/versionStacks/client/Client.mjs +44 -44
- package/dist/esm/api/resources/versionStacks/client/requests/VersionStackCreateParams.d.mts +1 -1
- package/dist/esm/api/resources/webhooks/client/Client.mjs +35 -35
- package/dist/esm/api/resources/workspacePermissions/client/Client.mjs +21 -21
- package/dist/esm/api/resources/workspaces/client/Client.mjs +35 -35
- package/dist/esm/api/types/Account.d.mts +0 -2
- package/dist/esm/api/types/AuditLogwithIncludes.d.mts +4 -0
- package/dist/esm/api/types/AuditLogwithIncludes.mjs +4 -0
- package/dist/esm/api/types/Filters.d.mts +4 -0
- package/dist/esm/api/types/Filters.mjs +4 -0
- package/dist/esm/index.d.mts +4 -0
- package/dist/esm/index.mjs +6 -0
- package/dist/esm/oauth/BaseAuth.d.mts +66 -0
- package/dist/esm/oauth/BaseAuth.mjs +109 -0
- package/dist/esm/oauth/NativeAppAuth.d.mts +32 -0
- package/dist/esm/oauth/NativeAppAuth.mjs +31 -0
- package/dist/esm/oauth/SPAAuth.d.mts +38 -0
- package/dist/esm/oauth/SPAAuth.mjs +92 -0
- package/dist/esm/oauth/ServerToServerAuth.d.mts +17 -0
- package/dist/esm/oauth/ServerToServerAuth.mjs +45 -0
- package/dist/esm/oauth/TokenManager.d.mts +83 -0
- package/dist/esm/oauth/TokenManager.mjs +170 -0
- package/dist/esm/oauth/WebAppAuth.d.mts +29 -0
- package/dist/esm/oauth/WebAppAuth.mjs +84 -0
- package/dist/esm/oauth/errors.d.mts +41 -0
- package/dist/esm/oauth/errors.mjs +72 -0
- package/dist/esm/oauth/http.d.mts +70 -0
- package/dist/esm/oauth/http.mjs +274 -0
- package/dist/esm/oauth/index.d.mts +34 -0
- package/dist/esm/oauth/index.mjs +30 -0
- package/dist/esm/oauth/logger.d.mts +17 -0
- package/dist/esm/oauth/logger.mjs +15 -0
- package/dist/esm/oauth/pkce.d.mts +30 -0
- package/dist/esm/oauth/pkce.mjs +98 -0
- package/dist/esm/oauth/validation.d.mts +17 -0
- package/dist/esm/oauth/validation.mjs +51 -0
- package/dist/esm/version.d.mts +1 -1
- package/dist/esm/version.mjs +1 -1
- package/package.json +1 -1
- package/reference.md +9 -9
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal HTTP helper for Adobe IMS token endpoint calls.
|
|
3
|
+
*
|
|
4
|
+
* Supports configurable timeouts, retries with exponential backoff,
|
|
5
|
+
* rate-limit handling (429), and response validation.
|
|
6
|
+
*
|
|
7
|
+
* Allows configurable IMS base URL for staging/alternative environments,
|
|
8
|
+
* and optional fetch injection for proxy, TLS, and custom HTTP handling.
|
|
9
|
+
*/
|
|
10
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
11
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
12
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
13
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
14
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
15
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
16
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
import { AuthenticationError, ConfigurationError, NetworkError, RateLimitError, TokenExpiredError } from "./errors";
|
|
20
|
+
import { noopLogger } from "./logger";
|
|
21
|
+
// Default Adobe IMS base URL (production). Override via imsBaseUrl for staging.
|
|
22
|
+
export const DEFAULT_IMS_BASE_URL = "https://ims-na1.adobelogin.com";
|
|
23
|
+
/**
|
|
24
|
+
* Build authorize, token, and revoke URLs from an IMS base URL.
|
|
25
|
+
*
|
|
26
|
+
* @throws {Error} if the base URL does not use HTTPS.
|
|
27
|
+
*/
|
|
28
|
+
export function buildImsUrls(imsBaseUrl) {
|
|
29
|
+
if (!imsBaseUrl.toLowerCase().startsWith("https://")) {
|
|
30
|
+
throw new ConfigurationError(`imsBaseUrl must use HTTPS, got: ${imsBaseUrl}`);
|
|
31
|
+
}
|
|
32
|
+
const base = imsBaseUrl.replace(/\/$/, "");
|
|
33
|
+
return {
|
|
34
|
+
authorizeUrl: `${base}/ims/authorize/v2`,
|
|
35
|
+
tokenUrl: `${base}/ims/token/v3`,
|
|
36
|
+
revokeUrl: `${base}/ims/revoke`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const DEFAULT_URLS = buildImsUrls(DEFAULT_IMS_BASE_URL);
|
|
40
|
+
export const AUTHORIZE_URL = DEFAULT_URLS.authorizeUrl;
|
|
41
|
+
export const TOKEN_URL = DEFAULT_URLS.tokenUrl;
|
|
42
|
+
export const REVOKE_URL = DEFAULT_URLS.revokeUrl;
|
|
43
|
+
// OAuth 2.0 RFC 6749 specifies space-separated scopes
|
|
44
|
+
export const DEFAULT_SCOPES = "openid email profile offline_access additional_info.roles";
|
|
45
|
+
export const S2S_SCOPES = "openid AdobeID frame.s2s.all";
|
|
46
|
+
// Defaults
|
|
47
|
+
export const DEFAULT_TIMEOUT = 30000; // milliseconds
|
|
48
|
+
export const DEFAULT_MAX_RETRIES = 2;
|
|
49
|
+
const RETRY_BACKOFF_SCHEDULE = [1000, 2000, 5000, 10000]; // ms
|
|
50
|
+
const MAX_RETRY_AFTER_MS = 60000; // cap Retry-After to 60 s, matching Python
|
|
51
|
+
const RETRYABLE_STATUS_CODES = new Set([500, 502, 503, 504]);
|
|
52
|
+
/**
|
|
53
|
+
* Parse a Retry-After header value (seconds or HTTP-date).
|
|
54
|
+
* Returns milliseconds to wait, or undefined.
|
|
55
|
+
*/
|
|
56
|
+
function parseRetryAfter(header) {
|
|
57
|
+
if (!header)
|
|
58
|
+
return undefined;
|
|
59
|
+
const seconds = Number(header);
|
|
60
|
+
if (!isNaN(seconds))
|
|
61
|
+
return seconds * 1000;
|
|
62
|
+
const date = new Date(header);
|
|
63
|
+
if (isNaN(date.getTime()))
|
|
64
|
+
return undefined;
|
|
65
|
+
return Math.max(date.getTime() - Date.now(), 0);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate that a token response has the required fields.
|
|
69
|
+
*/
|
|
70
|
+
function validateTokenResponse(data, log) {
|
|
71
|
+
if (typeof data !== "object" || data === null) {
|
|
72
|
+
throw new AuthenticationError("invalid_response", "Token endpoint returned non-object response.");
|
|
73
|
+
}
|
|
74
|
+
const obj = data;
|
|
75
|
+
if (!("access_token" in obj) || typeof obj.access_token !== "string" || !obj.access_token) {
|
|
76
|
+
throw new AuthenticationError("invalid_response", "Token response missing or empty 'access_token'.");
|
|
77
|
+
}
|
|
78
|
+
const tokenType = obj.token_type;
|
|
79
|
+
if (tokenType == null) {
|
|
80
|
+
log.debug("Token response missing 'token_type' field.");
|
|
81
|
+
}
|
|
82
|
+
else if (typeof tokenType === "string" && tokenType.toLowerCase() !== "bearer") {
|
|
83
|
+
log.warn(`Unexpected token_type: ${tokenType} (expected 'bearer').`);
|
|
84
|
+
}
|
|
85
|
+
if (obj.expires_in !== undefined && obj.expires_in !== null) {
|
|
86
|
+
if (typeof obj.expires_in !== "number" || obj.expires_in <= 0) {
|
|
87
|
+
log.warn(`Invalid expires_in value (${obj.expires_in}), using default.`);
|
|
88
|
+
delete obj.expires_in;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return obj;
|
|
92
|
+
}
|
|
93
|
+
/** Sleep for a given number of milliseconds. */
|
|
94
|
+
function sleep(ms) {
|
|
95
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Send a POST to the Adobe IMS token endpoint.
|
|
99
|
+
*
|
|
100
|
+
* Rate-limit retries (429) are tracked separately from error retries
|
|
101
|
+
* (5xx / network failures), so a 429 does not consume the error retry
|
|
102
|
+
* budget and vice versa.
|
|
103
|
+
*
|
|
104
|
+
* @param data - Form-encoded body parameters.
|
|
105
|
+
* @param options - Timeout, retry, and logger options.
|
|
106
|
+
* @returns Parsed token response.
|
|
107
|
+
*/
|
|
108
|
+
export function tokenRequest(data_1) {
|
|
109
|
+
return __awaiter(this, arguments, void 0, function* (data, options = {}) {
|
|
110
|
+
var _a, _b, _c, _d, _e, _f;
|
|
111
|
+
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT;
|
|
112
|
+
const maxRetries = (_b = options.maxRetries) !== null && _b !== void 0 ? _b : DEFAULT_MAX_RETRIES;
|
|
113
|
+
const log = (_c = options.logger) !== null && _c !== void 0 ? _c : noopLogger;
|
|
114
|
+
const tokenUrl = (_d = options.tokenUrl) !== null && _d !== void 0 ? _d : TOKEN_URL;
|
|
115
|
+
const fetchFn = (_e = options.fetch) !== null && _e !== void 0 ? _e : (typeof globalThis.fetch !== "undefined" ? globalThis.fetch.bind(globalThis) : undefined);
|
|
116
|
+
if (fetchFn === undefined) {
|
|
117
|
+
throw new NetworkError("no fetch implementation available; please provide options.fetch or polyfill global fetch");
|
|
118
|
+
}
|
|
119
|
+
const body = new URLSearchParams(data).toString();
|
|
120
|
+
const maxErrorAttempts = maxRetries + 1;
|
|
121
|
+
const maxRateLimitAttempts = maxRetries + 1;
|
|
122
|
+
const maxTotal = maxErrorAttempts + maxRateLimitAttempts;
|
|
123
|
+
let errorAttempts = 0;
|
|
124
|
+
let rateLimitAttempts = 0;
|
|
125
|
+
for (let i = 0; i < maxTotal; i++) {
|
|
126
|
+
let response;
|
|
127
|
+
try {
|
|
128
|
+
log.debug(`Token request (errors=${errorAttempts}/${maxErrorAttempts}, rate_limits=${rateLimitAttempts}/${maxRateLimitAttempts})`);
|
|
129
|
+
const controller = new AbortController();
|
|
130
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
131
|
+
try {
|
|
132
|
+
response = yield fetchFn(tokenUrl, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
135
|
+
body,
|
|
136
|
+
signal: controller.signal,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
errorAttempts++;
|
|
145
|
+
log.warn(`Network error on attempt ${errorAttempts}: ${err}`);
|
|
146
|
+
if (errorAttempts < maxErrorAttempts) {
|
|
147
|
+
const backoff = RETRY_BACKOFF_SCHEDULE[Math.min(errorAttempts - 1, RETRY_BACKOFF_SCHEDULE.length - 1)];
|
|
148
|
+
log.debug(`Retrying in ${backoff}ms...`);
|
|
149
|
+
yield sleep(backoff);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError")) {
|
|
153
|
+
throw new NetworkError(`Token request timed out after ${timeout}ms (${maxErrorAttempts} attempts).`);
|
|
154
|
+
}
|
|
155
|
+
throw new NetworkError(`Token request failed after ${maxErrorAttempts} attempts: ${err instanceof Error ? err.message : String(err)}`);
|
|
156
|
+
}
|
|
157
|
+
// Rate limit handling — separate budget from error retries
|
|
158
|
+
if (response.status === 429) {
|
|
159
|
+
rateLimitAttempts++;
|
|
160
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
|
|
161
|
+
const retryAfterSec = retryAfterMs !== undefined ? retryAfterMs / 1000 : undefined;
|
|
162
|
+
log.warn(`Rate limited (429), attempt ${rateLimitAttempts}/${maxRateLimitAttempts}. Retry-After: ${retryAfterSec}s`);
|
|
163
|
+
if (rateLimitAttempts < maxRateLimitAttempts) {
|
|
164
|
+
const waitMs = Math.min(retryAfterMs !== null && retryAfterMs !== void 0 ? retryAfterMs : RETRY_BACKOFF_SCHEDULE[Math.min(rateLimitAttempts - 1, RETRY_BACKOFF_SCHEDULE.length - 1)], MAX_RETRY_AFTER_MS);
|
|
165
|
+
yield sleep(waitMs);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
throw new RateLimitError(retryAfterSec);
|
|
169
|
+
}
|
|
170
|
+
// Retryable server errors
|
|
171
|
+
if (RETRYABLE_STATUS_CODES.has(response.status)) {
|
|
172
|
+
errorAttempts++;
|
|
173
|
+
log.warn(`Server error ${response.status}, attempt ${errorAttempts}/${maxErrorAttempts}`);
|
|
174
|
+
if (errorAttempts < maxErrorAttempts) {
|
|
175
|
+
const backoff = RETRY_BACKOFF_SCHEDULE[Math.min(errorAttempts - 1, RETRY_BACKOFF_SCHEDULE.length - 1)];
|
|
176
|
+
yield sleep(backoff);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
// Fall through to error handling below
|
|
180
|
+
}
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
let errorCode = `http_${response.status}`;
|
|
183
|
+
let errorDesc;
|
|
184
|
+
try {
|
|
185
|
+
const errBody = (yield response.json());
|
|
186
|
+
errorCode = (_f = errBody.error) !== null && _f !== void 0 ? _f : errorCode;
|
|
187
|
+
errorDesc = errBody.error_description;
|
|
188
|
+
}
|
|
189
|
+
catch (_g) {
|
|
190
|
+
// ignore parse errors
|
|
191
|
+
}
|
|
192
|
+
// Detect expired refresh token
|
|
193
|
+
if ((errorCode === "invalid_grant" || errorCode === "invalid_token") &&
|
|
194
|
+
errorDesc &&
|
|
195
|
+
errorDesc.toLowerCase().includes("expired")) {
|
|
196
|
+
log.error(`Token expired: ${errorCode}`);
|
|
197
|
+
throw new TokenExpiredError(`${errorCode} — ${errorDesc}`);
|
|
198
|
+
}
|
|
199
|
+
log.error(`Authentication error: ${errorCode}`);
|
|
200
|
+
throw new AuthenticationError(errorCode, errorDesc);
|
|
201
|
+
}
|
|
202
|
+
// Success
|
|
203
|
+
let responseBody;
|
|
204
|
+
try {
|
|
205
|
+
responseBody = yield response.json();
|
|
206
|
+
}
|
|
207
|
+
catch (_h) {
|
|
208
|
+
throw new AuthenticationError("invalid_response", "Token endpoint returned non-JSON body");
|
|
209
|
+
}
|
|
210
|
+
const result = validateTokenResponse(responseBody, log);
|
|
211
|
+
log.info(`Token acquired (length=${result.access_token.length})`);
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
// Should not reach here, but just in case
|
|
215
|
+
throw new NetworkError("Token request failed after all retries.");
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Revoke an access or refresh token.
|
|
220
|
+
*
|
|
221
|
+
* For confidential clients (with client_secret), credentials are sent via
|
|
222
|
+
* HTTP Basic Auth per RFC 7009. For public clients, the client_id is sent
|
|
223
|
+
* as a query parameter.
|
|
224
|
+
*
|
|
225
|
+
* This is best-effort — errors are logged but not thrown.
|
|
226
|
+
*/
|
|
227
|
+
export function revokeRequest(token_1, clientId_1, clientSecret_1) {
|
|
228
|
+
return __awaiter(this, arguments, void 0, function* (token, clientId, clientSecret, options = {}) {
|
|
229
|
+
var _a, _b, _c, _d;
|
|
230
|
+
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT;
|
|
231
|
+
const log = (_b = options.logger) !== null && _b !== void 0 ? _b : noopLogger;
|
|
232
|
+
const revokeUrl = (_c = options.revokeUrl) !== null && _c !== void 0 ? _c : REVOKE_URL;
|
|
233
|
+
const fetchFn = (_d = options.fetch) !== null && _d !== void 0 ? _d : (typeof globalThis.fetch !== "undefined" ? globalThis.fetch.bind(globalThis) : undefined);
|
|
234
|
+
if (fetchFn === undefined) {
|
|
235
|
+
log.warn("Token revocation skipped: no fetch implementation available");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const headers = {
|
|
239
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
240
|
+
};
|
|
241
|
+
const data = { token };
|
|
242
|
+
let url = revokeUrl;
|
|
243
|
+
if (clientSecret) {
|
|
244
|
+
// Confidential client: use HTTP Basic Auth (RFC 7009 §2.1)
|
|
245
|
+
const credentials = btoa(`${clientId}:${clientSecret}`);
|
|
246
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// Public client: send client_id as query parameter
|
|
250
|
+
url = `${revokeUrl}?${new URLSearchParams({ client_id: clientId }).toString()}`;
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
const controller = new AbortController();
|
|
254
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
255
|
+
try {
|
|
256
|
+
const response = yield fetchFn(url, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
headers,
|
|
259
|
+
body: new URLSearchParams(data).toString(),
|
|
260
|
+
signal: controller.signal,
|
|
261
|
+
});
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
log.warn(`Token revocation returned status ${response.status}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
clearTimeout(timer);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
log.warn(`Token revocation failed: ${err}`);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frameio OAuth 2.0 authentication for the Frame.io V4 SDK.
|
|
3
|
+
*
|
|
4
|
+
* Provides four authentication flows:
|
|
5
|
+
*
|
|
6
|
+
* - {@link ServerToServerAuth} — client_credentials grant (backend services)
|
|
7
|
+
* - {@link WebAppAuth} — authorization_code grant (server-side apps)
|
|
8
|
+
* - {@link SPAAuth} — authorization_code + PKCE (browser apps)
|
|
9
|
+
* - {@link NativeAppAuth} — authorization_code + PKCE (desktop / mobile)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { FrameioClient, ServerToServerAuth } from 'frameio';
|
|
14
|
+
*
|
|
15
|
+
* const auth = new ServerToServerAuth({ clientId: '...', clientSecret: '...' });
|
|
16
|
+
* const client = new FrameioClient({ token: () => auth.getToken() });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
export type { BaseAuthOptions } from "./BaseAuth";
|
|
22
|
+
export { ServerToServerAuth } from "./ServerToServerAuth";
|
|
23
|
+
export type { ServerToServerAuthOptions } from "./ServerToServerAuth";
|
|
24
|
+
export { WebAppAuth } from "./WebAppAuth";
|
|
25
|
+
export type { WebAppAuthOptions } from "./WebAppAuth";
|
|
26
|
+
export { SPAAuth } from "./SPAAuth";
|
|
27
|
+
export type { SPAAuthOptions, AuthorizationUrlResult } from "./SPAAuth";
|
|
28
|
+
export { NativeAppAuth } from "./NativeAppAuth";
|
|
29
|
+
export type { NativeAppAuthOptions } from "./NativeAppAuth";
|
|
30
|
+
export { FrameioAuthError, AuthenticationError, TokenExpiredError, NetworkError, RateLimitError, PKCEError, ConfigurationError, } from "./errors";
|
|
31
|
+
export type { Logger } from "./logger";
|
|
32
|
+
export { noopLogger } from "./logger";
|
|
33
|
+
export { DEFAULT_IMS_BASE_URL, buildImsUrls } from "./http";
|
|
34
|
+
export type { TokenResponse, ExportedTokens, OnTokenRefreshed } from "./TokenManager";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frameio OAuth 2.0 authentication for the Frame.io V4 SDK.
|
|
3
|
+
*
|
|
4
|
+
* Provides four authentication flows:
|
|
5
|
+
*
|
|
6
|
+
* - {@link ServerToServerAuth} — client_credentials grant (backend services)
|
|
7
|
+
* - {@link WebAppAuth} — authorization_code grant (server-side apps)
|
|
8
|
+
* - {@link SPAAuth} — authorization_code + PKCE (browser apps)
|
|
9
|
+
* - {@link NativeAppAuth} — authorization_code + PKCE (desktop / mobile)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { FrameioClient, ServerToServerAuth } from 'frameio';
|
|
14
|
+
*
|
|
15
|
+
* const auth = new ServerToServerAuth({ clientId: '...', clientSecret: '...' });
|
|
16
|
+
* const client = new FrameioClient({ token: () => auth.getToken() });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
// Auth flows
|
|
22
|
+
export { ServerToServerAuth } from "./ServerToServerAuth";
|
|
23
|
+
export { WebAppAuth } from "./WebAppAuth";
|
|
24
|
+
export { SPAAuth } from "./SPAAuth";
|
|
25
|
+
export { NativeAppAuth } from "./NativeAppAuth";
|
|
26
|
+
// Errors
|
|
27
|
+
export { FrameioAuthError, AuthenticationError, TokenExpiredError, NetworkError, RateLimitError, PKCEError, ConfigurationError, } from "./errors";
|
|
28
|
+
export { noopLogger } from "./logger";
|
|
29
|
+
// Config
|
|
30
|
+
export { DEFAULT_IMS_BASE_URL, buildImsUrls } from "./http";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable logger interface for frameio-auth-sdk.
|
|
3
|
+
*
|
|
4
|
+
* Consumers can supply any object matching this interface (e.g. `console`,
|
|
5
|
+
* `winston`, `pino`) via the `logger` option on auth class constructors.
|
|
6
|
+
*
|
|
7
|
+
* The default is a no-op logger (silent).
|
|
8
|
+
*/
|
|
9
|
+
/** Logger interface accepted by all frameio-auth-sdk classes. */
|
|
10
|
+
export interface Logger {
|
|
11
|
+
debug(msg: string): void;
|
|
12
|
+
info(msg: string): void;
|
|
13
|
+
warn(msg: string): void;
|
|
14
|
+
error(msg: string): void;
|
|
15
|
+
}
|
|
16
|
+
/** Silent no-op logger — the default when no logger is provided. */
|
|
17
|
+
export declare const noopLogger: Logger;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable logger interface for frameio-auth-sdk.
|
|
3
|
+
*
|
|
4
|
+
* Consumers can supply any object matching this interface (e.g. `console`,
|
|
5
|
+
* `winston`, `pino`) via the `logger` option on auth class constructors.
|
|
6
|
+
*
|
|
7
|
+
* The default is a no-op logger (silent).
|
|
8
|
+
*/
|
|
9
|
+
/** Silent no-op logger — the default when no logger is provided. */
|
|
10
|
+
export const noopLogger = {
|
|
11
|
+
debug() { },
|
|
12
|
+
info() { },
|
|
13
|
+
warn() { },
|
|
14
|
+
error() { },
|
|
15
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities.
|
|
3
|
+
*
|
|
4
|
+
* Implements code_verifier generation and S256 code_challenge derivation
|
|
5
|
+
* per RFC 7636.
|
|
6
|
+
*
|
|
7
|
+
* Uses the Web Crypto API (`globalThis.crypto`) so it works in both
|
|
8
|
+
* Node.js (>=18) and browsers.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Generate a cryptographically random code verifier.
|
|
12
|
+
*
|
|
13
|
+
* Uses `globalThis.crypto.getRandomValues` which is available in
|
|
14
|
+
* browsers and Node.js >= 18.
|
|
15
|
+
*
|
|
16
|
+
* @param length - Length of the verifier (43–128 inclusive). Defaults to 128.
|
|
17
|
+
* @returns A random string of unreserved URI characters.
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateCodeVerifier(length?: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Derive the S256 code challenge from a code verifier.
|
|
22
|
+
*
|
|
23
|
+
* `code_challenge = BASE64URL(SHA256(code_verifier))`
|
|
24
|
+
*
|
|
25
|
+
* Uses `crypto.subtle.digest` which is available in browsers and Node.js >= 18.
|
|
26
|
+
*
|
|
27
|
+
* @param verifier - The code verifier string.
|
|
28
|
+
* @returns Base64url-encoded (no padding) SHA-256 hash.
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateCodeChallenge(verifier: string): Promise<string>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities.
|
|
3
|
+
*
|
|
4
|
+
* Implements code_verifier generation and S256 code_challenge derivation
|
|
5
|
+
* per RFC 7636.
|
|
6
|
+
*
|
|
7
|
+
* Uses the Web Crypto API (`globalThis.crypto`) so it works in both
|
|
8
|
+
* Node.js (>=18) and browsers.
|
|
9
|
+
*/
|
|
10
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
11
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
12
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
13
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
14
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
15
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
16
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
/** Unreserved URI characters allowed in a code verifier (RFC 7636 §4.1). */
|
|
20
|
+
const VERIFIER_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
21
|
+
const MIN_VERIFIER_LENGTH = 43;
|
|
22
|
+
const MAX_VERIFIER_LENGTH = 128;
|
|
23
|
+
const DEFAULT_VERIFIER_LENGTH = 128;
|
|
24
|
+
/**
|
|
25
|
+
* Generate a cryptographically random code verifier.
|
|
26
|
+
*
|
|
27
|
+
* Uses `globalThis.crypto.getRandomValues` which is available in
|
|
28
|
+
* browsers and Node.js >= 18.
|
|
29
|
+
*
|
|
30
|
+
* @param length - Length of the verifier (43–128 inclusive). Defaults to 128.
|
|
31
|
+
* @returns A random string of unreserved URI characters.
|
|
32
|
+
*/
|
|
33
|
+
export function generateCodeVerifier(length = DEFAULT_VERIFIER_LENGTH) {
|
|
34
|
+
if (length < MIN_VERIFIER_LENGTH || length > MAX_VERIFIER_LENGTH) {
|
|
35
|
+
throw new RangeError(`code_verifier length must be between ${MIN_VERIFIER_LENGTH} and ${MAX_VERIFIER_LENGTH}, got ${length}`);
|
|
36
|
+
}
|
|
37
|
+
// Rejection sampling: discard random bytes that would introduce modulo bias.
|
|
38
|
+
// Only accept values below the largest multiple of the charset length that
|
|
39
|
+
// fits in a byte (256), so every character has equal probability.
|
|
40
|
+
const charsetLen = VERIFIER_CHARS.length; // 66
|
|
41
|
+
const limit = 256 - (256 % charsetLen); // 252
|
|
42
|
+
const chars = new Array(length);
|
|
43
|
+
// Batch random bytes; refill when exhausted. Oversize slightly to absorb the
|
|
44
|
+
// ~1.6% rejection rate without a second refill in the common case.
|
|
45
|
+
let buf = new Uint8Array(Math.ceil(length * 1.1));
|
|
46
|
+
globalThis.crypto.getRandomValues(buf);
|
|
47
|
+
let bufPos = 0;
|
|
48
|
+
for (let i = 0; i < length;) {
|
|
49
|
+
if (bufPos >= buf.length) {
|
|
50
|
+
buf = new Uint8Array(length);
|
|
51
|
+
globalThis.crypto.getRandomValues(buf);
|
|
52
|
+
bufPos = 0;
|
|
53
|
+
}
|
|
54
|
+
const b = buf[bufPos++];
|
|
55
|
+
if (b < limit) {
|
|
56
|
+
chars[i] = VERIFIER_CHARS[b % charsetLen];
|
|
57
|
+
i++;
|
|
58
|
+
}
|
|
59
|
+
// else: discard and advance to next byte
|
|
60
|
+
}
|
|
61
|
+
return chars.join("");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Derive the S256 code challenge from a code verifier.
|
|
65
|
+
*
|
|
66
|
+
* `code_challenge = BASE64URL(SHA256(code_verifier))`
|
|
67
|
+
*
|
|
68
|
+
* Uses `crypto.subtle.digest` which is available in browsers and Node.js >= 18.
|
|
69
|
+
*
|
|
70
|
+
* @param verifier - The code verifier string.
|
|
71
|
+
* @returns Base64url-encoded (no padding) SHA-256 hash.
|
|
72
|
+
*/
|
|
73
|
+
export function generateCodeChallenge(verifier) {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
const data = new TextEncoder().encode(verifier);
|
|
76
|
+
const hashBuffer = yield globalThis.crypto.subtle.digest("SHA-256", data);
|
|
77
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
78
|
+
// Base64url encode (no padding)
|
|
79
|
+
let base64 = "";
|
|
80
|
+
const len = hashArray.length;
|
|
81
|
+
for (let i = 0; i < len; i += 3) {
|
|
82
|
+
const a = hashArray[i];
|
|
83
|
+
const b = i + 1 < len ? hashArray[i + 1] : 0;
|
|
84
|
+
const c = i + 2 < len ? hashArray[i + 2] : 0;
|
|
85
|
+
base64 += toBase64Url((a >> 2) & 0x3f);
|
|
86
|
+
base64 += toBase64Url(((a & 0x03) << 4) | ((b >> 4) & 0x0f));
|
|
87
|
+
if (i + 1 < len)
|
|
88
|
+
base64 += toBase64Url(((b & 0x0f) << 2) | ((c >> 6) & 0x03));
|
|
89
|
+
if (i + 2 < len)
|
|
90
|
+
base64 += toBase64Url(c & 0x3f);
|
|
91
|
+
}
|
|
92
|
+
return base64;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const BASE64URL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
96
|
+
function toBase64Url(index) {
|
|
97
|
+
return BASE64URL_CHARS[index];
|
|
98
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation helpers for frameio-auth-sdk.
|
|
3
|
+
*/
|
|
4
|
+
import type { ExportedTokens } from "./TokenManager";
|
|
5
|
+
/**
|
|
6
|
+
* Validate that a redirect URI uses HTTPS, except for loopback addresses
|
|
7
|
+
* where HTTP is allowed for local development.
|
|
8
|
+
*
|
|
9
|
+
* @throws {ConfigurationError} if the URI uses http:// with a non-loopback host.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateRedirectUriScheme(uri: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Validate the structure of an exported token object before importing.
|
|
14
|
+
*
|
|
15
|
+
* @throws {ConfigurationError} if any field has an unexpected type.
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateTokenImport(data: unknown): asserts data is ExportedTokens;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation helpers for frameio-auth-sdk.
|
|
3
|
+
*/
|
|
4
|
+
import { ConfigurationError } from "./errors";
|
|
5
|
+
/** Hosts that are allowed to use http:// redirect URIs. */
|
|
6
|
+
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
7
|
+
/**
|
|
8
|
+
* Validate that a redirect URI uses HTTPS, except for loopback addresses
|
|
9
|
+
* where HTTP is allowed for local development.
|
|
10
|
+
*
|
|
11
|
+
* @throws {ConfigurationError} if the URI uses http:// with a non-loopback host.
|
|
12
|
+
*/
|
|
13
|
+
export function validateRedirectUriScheme(uri) {
|
|
14
|
+
const lower = uri.toLowerCase();
|
|
15
|
+
if (!lower.startsWith("http://")) {
|
|
16
|
+
return; // https:// and custom schemes are always allowed
|
|
17
|
+
}
|
|
18
|
+
const hostPart = lower.slice("http://".length);
|
|
19
|
+
// Extract host, handling IPv6 brackets (e.g. [::1])
|
|
20
|
+
let host;
|
|
21
|
+
if (hostPart.startsWith("[")) {
|
|
22
|
+
const bracketEnd = hostPart.indexOf("]");
|
|
23
|
+
host = bracketEnd !== -1 ? hostPart.slice(0, bracketEnd + 1) : hostPart.split("/")[0];
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
host = hostPart.split("/")[0].split(":")[0];
|
|
27
|
+
}
|
|
28
|
+
if (!LOOPBACK_HOSTS.has(host)) {
|
|
29
|
+
throw new ConfigurationError(`Redirect URI must use HTTPS for non-loopback hosts, got: ${uri}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate the structure of an exported token object before importing.
|
|
34
|
+
*
|
|
35
|
+
* @throws {ConfigurationError} if any field has an unexpected type.
|
|
36
|
+
*/
|
|
37
|
+
export function validateTokenImport(data) {
|
|
38
|
+
if (typeof data !== "object" || data === null) {
|
|
39
|
+
throw new ConfigurationError("Token import data must be a non-null object.");
|
|
40
|
+
}
|
|
41
|
+
const obj = data;
|
|
42
|
+
if ("access_token" in obj && obj.access_token !== null && typeof obj.access_token !== "string") {
|
|
43
|
+
throw new ConfigurationError(`access_token must be a string or null, got ${typeof obj.access_token}`);
|
|
44
|
+
}
|
|
45
|
+
if ("refresh_token" in obj && obj.refresh_token !== null && typeof obj.refresh_token !== "string") {
|
|
46
|
+
throw new ConfigurationError(`refresh_token must be a string or null, got ${typeof obj.refresh_token}`);
|
|
47
|
+
}
|
|
48
|
+
if ("expires_at" in obj && typeof obj.expires_at !== "number") {
|
|
49
|
+
throw new ConfigurationError(`expires_at must be a number, got ${typeof obj.expires_at}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
package/dist/esm/version.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "
|
|
1
|
+
export declare const SDK_VERSION = "4.1.0";
|
package/dist/esm/version.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "
|
|
1
|
+
export const SDK_VERSION = "4.1.0";
|
package/package.json
CHANGED
package/reference.md
CHANGED
|
@@ -1642,7 +1642,7 @@ Create a new Version Stack under the parent folder. <br/>Rate Limits: 10 calls p
|
|
|
1642
1642
|
```typescript
|
|
1643
1643
|
await client.versionStacks.create("b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", "b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", {
|
|
1644
1644
|
data: {
|
|
1645
|
-
file_ids: ["
|
|
1645
|
+
file_ids: ["bf6a7751-b5e4-4f2e-b7e4-67ee53d740e8", "0604f9c0-36a4-4b59-ab89-f48e3b172dc0"]
|
|
1646
1646
|
}
|
|
1647
1647
|
});
|
|
1648
1648
|
|
|
@@ -3548,7 +3548,7 @@ await client.shares.update("b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", "b2702c44-c6d
|
|
|
3548
3548
|
access: "public",
|
|
3549
3549
|
description: "A descriptive summary of the share",
|
|
3550
3550
|
downloading_enabled: true,
|
|
3551
|
-
expiration: "2026-04-
|
|
3551
|
+
expiration: "2026-04-15T00:53:07Z",
|
|
3552
3552
|
name: "Share Name",
|
|
3553
3553
|
passphrase: "as!dfj39sd(*"
|
|
3554
3554
|
}
|
|
@@ -3990,7 +3990,7 @@ Add new asset share. <br/>Rate Limits: 10 calls per 1.00 minute(s) per account_u
|
|
|
3990
3990
|
```typescript
|
|
3991
3991
|
await client.shares.addAsset("b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", "b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", {
|
|
3992
3992
|
data: {
|
|
3993
|
-
asset_id: "
|
|
3993
|
+
asset_id: "f2631de7-7f1d-423e-825f-350106ad5332"
|
|
3994
3994
|
}
|
|
3995
3995
|
});
|
|
3996
3996
|
|
|
@@ -4174,9 +4174,9 @@ await client.shares.create("b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", "b2702c44-c6d
|
|
|
4174
4174
|
data: {
|
|
4175
4175
|
type: "asset",
|
|
4176
4176
|
access: "public",
|
|
4177
|
-
asset_ids: ["
|
|
4177
|
+
asset_ids: ["ead05077-22fb-4f16-af36-44fdabb31c7f", "4cb5fe30-845a-4fc5-beb0-5f6e1e684a7c"],
|
|
4178
4178
|
downloading_enabled: true,
|
|
4179
|
-
expiration: "2026-04-
|
|
4179
|
+
expiration: "2026-04-15T00:53:07Z",
|
|
4180
4180
|
name: "Share Name",
|
|
4181
4181
|
passphrase: "as!dfj39sd(*"
|
|
4182
4182
|
}
|
|
@@ -4262,16 +4262,16 @@ Update metadata values across multiple files. <br/>Rate Limits: 10 calls per 1.0
|
|
|
4262
4262
|
```typescript
|
|
4263
4263
|
await client.metadata.bulkUpdate("b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", "b2702c44-c6da-4bb6-8bbd-be6e547ccf1b", {
|
|
4264
4264
|
data: {
|
|
4265
|
-
file_ids: ["
|
|
4265
|
+
file_ids: ["055e9655-5445-425c-9158-4dcff313a8a1", "7e91a226-d1ac-4bea-afd1-9128c52ffc8d"],
|
|
4266
4266
|
values: [{
|
|
4267
|
-
field_definition_id: "
|
|
4267
|
+
field_definition_id: "174f1f07-3e07-4ad0-ac74-a27c0dc4a781",
|
|
4268
4268
|
value: [
|
|
4269
4269
|
{
|
|
4270
|
-
"id": "
|
|
4270
|
+
"id": "f1ef4511-a8ff-4645-b406-47e3362ad15a",
|
|
4271
4271
|
"type": "user"
|
|
4272
4272
|
},
|
|
4273
4273
|
{
|
|
4274
|
-
"id": "
|
|
4274
|
+
"id": "3a015842-e804-496a-abbe-577a332fb255",
|
|
4275
4275
|
"type": "account_user_group"
|
|
4276
4276
|
}
|
|
4277
4277
|
]
|