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.
Files changed (119) hide show
  1. package/README.md +159 -4
  2. package/dist/cjs/BaseClient.d.ts +4 -0
  3. package/dist/cjs/BaseClient.js +4 -2
  4. package/dist/cjs/api/resources/accountPermissions/client/Client.js +6 -6
  5. package/dist/cjs/api/resources/accounts/client/Client.js +13 -13
  6. package/dist/cjs/api/resources/comments/client/Client.js +48 -48
  7. package/dist/cjs/api/resources/files/client/Client.js +77 -77
  8. package/dist/cjs/api/resources/folders/client/Client.js +56 -56
  9. package/dist/cjs/api/resources/metadata/client/Client.d.ts +4 -4
  10. package/dist/cjs/api/resources/metadata/client/Client.js +18 -18
  11. package/dist/cjs/api/resources/metadata/client/requests/BulkUpdateMetadataParams.d.ts +4 -4
  12. package/dist/cjs/api/resources/metadataFields/client/Client.js +28 -28
  13. package/dist/cjs/api/resources/projectPermissions/client/Client.js +20 -20
  14. package/dist/cjs/api/resources/projects/client/Client.js +34 -34
  15. package/dist/cjs/api/resources/shares/client/Client.d.ts +4 -4
  16. package/dist/cjs/api/resources/shares/client/Client.js +72 -72
  17. package/dist/cjs/api/resources/shares/client/requests/AddAssetParams.d.ts +1 -1
  18. package/dist/cjs/api/resources/shares/client/requests/CreateShareParams.d.ts +2 -2
  19. package/dist/cjs/api/resources/shares/client/requests/UpdateShareParams.d.ts +1 -1
  20. package/dist/cjs/api/resources/users/client/Client.js +7 -7
  21. package/dist/cjs/api/resources/versionStacks/client/Client.d.ts +1 -1
  22. package/dist/cjs/api/resources/versionStacks/client/Client.js +43 -43
  23. package/dist/cjs/api/resources/versionStacks/client/requests/VersionStackCreateParams.d.ts +1 -1
  24. package/dist/cjs/api/resources/webhooks/client/Client.js +34 -34
  25. package/dist/cjs/api/resources/workspacePermissions/client/Client.js +20 -20
  26. package/dist/cjs/api/resources/workspaces/client/Client.js +34 -34
  27. package/dist/cjs/api/types/Account.d.ts +0 -2
  28. package/dist/cjs/api/types/AuditLogwithIncludes.d.ts +4 -0
  29. package/dist/cjs/api/types/AuditLogwithIncludes.js +4 -0
  30. package/dist/cjs/api/types/Filters.d.ts +4 -0
  31. package/dist/cjs/api/types/Filters.js +4 -0
  32. package/dist/cjs/index.d.ts +4 -0
  33. package/dist/cjs/index.js +21 -1
  34. package/dist/cjs/oauth/BaseAuth.d.ts +66 -0
  35. package/dist/cjs/oauth/BaseAuth.js +113 -0
  36. package/dist/cjs/oauth/NativeAppAuth.d.ts +32 -0
  37. package/dist/cjs/oauth/NativeAppAuth.js +35 -0
  38. package/dist/cjs/oauth/SPAAuth.d.ts +38 -0
  39. package/dist/cjs/oauth/SPAAuth.js +96 -0
  40. package/dist/cjs/oauth/ServerToServerAuth.d.ts +17 -0
  41. package/dist/cjs/oauth/ServerToServerAuth.js +49 -0
  42. package/dist/cjs/oauth/TokenManager.d.ts +83 -0
  43. package/dist/cjs/oauth/TokenManager.js +174 -0
  44. package/dist/cjs/oauth/WebAppAuth.d.ts +29 -0
  45. package/dist/cjs/oauth/WebAppAuth.js +88 -0
  46. package/dist/cjs/oauth/errors.d.ts +41 -0
  47. package/dist/cjs/oauth/errors.js +83 -0
  48. package/dist/cjs/oauth/http.d.ts +70 -0
  49. package/dist/cjs/oauth/http.js +280 -0
  50. package/dist/cjs/oauth/index.d.ts +34 -0
  51. package/dist/cjs/oauth/index.js +47 -0
  52. package/dist/cjs/oauth/logger.d.ts +17 -0
  53. package/dist/cjs/oauth/logger.js +18 -0
  54. package/dist/cjs/oauth/pkce.d.ts +30 -0
  55. package/dist/cjs/oauth/pkce.js +102 -0
  56. package/dist/cjs/oauth/validation.d.ts +17 -0
  57. package/dist/cjs/oauth/validation.js +55 -0
  58. package/dist/cjs/version.d.ts +1 -1
  59. package/dist/cjs/version.js +1 -1
  60. package/dist/esm/BaseClient.d.mts +4 -0
  61. package/dist/esm/BaseClient.mjs +4 -2
  62. package/dist/esm/api/resources/accountPermissions/client/Client.mjs +7 -7
  63. package/dist/esm/api/resources/accounts/client/Client.mjs +13 -13
  64. package/dist/esm/api/resources/comments/client/Client.mjs +49 -49
  65. package/dist/esm/api/resources/files/client/Client.mjs +78 -78
  66. package/dist/esm/api/resources/folders/client/Client.mjs +57 -57
  67. package/dist/esm/api/resources/metadata/client/Client.d.mts +4 -4
  68. package/dist/esm/api/resources/metadata/client/Client.mjs +19 -19
  69. package/dist/esm/api/resources/metadata/client/requests/BulkUpdateMetadataParams.d.mts +4 -4
  70. package/dist/esm/api/resources/metadataFields/client/Client.mjs +29 -29
  71. package/dist/esm/api/resources/projectPermissions/client/Client.mjs +21 -21
  72. package/dist/esm/api/resources/projects/client/Client.mjs +35 -35
  73. package/dist/esm/api/resources/shares/client/Client.d.mts +4 -4
  74. package/dist/esm/api/resources/shares/client/Client.mjs +73 -73
  75. package/dist/esm/api/resources/shares/client/requests/AddAssetParams.d.mts +1 -1
  76. package/dist/esm/api/resources/shares/client/requests/CreateShareParams.d.mts +2 -2
  77. package/dist/esm/api/resources/shares/client/requests/UpdateShareParams.d.mts +1 -1
  78. package/dist/esm/api/resources/users/client/Client.mjs +8 -8
  79. package/dist/esm/api/resources/versionStacks/client/Client.d.mts +1 -1
  80. package/dist/esm/api/resources/versionStacks/client/Client.mjs +44 -44
  81. package/dist/esm/api/resources/versionStacks/client/requests/VersionStackCreateParams.d.mts +1 -1
  82. package/dist/esm/api/resources/webhooks/client/Client.mjs +35 -35
  83. package/dist/esm/api/resources/workspacePermissions/client/Client.mjs +21 -21
  84. package/dist/esm/api/resources/workspaces/client/Client.mjs +35 -35
  85. package/dist/esm/api/types/Account.d.mts +0 -2
  86. package/dist/esm/api/types/AuditLogwithIncludes.d.mts +4 -0
  87. package/dist/esm/api/types/AuditLogwithIncludes.mjs +4 -0
  88. package/dist/esm/api/types/Filters.d.mts +4 -0
  89. package/dist/esm/api/types/Filters.mjs +4 -0
  90. package/dist/esm/index.d.mts +4 -0
  91. package/dist/esm/index.mjs +6 -0
  92. package/dist/esm/oauth/BaseAuth.d.mts +66 -0
  93. package/dist/esm/oauth/BaseAuth.mjs +109 -0
  94. package/dist/esm/oauth/NativeAppAuth.d.mts +32 -0
  95. package/dist/esm/oauth/NativeAppAuth.mjs +31 -0
  96. package/dist/esm/oauth/SPAAuth.d.mts +38 -0
  97. package/dist/esm/oauth/SPAAuth.mjs +92 -0
  98. package/dist/esm/oauth/ServerToServerAuth.d.mts +17 -0
  99. package/dist/esm/oauth/ServerToServerAuth.mjs +45 -0
  100. package/dist/esm/oauth/TokenManager.d.mts +83 -0
  101. package/dist/esm/oauth/TokenManager.mjs +170 -0
  102. package/dist/esm/oauth/WebAppAuth.d.mts +29 -0
  103. package/dist/esm/oauth/WebAppAuth.mjs +84 -0
  104. package/dist/esm/oauth/errors.d.mts +41 -0
  105. package/dist/esm/oauth/errors.mjs +72 -0
  106. package/dist/esm/oauth/http.d.mts +70 -0
  107. package/dist/esm/oauth/http.mjs +274 -0
  108. package/dist/esm/oauth/index.d.mts +34 -0
  109. package/dist/esm/oauth/index.mjs +30 -0
  110. package/dist/esm/oauth/logger.d.mts +17 -0
  111. package/dist/esm/oauth/logger.mjs +15 -0
  112. package/dist/esm/oauth/pkce.d.mts +30 -0
  113. package/dist/esm/oauth/pkce.mjs +98 -0
  114. package/dist/esm/oauth/validation.d.mts +17 -0
  115. package/dist/esm/oauth/validation.mjs +51 -0
  116. package/dist/esm/version.d.mts +1 -1
  117. package/dist/esm/version.mjs +1 -1
  118. package/package.json +1 -1
  119. package/reference.md +9 -9
@@ -0,0 +1,70 @@
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
+ import type { Logger } from "./logger";
11
+ import type { TokenResponse } from "./TokenManager";
12
+ export declare const DEFAULT_IMS_BASE_URL = "https://ims-na1.adobelogin.com";
13
+ /**
14
+ * Build authorize, token, and revoke URLs from an IMS base URL.
15
+ *
16
+ * @throws {Error} if the base URL does not use HTTPS.
17
+ */
18
+ export declare function buildImsUrls(imsBaseUrl: string): {
19
+ authorizeUrl: string;
20
+ tokenUrl: string;
21
+ revokeUrl: string;
22
+ };
23
+ export declare const AUTHORIZE_URL: string;
24
+ export declare const TOKEN_URL: string;
25
+ export declare const REVOKE_URL: string;
26
+ export declare const DEFAULT_SCOPES = "openid email profile offline_access additional_info.roles";
27
+ export declare const S2S_SCOPES = "openid AdobeID frame.s2s.all";
28
+ export declare const DEFAULT_TIMEOUT = 30000;
29
+ export declare const DEFAULT_MAX_RETRIES = 2;
30
+ /** Options for token requests. */
31
+ export interface TokenRequestOptions {
32
+ timeout?: number;
33
+ maxRetries?: number;
34
+ logger?: Logger;
35
+ /** Token endpoint URL (for alternative IMS environments). */
36
+ tokenUrl?: string;
37
+ /** Custom fetch implementation (for proxy, TLS, etc.). */
38
+ fetch?: typeof fetch;
39
+ }
40
+ /**
41
+ * Send a POST to the Adobe IMS token endpoint.
42
+ *
43
+ * Rate-limit retries (429) are tracked separately from error retries
44
+ * (5xx / network failures), so a 429 does not consume the error retry
45
+ * budget and vice versa.
46
+ *
47
+ * @param data - Form-encoded body parameters.
48
+ * @param options - Timeout, retry, and logger options.
49
+ * @returns Parsed token response.
50
+ */
51
+ export declare function tokenRequest(data: Record<string, string>, options?: TokenRequestOptions): Promise<TokenResponse>;
52
+ /** Options for revoke requests. */
53
+ export interface RevokeRequestOptions {
54
+ timeout?: number;
55
+ logger?: Logger;
56
+ /** Revoke endpoint URL (for alternative IMS environments). */
57
+ revokeUrl?: string;
58
+ /** Custom fetch implementation (for proxy, TLS, etc.). */
59
+ fetch?: typeof fetch;
60
+ }
61
+ /**
62
+ * Revoke an access or refresh token.
63
+ *
64
+ * For confidential clients (with client_secret), credentials are sent via
65
+ * HTTP Basic Auth per RFC 7009. For public clients, the client_id is sent
66
+ * as a query parameter.
67
+ *
68
+ * This is best-effort — errors are logged but not thrown.
69
+ */
70
+ export declare function revokeRequest(token: string, clientId: string, clientSecret?: string, options?: RevokeRequestOptions): Promise<void>;
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ /**
3
+ * Internal HTTP helper for Adobe IMS token endpoint calls.
4
+ *
5
+ * Supports configurable timeouts, retries with exponential backoff,
6
+ * rate-limit handling (429), and response validation.
7
+ *
8
+ * Allows configurable IMS base URL for staging/alternative environments,
9
+ * and optional fetch injection for proxy, TLS, and custom HTTP handling.
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_TIMEOUT = exports.S2S_SCOPES = exports.DEFAULT_SCOPES = exports.REVOKE_URL = exports.TOKEN_URL = exports.AUTHORIZE_URL = exports.DEFAULT_IMS_BASE_URL = void 0;
22
+ exports.buildImsUrls = buildImsUrls;
23
+ exports.tokenRequest = tokenRequest;
24
+ exports.revokeRequest = revokeRequest;
25
+ const errors_1 = require("./errors");
26
+ const logger_1 = require("./logger");
27
+ // Default Adobe IMS base URL (production). Override via imsBaseUrl for staging.
28
+ exports.DEFAULT_IMS_BASE_URL = "https://ims-na1.adobelogin.com";
29
+ /**
30
+ * Build authorize, token, and revoke URLs from an IMS base URL.
31
+ *
32
+ * @throws {Error} if the base URL does not use HTTPS.
33
+ */
34
+ function buildImsUrls(imsBaseUrl) {
35
+ if (!imsBaseUrl.toLowerCase().startsWith("https://")) {
36
+ throw new errors_1.ConfigurationError(`imsBaseUrl must use HTTPS, got: ${imsBaseUrl}`);
37
+ }
38
+ const base = imsBaseUrl.replace(/\/$/, "");
39
+ return {
40
+ authorizeUrl: `${base}/ims/authorize/v2`,
41
+ tokenUrl: `${base}/ims/token/v3`,
42
+ revokeUrl: `${base}/ims/revoke`,
43
+ };
44
+ }
45
+ const DEFAULT_URLS = buildImsUrls(exports.DEFAULT_IMS_BASE_URL);
46
+ exports.AUTHORIZE_URL = DEFAULT_URLS.authorizeUrl;
47
+ exports.TOKEN_URL = DEFAULT_URLS.tokenUrl;
48
+ exports.REVOKE_URL = DEFAULT_URLS.revokeUrl;
49
+ // OAuth 2.0 RFC 6749 specifies space-separated scopes
50
+ exports.DEFAULT_SCOPES = "openid email profile offline_access additional_info.roles";
51
+ exports.S2S_SCOPES = "openid AdobeID frame.s2s.all";
52
+ // Defaults
53
+ exports.DEFAULT_TIMEOUT = 30000; // milliseconds
54
+ exports.DEFAULT_MAX_RETRIES = 2;
55
+ const RETRY_BACKOFF_SCHEDULE = [1000, 2000, 5000, 10000]; // ms
56
+ const MAX_RETRY_AFTER_MS = 60000; // cap Retry-After to 60 s, matching Python
57
+ const RETRYABLE_STATUS_CODES = new Set([500, 502, 503, 504]);
58
+ /**
59
+ * Parse a Retry-After header value (seconds or HTTP-date).
60
+ * Returns milliseconds to wait, or undefined.
61
+ */
62
+ function parseRetryAfter(header) {
63
+ if (!header)
64
+ return undefined;
65
+ const seconds = Number(header);
66
+ if (!isNaN(seconds))
67
+ return seconds * 1000;
68
+ const date = new Date(header);
69
+ if (isNaN(date.getTime()))
70
+ return undefined;
71
+ return Math.max(date.getTime() - Date.now(), 0);
72
+ }
73
+ /**
74
+ * Validate that a token response has the required fields.
75
+ */
76
+ function validateTokenResponse(data, log) {
77
+ if (typeof data !== "object" || data === null) {
78
+ throw new errors_1.AuthenticationError("invalid_response", "Token endpoint returned non-object response.");
79
+ }
80
+ const obj = data;
81
+ if (!("access_token" in obj) || typeof obj.access_token !== "string" || !obj.access_token) {
82
+ throw new errors_1.AuthenticationError("invalid_response", "Token response missing or empty 'access_token'.");
83
+ }
84
+ const tokenType = obj.token_type;
85
+ if (tokenType == null) {
86
+ log.debug("Token response missing 'token_type' field.");
87
+ }
88
+ else if (typeof tokenType === "string" && tokenType.toLowerCase() !== "bearer") {
89
+ log.warn(`Unexpected token_type: ${tokenType} (expected 'bearer').`);
90
+ }
91
+ if (obj.expires_in !== undefined && obj.expires_in !== null) {
92
+ if (typeof obj.expires_in !== "number" || obj.expires_in <= 0) {
93
+ log.warn(`Invalid expires_in value (${obj.expires_in}), using default.`);
94
+ delete obj.expires_in;
95
+ }
96
+ }
97
+ return obj;
98
+ }
99
+ /** Sleep for a given number of milliseconds. */
100
+ function sleep(ms) {
101
+ return new Promise((resolve) => setTimeout(resolve, ms));
102
+ }
103
+ /**
104
+ * Send a POST to the Adobe IMS token endpoint.
105
+ *
106
+ * Rate-limit retries (429) are tracked separately from error retries
107
+ * (5xx / network failures), so a 429 does not consume the error retry
108
+ * budget and vice versa.
109
+ *
110
+ * @param data - Form-encoded body parameters.
111
+ * @param options - Timeout, retry, and logger options.
112
+ * @returns Parsed token response.
113
+ */
114
+ function tokenRequest(data_1) {
115
+ return __awaiter(this, arguments, void 0, function* (data, options = {}) {
116
+ var _a, _b, _c, _d, _e, _f;
117
+ const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : exports.DEFAULT_TIMEOUT;
118
+ const maxRetries = (_b = options.maxRetries) !== null && _b !== void 0 ? _b : exports.DEFAULT_MAX_RETRIES;
119
+ const log = (_c = options.logger) !== null && _c !== void 0 ? _c : logger_1.noopLogger;
120
+ const tokenUrl = (_d = options.tokenUrl) !== null && _d !== void 0 ? _d : exports.TOKEN_URL;
121
+ const fetchFn = (_e = options.fetch) !== null && _e !== void 0 ? _e : (typeof globalThis.fetch !== "undefined" ? globalThis.fetch.bind(globalThis) : undefined);
122
+ if (fetchFn === undefined) {
123
+ throw new errors_1.NetworkError("no fetch implementation available; please provide options.fetch or polyfill global fetch");
124
+ }
125
+ const body = new URLSearchParams(data).toString();
126
+ const maxErrorAttempts = maxRetries + 1;
127
+ const maxRateLimitAttempts = maxRetries + 1;
128
+ const maxTotal = maxErrorAttempts + maxRateLimitAttempts;
129
+ let errorAttempts = 0;
130
+ let rateLimitAttempts = 0;
131
+ for (let i = 0; i < maxTotal; i++) {
132
+ let response;
133
+ try {
134
+ log.debug(`Token request (errors=${errorAttempts}/${maxErrorAttempts}, rate_limits=${rateLimitAttempts}/${maxRateLimitAttempts})`);
135
+ const controller = new AbortController();
136
+ const timer = setTimeout(() => controller.abort(), timeout);
137
+ try {
138
+ response = yield fetchFn(tokenUrl, {
139
+ method: "POST",
140
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
141
+ body,
142
+ signal: controller.signal,
143
+ });
144
+ }
145
+ finally {
146
+ clearTimeout(timer);
147
+ }
148
+ }
149
+ catch (err) {
150
+ errorAttempts++;
151
+ log.warn(`Network error on attempt ${errorAttempts}: ${err}`);
152
+ if (errorAttempts < maxErrorAttempts) {
153
+ const backoff = RETRY_BACKOFF_SCHEDULE[Math.min(errorAttempts - 1, RETRY_BACKOFF_SCHEDULE.length - 1)];
154
+ log.debug(`Retrying in ${backoff}ms...`);
155
+ yield sleep(backoff);
156
+ continue;
157
+ }
158
+ if (err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError")) {
159
+ throw new errors_1.NetworkError(`Token request timed out after ${timeout}ms (${maxErrorAttempts} attempts).`);
160
+ }
161
+ throw new errors_1.NetworkError(`Token request failed after ${maxErrorAttempts} attempts: ${err instanceof Error ? err.message : String(err)}`);
162
+ }
163
+ // Rate limit handling — separate budget from error retries
164
+ if (response.status === 429) {
165
+ rateLimitAttempts++;
166
+ const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
167
+ const retryAfterSec = retryAfterMs !== undefined ? retryAfterMs / 1000 : undefined;
168
+ log.warn(`Rate limited (429), attempt ${rateLimitAttempts}/${maxRateLimitAttempts}. Retry-After: ${retryAfterSec}s`);
169
+ if (rateLimitAttempts < maxRateLimitAttempts) {
170
+ 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);
171
+ yield sleep(waitMs);
172
+ continue;
173
+ }
174
+ throw new errors_1.RateLimitError(retryAfterSec);
175
+ }
176
+ // Retryable server errors
177
+ if (RETRYABLE_STATUS_CODES.has(response.status)) {
178
+ errorAttempts++;
179
+ log.warn(`Server error ${response.status}, attempt ${errorAttempts}/${maxErrorAttempts}`);
180
+ if (errorAttempts < maxErrorAttempts) {
181
+ const backoff = RETRY_BACKOFF_SCHEDULE[Math.min(errorAttempts - 1, RETRY_BACKOFF_SCHEDULE.length - 1)];
182
+ yield sleep(backoff);
183
+ continue;
184
+ }
185
+ // Fall through to error handling below
186
+ }
187
+ if (!response.ok) {
188
+ let errorCode = `http_${response.status}`;
189
+ let errorDesc;
190
+ try {
191
+ const errBody = (yield response.json());
192
+ errorCode = (_f = errBody.error) !== null && _f !== void 0 ? _f : errorCode;
193
+ errorDesc = errBody.error_description;
194
+ }
195
+ catch (_g) {
196
+ // ignore parse errors
197
+ }
198
+ // Detect expired refresh token
199
+ if ((errorCode === "invalid_grant" || errorCode === "invalid_token") &&
200
+ errorDesc &&
201
+ errorDesc.toLowerCase().includes("expired")) {
202
+ log.error(`Token expired: ${errorCode}`);
203
+ throw new errors_1.TokenExpiredError(`${errorCode} — ${errorDesc}`);
204
+ }
205
+ log.error(`Authentication error: ${errorCode}`);
206
+ throw new errors_1.AuthenticationError(errorCode, errorDesc);
207
+ }
208
+ // Success
209
+ let responseBody;
210
+ try {
211
+ responseBody = yield response.json();
212
+ }
213
+ catch (_h) {
214
+ throw new errors_1.AuthenticationError("invalid_response", "Token endpoint returned non-JSON body");
215
+ }
216
+ const result = validateTokenResponse(responseBody, log);
217
+ log.info(`Token acquired (length=${result.access_token.length})`);
218
+ return result;
219
+ }
220
+ // Should not reach here, but just in case
221
+ throw new errors_1.NetworkError("Token request failed after all retries.");
222
+ });
223
+ }
224
+ /**
225
+ * Revoke an access or refresh token.
226
+ *
227
+ * For confidential clients (with client_secret), credentials are sent via
228
+ * HTTP Basic Auth per RFC 7009. For public clients, the client_id is sent
229
+ * as a query parameter.
230
+ *
231
+ * This is best-effort — errors are logged but not thrown.
232
+ */
233
+ function revokeRequest(token_1, clientId_1, clientSecret_1) {
234
+ return __awaiter(this, arguments, void 0, function* (token, clientId, clientSecret, options = {}) {
235
+ var _a, _b, _c, _d;
236
+ const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : exports.DEFAULT_TIMEOUT;
237
+ const log = (_b = options.logger) !== null && _b !== void 0 ? _b : logger_1.noopLogger;
238
+ const revokeUrl = (_c = options.revokeUrl) !== null && _c !== void 0 ? _c : exports.REVOKE_URL;
239
+ const fetchFn = (_d = options.fetch) !== null && _d !== void 0 ? _d : (typeof globalThis.fetch !== "undefined" ? globalThis.fetch.bind(globalThis) : undefined);
240
+ if (fetchFn === undefined) {
241
+ log.warn("Token revocation skipped: no fetch implementation available");
242
+ return;
243
+ }
244
+ const headers = {
245
+ "Content-Type": "application/x-www-form-urlencoded",
246
+ };
247
+ const data = { token };
248
+ let url = revokeUrl;
249
+ if (clientSecret) {
250
+ // Confidential client: use HTTP Basic Auth (RFC 7009 §2.1)
251
+ const credentials = btoa(`${clientId}:${clientSecret}`);
252
+ headers["Authorization"] = `Basic ${credentials}`;
253
+ }
254
+ else {
255
+ // Public client: send client_id as query parameter
256
+ url = `${revokeUrl}?${new URLSearchParams({ client_id: clientId }).toString()}`;
257
+ }
258
+ try {
259
+ const controller = new AbortController();
260
+ const timer = setTimeout(() => controller.abort(), timeout);
261
+ try {
262
+ const response = yield fetchFn(url, {
263
+ method: "POST",
264
+ headers,
265
+ body: new URLSearchParams(data).toString(),
266
+ signal: controller.signal,
267
+ });
268
+ if (!response.ok) {
269
+ log.warn(`Token revocation returned status ${response.status}`);
270
+ }
271
+ }
272
+ finally {
273
+ clearTimeout(timer);
274
+ }
275
+ }
276
+ catch (err) {
277
+ log.warn(`Token revocation failed: ${err}`);
278
+ }
279
+ });
280
+ }
@@ -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,47 @@
1
+ "use strict";
2
+ /**
3
+ * frameio OAuth 2.0 authentication for the Frame.io V4 SDK.
4
+ *
5
+ * Provides four authentication flows:
6
+ *
7
+ * - {@link ServerToServerAuth} — client_credentials grant (backend services)
8
+ * - {@link WebAppAuth} — authorization_code grant (server-side apps)
9
+ * - {@link SPAAuth} — authorization_code + PKCE (browser apps)
10
+ * - {@link NativeAppAuth} — authorization_code + PKCE (desktop / mobile)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { FrameioClient, ServerToServerAuth } from 'frameio';
15
+ *
16
+ * const auth = new ServerToServerAuth({ clientId: '...', clientSecret: '...' });
17
+ * const client = new FrameioClient({ token: () => auth.getToken() });
18
+ * ```
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.buildImsUrls = exports.DEFAULT_IMS_BASE_URL = exports.noopLogger = exports.ConfigurationError = exports.PKCEError = exports.RateLimitError = exports.NetworkError = exports.TokenExpiredError = exports.AuthenticationError = exports.FrameioAuthError = exports.NativeAppAuth = exports.SPAAuth = exports.WebAppAuth = exports.ServerToServerAuth = void 0;
24
+ // Auth flows
25
+ var ServerToServerAuth_1 = require("./ServerToServerAuth");
26
+ Object.defineProperty(exports, "ServerToServerAuth", { enumerable: true, get: function () { return ServerToServerAuth_1.ServerToServerAuth; } });
27
+ var WebAppAuth_1 = require("./WebAppAuth");
28
+ Object.defineProperty(exports, "WebAppAuth", { enumerable: true, get: function () { return WebAppAuth_1.WebAppAuth; } });
29
+ var SPAAuth_1 = require("./SPAAuth");
30
+ Object.defineProperty(exports, "SPAAuth", { enumerable: true, get: function () { return SPAAuth_1.SPAAuth; } });
31
+ var NativeAppAuth_1 = require("./NativeAppAuth");
32
+ Object.defineProperty(exports, "NativeAppAuth", { enumerable: true, get: function () { return NativeAppAuth_1.NativeAppAuth; } });
33
+ // Errors
34
+ var errors_1 = require("./errors");
35
+ Object.defineProperty(exports, "FrameioAuthError", { enumerable: true, get: function () { return errors_1.FrameioAuthError; } });
36
+ Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_1.AuthenticationError; } });
37
+ Object.defineProperty(exports, "TokenExpiredError", { enumerable: true, get: function () { return errors_1.TokenExpiredError; } });
38
+ Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return errors_1.NetworkError; } });
39
+ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_1.RateLimitError; } });
40
+ Object.defineProperty(exports, "PKCEError", { enumerable: true, get: function () { return errors_1.PKCEError; } });
41
+ Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_1.ConfigurationError; } });
42
+ var logger_1 = require("./logger");
43
+ Object.defineProperty(exports, "noopLogger", { enumerable: true, get: function () { return logger_1.noopLogger; } });
44
+ // Config
45
+ var http_1 = require("./http");
46
+ Object.defineProperty(exports, "DEFAULT_IMS_BASE_URL", { enumerable: true, get: function () { return http_1.DEFAULT_IMS_BASE_URL; } });
47
+ Object.defineProperty(exports, "buildImsUrls", { enumerable: true, get: function () { return http_1.buildImsUrls; } });
@@ -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,18 @@
1
+ "use strict";
2
+ /**
3
+ * Pluggable logger interface for frameio-auth-sdk.
4
+ *
5
+ * Consumers can supply any object matching this interface (e.g. `console`,
6
+ * `winston`, `pino`) via the `logger` option on auth class constructors.
7
+ *
8
+ * The default is a no-op logger (silent).
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.noopLogger = void 0;
12
+ /** Silent no-op logger — the default when no logger is provided. */
13
+ exports.noopLogger = {
14
+ debug() { },
15
+ info() { },
16
+ warn() { },
17
+ error() { },
18
+ };
@@ -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,102 @@
1
+ "use strict";
2
+ /**
3
+ * PKCE (Proof Key for Code Exchange) utilities.
4
+ *
5
+ * Implements code_verifier generation and S256 code_challenge derivation
6
+ * per RFC 7636.
7
+ *
8
+ * Uses the Web Crypto API (`globalThis.crypto`) so it works in both
9
+ * Node.js (>=18) and browsers.
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.generateCodeVerifier = generateCodeVerifier;
22
+ exports.generateCodeChallenge = generateCodeChallenge;
23
+ /** Unreserved URI characters allowed in a code verifier (RFC 7636 §4.1). */
24
+ const VERIFIER_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
25
+ const MIN_VERIFIER_LENGTH = 43;
26
+ const MAX_VERIFIER_LENGTH = 128;
27
+ const DEFAULT_VERIFIER_LENGTH = 128;
28
+ /**
29
+ * Generate a cryptographically random code verifier.
30
+ *
31
+ * Uses `globalThis.crypto.getRandomValues` which is available in
32
+ * browsers and Node.js >= 18.
33
+ *
34
+ * @param length - Length of the verifier (43–128 inclusive). Defaults to 128.
35
+ * @returns A random string of unreserved URI characters.
36
+ */
37
+ function generateCodeVerifier(length = DEFAULT_VERIFIER_LENGTH) {
38
+ if (length < MIN_VERIFIER_LENGTH || length > MAX_VERIFIER_LENGTH) {
39
+ throw new RangeError(`code_verifier length must be between ${MIN_VERIFIER_LENGTH} and ${MAX_VERIFIER_LENGTH}, got ${length}`);
40
+ }
41
+ // Rejection sampling: discard random bytes that would introduce modulo bias.
42
+ // Only accept values below the largest multiple of the charset length that
43
+ // fits in a byte (256), so every character has equal probability.
44
+ const charsetLen = VERIFIER_CHARS.length; // 66
45
+ const limit = 256 - (256 % charsetLen); // 252
46
+ const chars = new Array(length);
47
+ // Batch random bytes; refill when exhausted. Oversize slightly to absorb the
48
+ // ~1.6% rejection rate without a second refill in the common case.
49
+ let buf = new Uint8Array(Math.ceil(length * 1.1));
50
+ globalThis.crypto.getRandomValues(buf);
51
+ let bufPos = 0;
52
+ for (let i = 0; i < length;) {
53
+ if (bufPos >= buf.length) {
54
+ buf = new Uint8Array(length);
55
+ globalThis.crypto.getRandomValues(buf);
56
+ bufPos = 0;
57
+ }
58
+ const b = buf[bufPos++];
59
+ if (b < limit) {
60
+ chars[i] = VERIFIER_CHARS[b % charsetLen];
61
+ i++;
62
+ }
63
+ // else: discard and advance to next byte
64
+ }
65
+ return chars.join("");
66
+ }
67
+ /**
68
+ * Derive the S256 code challenge from a code verifier.
69
+ *
70
+ * `code_challenge = BASE64URL(SHA256(code_verifier))`
71
+ *
72
+ * Uses `crypto.subtle.digest` which is available in browsers and Node.js >= 18.
73
+ *
74
+ * @param verifier - The code verifier string.
75
+ * @returns Base64url-encoded (no padding) SHA-256 hash.
76
+ */
77
+ function generateCodeChallenge(verifier) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ const data = new TextEncoder().encode(verifier);
80
+ const hashBuffer = yield globalThis.crypto.subtle.digest("SHA-256", data);
81
+ const hashArray = new Uint8Array(hashBuffer);
82
+ // Base64url encode (no padding)
83
+ let base64 = "";
84
+ const len = hashArray.length;
85
+ for (let i = 0; i < len; i += 3) {
86
+ const a = hashArray[i];
87
+ const b = i + 1 < len ? hashArray[i + 1] : 0;
88
+ const c = i + 2 < len ? hashArray[i + 2] : 0;
89
+ base64 += toBase64Url((a >> 2) & 0x3f);
90
+ base64 += toBase64Url(((a & 0x03) << 4) | ((b >> 4) & 0x0f));
91
+ if (i + 1 < len)
92
+ base64 += toBase64Url(((b & 0x0f) << 2) | ((c >> 6) & 0x03));
93
+ if (i + 2 < len)
94
+ base64 += toBase64Url(c & 0x3f);
95
+ }
96
+ return base64;
97
+ });
98
+ }
99
+ const BASE64URL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
100
+ function toBase64Url(index) {
101
+ return BASE64URL_CHARS[index];
102
+ }
@@ -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;