@alter-ai/alter-sdk 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -47
- package/dist/index.cjs +408 -107
- package/dist/index.d.cts +232 -34
- package/dist/index.d.ts +232 -34
- package/dist/index.js +399 -104
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,40 +17,70 @@ var AlterSDKError = class extends Error {
|
|
|
17
17
|
return this.message;
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
-
var
|
|
20
|
+
var BackendError = class extends AlterSDKError {
|
|
21
21
|
constructor(message, details) {
|
|
22
22
|
super(message, details);
|
|
23
|
-
this.name = "
|
|
23
|
+
this.name = "BackendError";
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
constructor(message, policyError, details) {
|
|
26
|
+
var ReAuthRequiredError = class extends BackendError {
|
|
27
|
+
constructor(message, details) {
|
|
29
28
|
super(message, details);
|
|
30
|
-
this.name = "
|
|
31
|
-
this.policyError = policyError;
|
|
29
|
+
this.name = "ReAuthRequiredError";
|
|
32
30
|
}
|
|
33
31
|
};
|
|
34
|
-
var
|
|
32
|
+
var ConnectionExpiredError = class extends ReAuthRequiredError {
|
|
35
33
|
constructor(message, details) {
|
|
36
34
|
super(message, details);
|
|
37
|
-
this.name = "
|
|
35
|
+
this.name = "ConnectionExpiredError";
|
|
38
36
|
}
|
|
39
37
|
};
|
|
40
|
-
var
|
|
38
|
+
var ConnectionRevokedError = class extends ReAuthRequiredError {
|
|
41
39
|
connectionId;
|
|
42
40
|
constructor(message, connectionId, details) {
|
|
43
41
|
super(message, details);
|
|
44
|
-
this.name = "
|
|
42
|
+
this.name = "ConnectionRevokedError";
|
|
45
43
|
this.connectionId = connectionId;
|
|
46
44
|
}
|
|
47
45
|
};
|
|
46
|
+
var ConnectionDeletedError = class extends ReAuthRequiredError {
|
|
47
|
+
constructor(message, details) {
|
|
48
|
+
super(message, details);
|
|
49
|
+
this.name = "ConnectionDeletedError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var ConnectionNotFoundError = class extends BackendError {
|
|
53
|
+
constructor(message, details) {
|
|
54
|
+
super(message, details);
|
|
55
|
+
this.name = "ConnectionNotFoundError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var PolicyViolationError = class extends BackendError {
|
|
59
|
+
policyError;
|
|
60
|
+
constructor(message, policyError, details) {
|
|
61
|
+
super(message, details);
|
|
62
|
+
this.name = "PolicyViolationError";
|
|
63
|
+
this.policyError = policyError;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
48
66
|
var ConnectFlowError = class extends AlterSDKError {
|
|
49
67
|
constructor(message, details) {
|
|
50
68
|
super(message, details);
|
|
51
69
|
this.name = "ConnectFlowError";
|
|
52
70
|
}
|
|
53
71
|
};
|
|
72
|
+
var ConnectDeniedError = class extends ConnectFlowError {
|
|
73
|
+
constructor(message, details) {
|
|
74
|
+
super(message, details);
|
|
75
|
+
this.name = "ConnectDeniedError";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var ConnectConfigError = class extends ConnectFlowError {
|
|
79
|
+
constructor(message, details) {
|
|
80
|
+
super(message, details);
|
|
81
|
+
this.name = "ConnectConfigError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
54
84
|
var ConnectTimeoutError = class extends ConnectFlowError {
|
|
55
85
|
constructor(message, details) {
|
|
56
86
|
super(message, details);
|
|
@@ -67,6 +97,16 @@ var ProviderAPIError = class extends AlterSDKError {
|
|
|
67
97
|
this.responseBody = responseBody;
|
|
68
98
|
}
|
|
69
99
|
};
|
|
100
|
+
var ScopeReauthRequiredError = class extends ProviderAPIError {
|
|
101
|
+
connectionId;
|
|
102
|
+
providerId;
|
|
103
|
+
constructor(message, connectionId, providerId, statusCode, responseBody, details) {
|
|
104
|
+
super(message, statusCode, responseBody, details);
|
|
105
|
+
this.name = "ScopeReauthRequiredError";
|
|
106
|
+
this.connectionId = connectionId;
|
|
107
|
+
this.providerId = providerId;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
70
110
|
var NetworkError = class extends AlterSDKError {
|
|
71
111
|
constructor(message, details) {
|
|
72
112
|
super(message, details);
|
|
@@ -106,6 +146,8 @@ var TokenResponse = class _TokenResponse {
|
|
|
106
146
|
injectionFormat;
|
|
107
147
|
/** Extra credentials for multi-part auth (e.g. AWS SigV4 secret_key, region) */
|
|
108
148
|
additionalCredentials;
|
|
149
|
+
/** Additional injection rules for multi-header or query param auth */
|
|
150
|
+
additionalInjections;
|
|
109
151
|
constructor(data) {
|
|
110
152
|
this.tokenType = data.token_type ?? "Bearer";
|
|
111
153
|
this.expiresIn = data.expires_in ?? null;
|
|
@@ -121,6 +163,7 @@ var TokenResponse = class _TokenResponse {
|
|
|
121
163
|
} else {
|
|
122
164
|
this.additionalCredentials = null;
|
|
123
165
|
}
|
|
166
|
+
this.additionalInjections = data.additional_injections ?? null;
|
|
124
167
|
Object.freeze(this);
|
|
125
168
|
}
|
|
126
169
|
/**
|
|
@@ -166,7 +209,8 @@ var TokenResponse = class _TokenResponse {
|
|
|
166
209
|
expires_in: this.expiresIn,
|
|
167
210
|
expires_at: this.expiresAt?.toISOString() ?? null,
|
|
168
211
|
scopes: this.scopes,
|
|
169
|
-
connection_id: this.connectionId
|
|
212
|
+
connection_id: this.connectionId,
|
|
213
|
+
provider_id: this.providerId
|
|
170
214
|
};
|
|
171
215
|
}
|
|
172
216
|
/**
|
|
@@ -183,22 +227,24 @@ var TokenResponse = class _TokenResponse {
|
|
|
183
227
|
}
|
|
184
228
|
};
|
|
185
229
|
var ConnectionInfo = class {
|
|
186
|
-
|
|
230
|
+
connectionId;
|
|
187
231
|
providerId;
|
|
188
232
|
scopes;
|
|
189
233
|
accountIdentifier;
|
|
190
234
|
accountDisplayName;
|
|
191
235
|
status;
|
|
236
|
+
scopeMismatch;
|
|
192
237
|
expiresAt;
|
|
193
238
|
createdAt;
|
|
194
239
|
lastUsedAt;
|
|
195
240
|
constructor(data) {
|
|
196
|
-
this.
|
|
241
|
+
this.connectionId = data.connection_id;
|
|
197
242
|
this.providerId = data.provider_id;
|
|
198
243
|
this.scopes = data.scopes ?? [];
|
|
199
244
|
this.accountIdentifier = data.account_identifier ?? null;
|
|
200
245
|
this.accountDisplayName = data.account_display_name ?? null;
|
|
201
246
|
this.status = data.status;
|
|
247
|
+
this.scopeMismatch = data.scope_mismatch ?? false;
|
|
202
248
|
this.expiresAt = data.expires_at ?? null;
|
|
203
249
|
this.createdAt = data.created_at;
|
|
204
250
|
this.lastUsedAt = data.last_used_at ?? null;
|
|
@@ -206,19 +252,20 @@ var ConnectionInfo = class {
|
|
|
206
252
|
}
|
|
207
253
|
toJSON() {
|
|
208
254
|
return {
|
|
209
|
-
|
|
255
|
+
connection_id: this.connectionId,
|
|
210
256
|
provider_id: this.providerId,
|
|
211
257
|
scopes: this.scopes,
|
|
212
258
|
account_identifier: this.accountIdentifier,
|
|
213
259
|
account_display_name: this.accountDisplayName,
|
|
214
260
|
status: this.status,
|
|
261
|
+
scope_mismatch: this.scopeMismatch,
|
|
215
262
|
expires_at: this.expiresAt,
|
|
216
263
|
created_at: this.createdAt,
|
|
217
264
|
last_used_at: this.lastUsedAt
|
|
218
265
|
};
|
|
219
266
|
}
|
|
220
267
|
toString() {
|
|
221
|
-
return `ConnectionInfo(
|
|
268
|
+
return `ConnectionInfo(connection_id=${this.connectionId}, provider=${this.providerId}, status=${this.status})`;
|
|
222
269
|
}
|
|
223
270
|
};
|
|
224
271
|
var ConnectSession = class {
|
|
@@ -265,11 +312,23 @@ var ConnectResult = class {
|
|
|
265
312
|
providerId;
|
|
266
313
|
accountIdentifier;
|
|
267
314
|
scopes;
|
|
315
|
+
connectionPolicy;
|
|
268
316
|
constructor(data) {
|
|
269
317
|
this.connectionId = data.connection_id;
|
|
270
318
|
this.providerId = data.provider_id;
|
|
271
319
|
this.accountIdentifier = data.account_identifier ?? null;
|
|
272
320
|
this.scopes = data.scopes ?? [];
|
|
321
|
+
const rawPolicy = data.connection_policy;
|
|
322
|
+
if (rawPolicy) {
|
|
323
|
+
const raw = rawPolicy;
|
|
324
|
+
this.connectionPolicy = Object.freeze({
|
|
325
|
+
expiresAt: raw["expires_at"] !== void 0 ? raw["expires_at"] : rawPolicy.expiresAt ?? null,
|
|
326
|
+
createdBy: raw["created_by"] !== void 0 ? raw["created_by"] : rawPolicy.createdBy ?? null,
|
|
327
|
+
createdAt: raw["created_at"] !== void 0 ? raw["created_at"] : rawPolicy.createdAt ?? null
|
|
328
|
+
});
|
|
329
|
+
} else {
|
|
330
|
+
this.connectionPolicy = null;
|
|
331
|
+
}
|
|
273
332
|
Object.freeze(this);
|
|
274
333
|
}
|
|
275
334
|
toJSON() {
|
|
@@ -277,7 +336,12 @@ var ConnectResult = class {
|
|
|
277
336
|
connection_id: this.connectionId,
|
|
278
337
|
provider_id: this.providerId,
|
|
279
338
|
account_identifier: this.accountIdentifier,
|
|
280
|
-
scopes: this.scopes
|
|
339
|
+
scopes: this.scopes,
|
|
340
|
+
connection_policy: this.connectionPolicy ? {
|
|
341
|
+
expires_at: this.connectionPolicy.expiresAt ?? null,
|
|
342
|
+
created_by: this.connectionPolicy.createdBy ?? null,
|
|
343
|
+
created_at: this.connectionPolicy.createdAt ?? null
|
|
344
|
+
} : null
|
|
281
345
|
};
|
|
282
346
|
}
|
|
283
347
|
toString() {
|
|
@@ -291,7 +355,8 @@ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
|
291
355
|
"x-api-key",
|
|
292
356
|
"x-auth-token",
|
|
293
357
|
"x-amz-date",
|
|
294
|
-
"x-amz-content-sha256"
|
|
358
|
+
"x-amz-content-sha256",
|
|
359
|
+
"x-amz-security-token"
|
|
295
360
|
]);
|
|
296
361
|
var APICallAuditLog = class {
|
|
297
362
|
connectionId;
|
|
@@ -370,6 +435,7 @@ import { createHash, createHmac } from "crypto";
|
|
|
370
435
|
var ALGORITHM = "AWS4-HMAC-SHA256";
|
|
371
436
|
var AWS_HOST_RE = /^(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
|
|
372
437
|
var S3_VIRTUAL_HOST_RE = /^[^.]+\.s3\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
|
|
438
|
+
var VPCE_HOST_RE = /^vpce-[a-z0-9-]+\.(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.vpce\.amazonaws\.com$/;
|
|
373
439
|
function hmacSha256(key, message) {
|
|
374
440
|
return createHmac("sha256", key).update(message, "utf8").digest();
|
|
375
441
|
}
|
|
@@ -392,6 +458,10 @@ function detectServiceAndRegion(hostname) {
|
|
|
392
458
|
if (s3m?.groups) {
|
|
393
459
|
return { service: "s3", region: s3m.groups.region };
|
|
394
460
|
}
|
|
461
|
+
const vpcem = VPCE_HOST_RE.exec(lower);
|
|
462
|
+
if (vpcem?.groups) {
|
|
463
|
+
return { service: vpcem.groups.service, region: vpcem.groups.region };
|
|
464
|
+
}
|
|
395
465
|
return { service: null, region: null };
|
|
396
466
|
}
|
|
397
467
|
function deriveSigningKey(secretKey, dateStamp, region, service) {
|
|
@@ -426,18 +496,21 @@ function canonicalQueryString(query) {
|
|
|
426
496
|
]);
|
|
427
497
|
}
|
|
428
498
|
}
|
|
429
|
-
|
|
499
|
+
const sigv4Encode = (s) => encodeURIComponent(s).replace(
|
|
500
|
+
/[!'()*]/g,
|
|
501
|
+
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
|
502
|
+
);
|
|
503
|
+
const encoded = sorted.map(
|
|
504
|
+
([k, v]) => [sigv4Encode(k), sigv4Encode(v)]
|
|
505
|
+
);
|
|
506
|
+
encoded.sort((a, b) => {
|
|
430
507
|
if (a[0] < b[0]) return -1;
|
|
431
508
|
if (a[0] > b[0]) return 1;
|
|
432
509
|
if (a[1] < b[1]) return -1;
|
|
433
510
|
if (a[1] > b[1]) return 1;
|
|
434
511
|
return 0;
|
|
435
512
|
});
|
|
436
|
-
|
|
437
|
-
/[!'()*]/g,
|
|
438
|
-
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
|
439
|
-
);
|
|
440
|
-
return sorted.map(([k, v]) => `${sigv4Encode(k)}=${sigv4Encode(v)}`).join("&");
|
|
513
|
+
return encoded.map(([k, v]) => `${k}=${v}`).join("&");
|
|
441
514
|
}
|
|
442
515
|
function canonicalHeadersAndSigned(headers) {
|
|
443
516
|
const canonical = {};
|
|
@@ -456,11 +529,9 @@ function signAwsRequest(opts) {
|
|
|
456
529
|
const parsed = new URL(opts.url);
|
|
457
530
|
const hostname = parsed.hostname;
|
|
458
531
|
let { region, service } = opts;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (service == null) service = detected.service;
|
|
463
|
-
}
|
|
532
|
+
const detected = detectServiceAndRegion(hostname);
|
|
533
|
+
if (detected.region != null) region = detected.region;
|
|
534
|
+
if (detected.service != null) service = detected.service;
|
|
464
535
|
if (!region) {
|
|
465
536
|
throw new Error(
|
|
466
537
|
`Cannot determine AWS region from URL '${opts.url}'. Pass region explicitly via additional_credentials.`
|
|
@@ -543,10 +614,11 @@ function _extractAdditionalCredentials(token) {
|
|
|
543
614
|
return _additionalCredsStore.get(token);
|
|
544
615
|
}
|
|
545
616
|
var _fetch;
|
|
546
|
-
var SDK_VERSION = "0.
|
|
617
|
+
var SDK_VERSION = "0.6.0";
|
|
547
618
|
var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
|
|
548
619
|
var HTTP_FORBIDDEN = 403;
|
|
549
620
|
var HTTP_NOT_FOUND = 404;
|
|
621
|
+
var HTTP_GONE = 410;
|
|
550
622
|
var HTTP_BAD_REQUEST = 400;
|
|
551
623
|
var HTTP_UNAUTHORIZED = 401;
|
|
552
624
|
var HTTP_BAD_GATEWAY = 502;
|
|
@@ -602,6 +674,9 @@ var HttpClient = class {
|
|
|
602
674
|
};
|
|
603
675
|
if (options?.body !== void 0) {
|
|
604
676
|
init.body = options.body;
|
|
677
|
+
if (!mergedHeaders["Content-Type"]) {
|
|
678
|
+
mergedHeaders["Content-Type"] = "application/json";
|
|
679
|
+
}
|
|
605
680
|
} else if (options?.json !== void 0) {
|
|
606
681
|
init.body = JSON.stringify(options.json);
|
|
607
682
|
mergedHeaders["Content-Type"] = "application/json";
|
|
@@ -638,6 +713,8 @@ var AlterVault = class _AlterVault {
|
|
|
638
713
|
#closed = false;
|
|
639
714
|
/** Pending audit log promises (fire-and-forget) */
|
|
640
715
|
#auditPromises = /* @__PURE__ */ new Set();
|
|
716
|
+
/** JWT identity resolution: callable that returns user token */
|
|
717
|
+
#userTokenGetter = null;
|
|
641
718
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
642
719
|
// Public readonly properties (frozen by Object.freeze)
|
|
643
720
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -673,6 +750,7 @@ var AlterVault = class _AlterVault {
|
|
|
673
750
|
this.#actorName = options.actorName;
|
|
674
751
|
this.#actorVersion = options.actorVersion;
|
|
675
752
|
this.#clientType = options.clientType;
|
|
753
|
+
this.#userTokenGetter = options.userTokenGetter ?? null;
|
|
676
754
|
this.#framework = options.framework;
|
|
677
755
|
if (!_fetch) {
|
|
678
756
|
_fetch = globalThis.fetch;
|
|
@@ -847,6 +925,24 @@ ${contentHash}`;
|
|
|
847
925
|
}
|
|
848
926
|
return false;
|
|
849
927
|
}
|
|
928
|
+
/**
|
|
929
|
+
* Resolve a value_source to the actual value for injection.
|
|
930
|
+
*
|
|
931
|
+
* @param valueSource - "token" or "additional_credentials.<field>"
|
|
932
|
+
* @param accessToken - The main credential value
|
|
933
|
+
* @param additionalCreds - Additional credentials from vault
|
|
934
|
+
* @returns The resolved value, or null if not available
|
|
935
|
+
*/
|
|
936
|
+
static #resolveInjectionValue(valueSource, accessToken, additionalCreds) {
|
|
937
|
+
if (valueSource === "token") {
|
|
938
|
+
return accessToken;
|
|
939
|
+
}
|
|
940
|
+
if (valueSource.startsWith("additional_credentials.")) {
|
|
941
|
+
const field = valueSource.slice("additional_credentials.".length);
|
|
942
|
+
return additionalCreds?.[field] ?? null;
|
|
943
|
+
}
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
850
946
|
static async #safeParseJson(response) {
|
|
851
947
|
try {
|
|
852
948
|
return await response.clone().json();
|
|
@@ -868,12 +964,25 @@ ${contentHash}`;
|
|
|
868
964
|
}
|
|
869
965
|
if (response.status === HTTP_FORBIDDEN) {
|
|
870
966
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
967
|
+
if (errorData.error === "connection_expired") {
|
|
968
|
+
throw new ConnectionExpiredError(
|
|
969
|
+
errorData.message ?? "Connection expired per TTL policy",
|
|
970
|
+
errorData.details
|
|
971
|
+
);
|
|
972
|
+
}
|
|
871
973
|
throw new PolicyViolationError(
|
|
872
974
|
errorData.message ?? "Access denied by policy",
|
|
873
975
|
errorData.error,
|
|
874
976
|
errorData.details
|
|
875
977
|
);
|
|
876
978
|
}
|
|
979
|
+
if (response.status === HTTP_GONE) {
|
|
980
|
+
const errorData = await _AlterVault.#safeParseJson(response);
|
|
981
|
+
throw new ConnectionDeletedError(
|
|
982
|
+
errorData.message ?? "Connection has been deleted. A new connection_id will be issued on re-authorization.",
|
|
983
|
+
errorData
|
|
984
|
+
);
|
|
985
|
+
}
|
|
877
986
|
if (response.status === HTTP_NOT_FOUND) {
|
|
878
987
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
879
988
|
throw new ConnectionNotFoundError(
|
|
@@ -883,35 +992,35 @@ ${contentHash}`;
|
|
|
883
992
|
}
|
|
884
993
|
if (response.status === HTTP_BAD_REQUEST || response.status === HTTP_BAD_GATEWAY) {
|
|
885
994
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
886
|
-
if (
|
|
887
|
-
throw new
|
|
888
|
-
errorData.message ?? "
|
|
995
|
+
if (errorData.error === "connection_revoked") {
|
|
996
|
+
throw new ConnectionRevokedError(
|
|
997
|
+
errorData.message ?? "Connection has been revoked. User must re-authorize.",
|
|
889
998
|
errorData.connection_id,
|
|
890
999
|
errorData
|
|
891
1000
|
);
|
|
892
1001
|
}
|
|
893
|
-
throw new
|
|
1002
|
+
throw new BackendError(
|
|
894
1003
|
errorData.message ?? `Backend error ${response.status}`,
|
|
895
1004
|
errorData
|
|
896
1005
|
);
|
|
897
1006
|
}
|
|
898
1007
|
if (response.status === HTTP_UNAUTHORIZED) {
|
|
899
1008
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
900
|
-
throw new
|
|
1009
|
+
throw new BackendError(
|
|
901
1010
|
errorData.message ?? "Unauthorized \u2014 check your API key",
|
|
902
1011
|
errorData
|
|
903
1012
|
);
|
|
904
1013
|
}
|
|
905
1014
|
if (response.status === HTTP_INTERNAL_SERVER_ERROR || response.status === HTTP_SERVICE_UNAVAILABLE) {
|
|
906
1015
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
907
|
-
throw new
|
|
1016
|
+
throw new BackendError(
|
|
908
1017
|
errorData.message ?? `Backend unavailable (HTTP ${response.status})`,
|
|
909
1018
|
errorData
|
|
910
1019
|
);
|
|
911
1020
|
}
|
|
912
1021
|
if (response.status >= HTTP_CLIENT_ERROR_START) {
|
|
913
1022
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
914
|
-
throw new
|
|
1023
|
+
throw new BackendError(
|
|
915
1024
|
errorData.message ?? `Unexpected backend error (HTTP ${response.status})`,
|
|
916
1025
|
errorData
|
|
917
1026
|
);
|
|
@@ -923,24 +1032,52 @@ ${contentHash}`;
|
|
|
923
1032
|
* This is a private method. Tokens are NEVER exposed to developers.
|
|
924
1033
|
* Use request() instead, which handles tokens internally.
|
|
925
1034
|
*/
|
|
926
|
-
async #
|
|
1035
|
+
async #getUserToken() {
|
|
1036
|
+
if (!this.#userTokenGetter) {
|
|
1037
|
+
throw new AlterSDKError(
|
|
1038
|
+
"userTokenGetter is required for provider-based resolution. Pass userTokenGetter to AlterVault constructor."
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
const result = this.#userTokenGetter();
|
|
1042
|
+
const token = result instanceof Promise ? await result : result;
|
|
1043
|
+
if (!token || typeof token !== "string") {
|
|
1044
|
+
throw new AlterSDKError("userTokenGetter must return a non-empty string");
|
|
1045
|
+
}
|
|
1046
|
+
return token;
|
|
1047
|
+
}
|
|
1048
|
+
async #getToken(connectionId, reason, requestMetadata, runId, threadId, toolCallId, provider, account) {
|
|
927
1049
|
const actorHeaders = this.#getActorRequestHeaders(
|
|
928
1050
|
runId,
|
|
929
1051
|
threadId,
|
|
930
1052
|
toolCallId
|
|
931
1053
|
);
|
|
932
1054
|
let response;
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1055
|
+
let tokenBody;
|
|
1056
|
+
if (provider) {
|
|
1057
|
+
const userToken = await this.#getUserToken();
|
|
1058
|
+
tokenBody = {
|
|
1059
|
+
provider_id: provider,
|
|
1060
|
+
user_token: userToken,
|
|
1061
|
+
reason: reason ?? null,
|
|
1062
|
+
request: requestMetadata ?? null
|
|
1063
|
+
};
|
|
1064
|
+
if (account) {
|
|
1065
|
+
tokenBody.account = account;
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
tokenBody = {
|
|
1069
|
+
connection_id: connectionId,
|
|
1070
|
+
reason: reason ?? null,
|
|
1071
|
+
request: requestMetadata ?? null
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
938
1074
|
const tokenPath = "/sdk/token";
|
|
939
|
-
const
|
|
1075
|
+
const tokenBodyStr = JSON.stringify(tokenBody);
|
|
1076
|
+
const hmacHeaders = this.#computeHmacHeaders("POST", tokenPath, tokenBodyStr);
|
|
940
1077
|
try {
|
|
941
1078
|
response = await this.#alterClient.post(tokenPath, {
|
|
942
|
-
|
|
943
|
-
headers: { ...actorHeaders, ...hmacHeaders }
|
|
1079
|
+
body: tokenBodyStr,
|
|
1080
|
+
headers: { ...actorHeaders, ...hmacHeaders, "Content-Type": "application/json" }
|
|
944
1081
|
});
|
|
945
1082
|
} catch (error) {
|
|
946
1083
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -955,7 +1092,7 @@ ${contentHash}`;
|
|
|
955
1092
|
{ base_url: this.baseUrl }
|
|
956
1093
|
);
|
|
957
1094
|
}
|
|
958
|
-
throw new
|
|
1095
|
+
throw new BackendError(
|
|
959
1096
|
`Failed to retrieve token: ${error instanceof Error ? error.message : String(error)}`,
|
|
960
1097
|
{ connection_id: connectionId, error: String(error) }
|
|
961
1098
|
);
|
|
@@ -964,15 +1101,16 @@ ${contentHash}`;
|
|
|
964
1101
|
await this.#handleErrorResponse(response);
|
|
965
1102
|
const tokenData = await response.json();
|
|
966
1103
|
const typedData = tokenData;
|
|
1104
|
+
const scopeMismatch = typedData.scope_mismatch ?? false;
|
|
967
1105
|
const tokenResponse = new TokenResponse(typedData);
|
|
968
1106
|
if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(tokenResponse.injectionHeader)) {
|
|
969
|
-
throw new
|
|
1107
|
+
throw new BackendError(
|
|
970
1108
|
`Backend returned invalid injection_header: ${tokenResponse.injectionHeader}`,
|
|
971
1109
|
{ connectionId: String(connectionId) }
|
|
972
1110
|
);
|
|
973
1111
|
}
|
|
974
1112
|
if (/[\r\n\x00]/.test(tokenResponse.injectionFormat)) {
|
|
975
|
-
throw new
|
|
1113
|
+
throw new BackendError(
|
|
976
1114
|
`Backend returned invalid injection_format (contains control characters)`,
|
|
977
1115
|
{ connectionId: String(connectionId) }
|
|
978
1116
|
);
|
|
@@ -981,7 +1119,7 @@ ${contentHash}`;
|
|
|
981
1119
|
if (typedData.additional_credentials) {
|
|
982
1120
|
_storeAdditionalCredentials(tokenResponse, typedData.additional_credentials);
|
|
983
1121
|
}
|
|
984
|
-
return tokenResponse;
|
|
1122
|
+
return { tokenResponse, scopeMismatch };
|
|
985
1123
|
}
|
|
986
1124
|
/**
|
|
987
1125
|
* Log an API call to the backend audit endpoint (INTERNAL).
|
|
@@ -1011,10 +1149,11 @@ ${contentHash}`;
|
|
|
1011
1149
|
const actorHeaders = this.#getActorRequestHeaders(params.runId);
|
|
1012
1150
|
const auditPath = "/sdk/oauth/audit/api-call";
|
|
1013
1151
|
const auditBody = sanitized;
|
|
1014
|
-
const
|
|
1152
|
+
const auditBodyStr = JSON.stringify(auditBody);
|
|
1153
|
+
const auditHmac = this.#computeHmacHeaders("POST", auditPath, auditBodyStr);
|
|
1015
1154
|
const response = await this.#alterClient.post(auditPath, {
|
|
1016
|
-
|
|
1017
|
-
headers: { ...actorHeaders, ...auditHmac }
|
|
1155
|
+
body: auditBodyStr,
|
|
1156
|
+
headers: { ...actorHeaders, ...auditHmac, "Content-Type": "application/json" }
|
|
1018
1157
|
});
|
|
1019
1158
|
this.#cacheActorIdFromResponse(response);
|
|
1020
1159
|
if (!response.ok) {
|
|
@@ -1086,12 +1225,22 @@ ${contentHash}`;
|
|
|
1086
1225
|
"SDK instance has been closed. Create a new AlterVault instance to make requests."
|
|
1087
1226
|
);
|
|
1088
1227
|
}
|
|
1228
|
+
const provider = options?.provider;
|
|
1229
|
+
const account = options?.account;
|
|
1230
|
+
if (!connectionId && !provider) {
|
|
1231
|
+
throw new AlterSDKError("Provide connectionId or options.provider");
|
|
1232
|
+
}
|
|
1233
|
+
if (connectionId && provider) {
|
|
1234
|
+
throw new AlterSDKError("Cannot provide both connectionId and options.provider");
|
|
1235
|
+
}
|
|
1236
|
+
const effectiveConnectionId = connectionId ?? null;
|
|
1237
|
+
let currentUrl = url;
|
|
1089
1238
|
const runId = options?.runId ?? randomUUID();
|
|
1090
1239
|
const methodStr = String(method).toUpperCase();
|
|
1091
|
-
const urlLower =
|
|
1240
|
+
const urlLower = currentUrl.toLowerCase();
|
|
1092
1241
|
if (!ALLOWED_URL_SCHEMES.some((scheme) => urlLower.startsWith(scheme))) {
|
|
1093
1242
|
throw new AlterSDKError(
|
|
1094
|
-
`URL must start with https:// or http://, got: ${
|
|
1243
|
+
`URL must start with https:// or http://, got: ${currentUrl.slice(0, 50)}`
|
|
1095
1244
|
);
|
|
1096
1245
|
}
|
|
1097
1246
|
if (options?.pathParams && Object.keys(options.pathParams).length > 0) {
|
|
@@ -1100,7 +1249,7 @@ ${contentHash}`;
|
|
|
1100
1249
|
encodedParams[key] = encodeURIComponent(String(value));
|
|
1101
1250
|
}
|
|
1102
1251
|
try {
|
|
1103
|
-
let resolvedUrl =
|
|
1252
|
+
let resolvedUrl = currentUrl;
|
|
1104
1253
|
for (const [key, value] of Object.entries(encodedParams)) {
|
|
1105
1254
|
const placeholder = `{${key}}`;
|
|
1106
1255
|
if (!resolvedUrl.includes(placeholder)) {
|
|
@@ -1111,26 +1260,28 @@ ${contentHash}`;
|
|
|
1111
1260
|
const remaining = resolvedUrl.match(/\{(\w+)\}/);
|
|
1112
1261
|
if (remaining) {
|
|
1113
1262
|
throw new AlterSDKError(
|
|
1114
|
-
`Invalid URL template or missing path_params: '${remaining[1]}'. URL: ${
|
|
1263
|
+
`Invalid URL template or missing path_params: '${remaining[1]}'. URL: ${currentUrl}, path_params: ${JSON.stringify(options.pathParams)}`
|
|
1115
1264
|
);
|
|
1116
1265
|
}
|
|
1117
|
-
|
|
1266
|
+
currentUrl = resolvedUrl;
|
|
1118
1267
|
} catch (error) {
|
|
1119
1268
|
if (error instanceof AlterSDKError) {
|
|
1120
1269
|
throw error;
|
|
1121
1270
|
}
|
|
1122
1271
|
throw new AlterSDKError(
|
|
1123
|
-
`Invalid URL template or missing path_params: ${error instanceof Error ? error.message : String(error)}. URL: ${
|
|
1272
|
+
`Invalid URL template or missing path_params: ${error instanceof Error ? error.message : String(error)}. URL: ${currentUrl}, path_params: ${JSON.stringify(options.pathParams)}`
|
|
1124
1273
|
);
|
|
1125
1274
|
}
|
|
1126
1275
|
}
|
|
1127
|
-
const tokenResponse = await this.#getToken(
|
|
1128
|
-
|
|
1276
|
+
const { tokenResponse, scopeMismatch } = await this.#getToken(
|
|
1277
|
+
effectiveConnectionId,
|
|
1129
1278
|
options?.reason,
|
|
1130
|
-
{ method: methodStr, url },
|
|
1279
|
+
{ method: methodStr, url: currentUrl },
|
|
1131
1280
|
runId,
|
|
1132
1281
|
options?.threadId,
|
|
1133
|
-
options?.toolCallId
|
|
1282
|
+
options?.toolCallId,
|
|
1283
|
+
provider,
|
|
1284
|
+
account
|
|
1134
1285
|
);
|
|
1135
1286
|
const requestHeaders = options?.extraHeaders ? { ...options.extraHeaders } : {};
|
|
1136
1287
|
const accessToken = _extractAccessToken(tokenResponse);
|
|
@@ -1147,14 +1298,14 @@ ${contentHash}`;
|
|
|
1147
1298
|
}
|
|
1148
1299
|
}
|
|
1149
1300
|
if (!additionalCreds.secret_key) {
|
|
1150
|
-
throw new
|
|
1301
|
+
throw new BackendError(
|
|
1151
1302
|
"AWS SigV4 credential is missing secret_key in additional_credentials. Re-store the credential with both Access Key ID and Secret Access Key.",
|
|
1152
|
-
{ connection_id:
|
|
1303
|
+
{ connection_id: effectiveConnectionId }
|
|
1153
1304
|
);
|
|
1154
1305
|
}
|
|
1155
1306
|
const awsHeaders = signAwsRequest({
|
|
1156
1307
|
method: methodStr,
|
|
1157
|
-
url,
|
|
1308
|
+
url: currentUrl,
|
|
1158
1309
|
headers: requestHeaders,
|
|
1159
1310
|
body: sigv4BodyStr,
|
|
1160
1311
|
accessKeyId,
|
|
@@ -1173,6 +1324,25 @@ ${contentHash}`;
|
|
|
1173
1324
|
}
|
|
1174
1325
|
requestHeaders[tokenResponse.injectionHeader] = tokenResponse.injectionFormat.replace("{token}", accessToken);
|
|
1175
1326
|
}
|
|
1327
|
+
const auditUrl = currentUrl;
|
|
1328
|
+
if (tokenResponse.additionalInjections && !isSigV4) {
|
|
1329
|
+
for (const rule of tokenResponse.additionalInjections) {
|
|
1330
|
+
const value = _AlterVault.#resolveInjectionValue(
|
|
1331
|
+
rule.value_source,
|
|
1332
|
+
accessToken,
|
|
1333
|
+
additionalCreds
|
|
1334
|
+
);
|
|
1335
|
+
if (!value) continue;
|
|
1336
|
+
if (!/^[A-Za-z][A-Za-z0-9\-_]*$/.test(rule.key)) continue;
|
|
1337
|
+
if (rule.target === "header") {
|
|
1338
|
+
requestHeaders[rule.key] = value;
|
|
1339
|
+
} else if (rule.target === "query_param") {
|
|
1340
|
+
const urlObj = new URL(currentUrl);
|
|
1341
|
+
urlObj.searchParams.set(rule.key, value);
|
|
1342
|
+
currentUrl = urlObj.toString();
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1176
1346
|
if (!requestHeaders["User-Agent"]) {
|
|
1177
1347
|
requestHeaders["User-Agent"] = SDK_USER_AGENT;
|
|
1178
1348
|
}
|
|
@@ -1180,13 +1350,13 @@ ${contentHash}`;
|
|
|
1180
1350
|
let response;
|
|
1181
1351
|
try {
|
|
1182
1352
|
if (isSigV4 && sigv4BodyStr != null) {
|
|
1183
|
-
response = await this.#providerClient.request(methodStr,
|
|
1353
|
+
response = await this.#providerClient.request(methodStr, currentUrl, {
|
|
1184
1354
|
body: sigv4BodyStr,
|
|
1185
1355
|
headers: requestHeaders,
|
|
1186
1356
|
params: options?.queryParams
|
|
1187
1357
|
});
|
|
1188
1358
|
} else {
|
|
1189
|
-
response = await this.#providerClient.request(methodStr,
|
|
1359
|
+
response = await this.#providerClient.request(methodStr, currentUrl, {
|
|
1190
1360
|
json: options?.json,
|
|
1191
1361
|
headers: requestHeaders,
|
|
1192
1362
|
params: options?.queryParams
|
|
@@ -1197,18 +1367,18 @@ ${contentHash}`;
|
|
|
1197
1367
|
throw new TimeoutError(
|
|
1198
1368
|
`Provider API request timed out: ${error instanceof Error ? error.message : String(error)}`,
|
|
1199
1369
|
{
|
|
1200
|
-
connection_id:
|
|
1370
|
+
connection_id: effectiveConnectionId,
|
|
1201
1371
|
method: methodStr,
|
|
1202
|
-
url
|
|
1372
|
+
url: currentUrl
|
|
1203
1373
|
}
|
|
1204
1374
|
);
|
|
1205
1375
|
}
|
|
1206
1376
|
throw new NetworkError(
|
|
1207
1377
|
`Failed to call provider API: ${error instanceof Error ? error.message : String(error)}`,
|
|
1208
1378
|
{
|
|
1209
|
-
connection_id:
|
|
1379
|
+
connection_id: effectiveConnectionId,
|
|
1210
1380
|
method: methodStr,
|
|
1211
|
-
url,
|
|
1381
|
+
url: currentUrl,
|
|
1212
1382
|
error: String(error)
|
|
1213
1383
|
}
|
|
1214
1384
|
);
|
|
@@ -1216,6 +1386,13 @@ ${contentHash}`;
|
|
|
1216
1386
|
const latencyMs = Date.now() - startTime;
|
|
1217
1387
|
const sigv4Sensitive = /* @__PURE__ */ new Set(["authorization", "x-amz-date", "x-amz-content-sha256"]);
|
|
1218
1388
|
const stripHeaders = isSigV4 ? sigv4Sensitive : /* @__PURE__ */ new Set([injectionHeaderLower]);
|
|
1389
|
+
if (tokenResponse.additionalInjections && !isSigV4) {
|
|
1390
|
+
for (const rule of tokenResponse.additionalInjections) {
|
|
1391
|
+
if (rule.target === "header") {
|
|
1392
|
+
stripHeaders.add(rule.key.toLowerCase());
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1219
1396
|
const auditHeaders = {};
|
|
1220
1397
|
for (const [key, value] of Object.entries(requestHeaders)) {
|
|
1221
1398
|
if (!stripHeaders.has(key.toLowerCase())) {
|
|
@@ -1229,9 +1406,9 @@ ${contentHash}`;
|
|
|
1229
1406
|
});
|
|
1230
1407
|
this.#scheduleAuditLog({
|
|
1231
1408
|
connectionId: tokenResponse.connectionId,
|
|
1232
|
-
providerId: tokenResponse.providerId ||
|
|
1409
|
+
providerId: tokenResponse.providerId || effectiveConnectionId || "",
|
|
1233
1410
|
method: methodStr,
|
|
1234
|
-
url,
|
|
1411
|
+
url: auditUrl,
|
|
1235
1412
|
requestHeaders: auditHeaders,
|
|
1236
1413
|
requestBody: options?.json ?? null,
|
|
1237
1414
|
responseStatus: response.status,
|
|
@@ -1244,14 +1421,29 @@ ${contentHash}`;
|
|
|
1244
1421
|
toolCallId: options?.toolCallId ?? null
|
|
1245
1422
|
});
|
|
1246
1423
|
if (response.status >= HTTP_CLIENT_ERROR_START) {
|
|
1424
|
+
if (response.status === HTTP_FORBIDDEN && scopeMismatch) {
|
|
1425
|
+
throw new ScopeReauthRequiredError(
|
|
1426
|
+
"Provider API returned 403 and the connection's scopes don't match the provider config. The user needs to re-authorize to grant updated permissions.",
|
|
1427
|
+
tokenResponse.connectionId,
|
|
1428
|
+
tokenResponse.providerId,
|
|
1429
|
+
response.status,
|
|
1430
|
+
responseBody,
|
|
1431
|
+
{
|
|
1432
|
+
connection_id: tokenResponse.connectionId,
|
|
1433
|
+
provider_id: tokenResponse.providerId,
|
|
1434
|
+
method: methodStr,
|
|
1435
|
+
url: currentUrl
|
|
1436
|
+
}
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1247
1439
|
throw new ProviderAPIError(
|
|
1248
1440
|
`Provider API returned error ${response.status}`,
|
|
1249
1441
|
response.status,
|
|
1250
1442
|
responseBody,
|
|
1251
1443
|
{
|
|
1252
|
-
connection_id:
|
|
1444
|
+
connection_id: effectiveConnectionId,
|
|
1253
1445
|
method: methodStr,
|
|
1254
|
-
url
|
|
1446
|
+
url: currentUrl
|
|
1255
1447
|
}
|
|
1256
1448
|
);
|
|
1257
1449
|
}
|
|
@@ -1276,12 +1468,23 @@ ${contentHash}`;
|
|
|
1276
1468
|
limit: options?.limit ?? 100,
|
|
1277
1469
|
offset: options?.offset ?? 0
|
|
1278
1470
|
};
|
|
1471
|
+
if (this.#userTokenGetter) {
|
|
1472
|
+
try {
|
|
1473
|
+
listBody.user_token = await this.#getUserToken();
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
console.warn(
|
|
1476
|
+
"user_token_getter failed in listConnections, falling back to un-scoped listing:",
|
|
1477
|
+
err instanceof Error ? err.message : String(err)
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1279
1481
|
const listPath = "/sdk/oauth/connections/list";
|
|
1280
|
-
const
|
|
1482
|
+
const listBodyStr = JSON.stringify(listBody);
|
|
1483
|
+
const listHmac = this.#computeHmacHeaders("POST", listPath, listBodyStr);
|
|
1281
1484
|
try {
|
|
1282
1485
|
response = await this.#alterClient.post(listPath, {
|
|
1283
|
-
|
|
1284
|
-
headers: { ...actorHeaders, ...listHmac }
|
|
1486
|
+
body: listBodyStr,
|
|
1487
|
+
headers: { ...actorHeaders, ...listHmac, "Content-Type": "application/json" }
|
|
1285
1488
|
});
|
|
1286
1489
|
} catch (error) {
|
|
1287
1490
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1327,24 +1530,37 @@ ${contentHash}`;
|
|
|
1327
1530
|
"SDK instance has been closed. Create a new AlterVault instance to make requests."
|
|
1328
1531
|
);
|
|
1329
1532
|
}
|
|
1330
|
-
if (!options.endUser?.id) {
|
|
1331
|
-
throw new AlterSDKError("endUser.id is required");
|
|
1332
|
-
}
|
|
1333
1533
|
const actorHeaders = this.#getActorRequestHeaders();
|
|
1334
1534
|
let response;
|
|
1335
1535
|
const sessionBody = {
|
|
1336
|
-
end_user: options.endUser,
|
|
1337
1536
|
allowed_providers: options.allowedProviders ?? null,
|
|
1338
1537
|
return_url: options.returnUrl ?? null,
|
|
1339
1538
|
allowed_origin: options.allowedOrigin ?? null,
|
|
1340
1539
|
metadata: options.metadata ?? null
|
|
1341
1540
|
};
|
|
1541
|
+
if (options.connectionPolicy) {
|
|
1542
|
+
sessionBody.connection_policy = {
|
|
1543
|
+
max_ttl_seconds: options.connectionPolicy.maxTtlSeconds ?? null,
|
|
1544
|
+
default_ttl_seconds: options.connectionPolicy.defaultTtlSeconds ?? null
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
if (this.#userTokenGetter) {
|
|
1548
|
+
try {
|
|
1549
|
+
sessionBody.user_token = await this.#getUserToken();
|
|
1550
|
+
} catch (err) {
|
|
1551
|
+
console.warn(
|
|
1552
|
+
"userTokenGetter failed in createConnectSession, session will not have identity tagging:",
|
|
1553
|
+
err instanceof Error ? err.message : String(err)
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1342
1557
|
const sessionPath = "/sdk/oauth/connect/session";
|
|
1343
|
-
const
|
|
1558
|
+
const sessionBodyStr = JSON.stringify(sessionBody);
|
|
1559
|
+
const sessionHmac = this.#computeHmacHeaders("POST", sessionPath, sessionBodyStr);
|
|
1344
1560
|
try {
|
|
1345
1561
|
response = await this.#alterClient.post(sessionPath, {
|
|
1346
|
-
|
|
1347
|
-
headers: { ...actorHeaders, ...sessionHmac }
|
|
1562
|
+
body: sessionBodyStr,
|
|
1563
|
+
headers: { ...actorHeaders, ...sessionHmac, "Content-Type": "application/json" }
|
|
1348
1564
|
});
|
|
1349
1565
|
} catch (error) {
|
|
1350
1566
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1375,7 +1591,7 @@ ${contentHash}`;
|
|
|
1375
1591
|
* server-side applications. It creates a Connect session, opens the
|
|
1376
1592
|
* browser, and polls until the user completes OAuth.
|
|
1377
1593
|
*
|
|
1378
|
-
* @param options - Connect options
|
|
1594
|
+
* @param options - Connect options
|
|
1379
1595
|
* @returns Array of ConnectResult objects (one per connected provider)
|
|
1380
1596
|
* @throws ConnectTimeoutError if the user doesn't complete within timeout
|
|
1381
1597
|
* @throws ConnectFlowError if the user denies or provider returns error
|
|
@@ -1391,8 +1607,8 @@ ${contentHash}`;
|
|
|
1391
1607
|
const pollInterval = options.pollInterval ?? 2;
|
|
1392
1608
|
const openBrowser = options.openBrowser ?? true;
|
|
1393
1609
|
const session = await this.createConnectSession({
|
|
1394
|
-
|
|
1395
|
-
|
|
1610
|
+
allowedProviders: options.providers,
|
|
1611
|
+
connectionPolicy: options.connectionPolicy
|
|
1396
1612
|
});
|
|
1397
1613
|
if (openBrowser) {
|
|
1398
1614
|
try {
|
|
@@ -1429,18 +1645,28 @@ ${contentHash}`;
|
|
|
1429
1645
|
connection_id: conn.connection_id ?? "",
|
|
1430
1646
|
provider_id: conn.provider_id ?? "",
|
|
1431
1647
|
account_identifier: conn.account_identifier ?? null,
|
|
1432
|
-
scopes: conn.scopes ?? []
|
|
1648
|
+
scopes: conn.scopes ?? [],
|
|
1649
|
+
connection_policy: conn.connection_policy ?? null
|
|
1433
1650
|
})
|
|
1434
1651
|
);
|
|
1435
1652
|
}
|
|
1436
1653
|
if (pollStatus === "error") {
|
|
1437
1654
|
const err = pollResult.error;
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1655
|
+
const errorCode = err?.error_code ?? "unknown_error";
|
|
1656
|
+
const errorMessage = err?.error_message ?? "OAuth flow failed";
|
|
1657
|
+
const errorDetails = { error_code: errorCode };
|
|
1658
|
+
if (errorCode === "connect_denied") {
|
|
1659
|
+
throw new ConnectDeniedError(errorMessage, errorDetails);
|
|
1660
|
+
}
|
|
1661
|
+
if ([
|
|
1662
|
+
"connect_config_error",
|
|
1663
|
+
"invalid_redirect_uri",
|
|
1664
|
+
"invalid_client",
|
|
1665
|
+
"unauthorized_client"
|
|
1666
|
+
].includes(errorCode)) {
|
|
1667
|
+
throw new ConnectConfigError(errorMessage, errorDetails);
|
|
1668
|
+
}
|
|
1669
|
+
throw new ConnectFlowError(errorMessage, errorDetails);
|
|
1444
1670
|
}
|
|
1445
1671
|
if (pollStatus === "expired") {
|
|
1446
1672
|
throw new ConnectFlowError(
|
|
@@ -1462,16 +1688,17 @@ ${contentHash}`;
|
|
|
1462
1688
|
const actorHeaders = this.#getActorRequestHeaders();
|
|
1463
1689
|
const pollPath = "/sdk/oauth/connect/session/poll";
|
|
1464
1690
|
const pollBody = { session_token: sessionToken };
|
|
1691
|
+
const pollBodyStr = JSON.stringify(pollBody);
|
|
1465
1692
|
const pollHmac = this.#computeHmacHeaders(
|
|
1466
1693
|
"POST",
|
|
1467
1694
|
pollPath,
|
|
1468
|
-
|
|
1695
|
+
pollBodyStr
|
|
1469
1696
|
);
|
|
1470
1697
|
let response;
|
|
1471
1698
|
try {
|
|
1472
1699
|
response = await this.#alterClient.post(pollPath, {
|
|
1473
|
-
|
|
1474
|
-
headers: { ...actorHeaders, ...pollHmac }
|
|
1700
|
+
body: pollBodyStr,
|
|
1701
|
+
headers: { ...actorHeaders, ...pollHmac, "Content-Type": "application/json" }
|
|
1475
1702
|
});
|
|
1476
1703
|
} catch (error) {
|
|
1477
1704
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1519,10 +1746,72 @@ Object.freeze(AlterVault.prototype);
|
|
|
1519
1746
|
|
|
1520
1747
|
// src/providers/enums.ts
|
|
1521
1748
|
var Provider = /* @__PURE__ */ ((Provider2) => {
|
|
1522
|
-
Provider2["
|
|
1749
|
+
Provider2["ACUITY_SCHEDULING"] = "acuity-scheduling";
|
|
1750
|
+
Provider2["ADOBE"] = "adobe";
|
|
1751
|
+
Provider2["AIRCALL"] = "aircall";
|
|
1752
|
+
Provider2["AIRTABLE"] = "airtable";
|
|
1753
|
+
Provider2["APOLLO"] = "apollo";
|
|
1754
|
+
Provider2["ASANA"] = "asana";
|
|
1755
|
+
Provider2["ATLASSIAN"] = "atlassian";
|
|
1756
|
+
Provider2["ATTIO"] = "attio";
|
|
1757
|
+
Provider2["AUTODESK"] = "autodesk";
|
|
1758
|
+
Provider2["BASECAMP"] = "basecamp";
|
|
1759
|
+
Provider2["BITBUCKET"] = "bitbucket";
|
|
1760
|
+
Provider2["BITLY"] = "bitly";
|
|
1761
|
+
Provider2["BOX"] = "box";
|
|
1762
|
+
Provider2["BREX"] = "brex";
|
|
1763
|
+
Provider2["CALENDLY"] = "calendly";
|
|
1764
|
+
Provider2["CAL_COM"] = "cal-com";
|
|
1765
|
+
Provider2["CANVA"] = "canva";
|
|
1766
|
+
Provider2["CLICKUP"] = "clickup";
|
|
1767
|
+
Provider2["CLOSE"] = "close";
|
|
1768
|
+
Provider2["CONSTANT_CONTACT"] = "constant-contact";
|
|
1769
|
+
Provider2["CONTENTFUL"] = "contentful";
|
|
1770
|
+
Provider2["DEEL"] = "deel";
|
|
1771
|
+
Provider2["DIALPAD"] = "dialpad";
|
|
1772
|
+
Provider2["DIGITALOCEAN"] = "digitalocean";
|
|
1773
|
+
Provider2["DISCORD"] = "discord";
|
|
1774
|
+
Provider2["DOCUSIGN"] = "docusign";
|
|
1775
|
+
Provider2["DROPBOX"] = "dropbox";
|
|
1776
|
+
Provider2["EBAY"] = "ebay";
|
|
1777
|
+
Provider2["EVENTBRITE"] = "eventbrite";
|
|
1778
|
+
Provider2["FACEBOOK"] = "facebook";
|
|
1779
|
+
Provider2["FIGMA"] = "figma";
|
|
1523
1780
|
Provider2["GITHUB"] = "github";
|
|
1524
|
-
Provider2["
|
|
1781
|
+
Provider2["GOOGLE"] = "google";
|
|
1782
|
+
Provider2["HUBSPOT"] = "hubspot";
|
|
1783
|
+
Provider2["INSTAGRAM"] = "instagram";
|
|
1784
|
+
Provider2["LINEAR"] = "linear";
|
|
1785
|
+
Provider2["LINKEDIN"] = "linkedin";
|
|
1786
|
+
Provider2["MAILCHIMP"] = "mailchimp";
|
|
1787
|
+
Provider2["MERCURY"] = "mercury";
|
|
1788
|
+
Provider2["MICROSOFT"] = "microsoft";
|
|
1789
|
+
Provider2["MIRO"] = "miro";
|
|
1790
|
+
Provider2["MONDAY"] = "monday";
|
|
1791
|
+
Provider2["NOTION"] = "notion";
|
|
1792
|
+
Provider2["OUTREACH"] = "outreach";
|
|
1793
|
+
Provider2["PAGERDUTY"] = "pagerduty";
|
|
1794
|
+
Provider2["PAYPAL"] = "paypal";
|
|
1795
|
+
Provider2["PINTEREST"] = "pinterest";
|
|
1796
|
+
Provider2["PIPEDRIVE"] = "pipedrive";
|
|
1797
|
+
Provider2["QUICKBOOKS"] = "quickbooks";
|
|
1798
|
+
Provider2["RAMP"] = "ramp";
|
|
1799
|
+
Provider2["REDDIT"] = "reddit";
|
|
1800
|
+
Provider2["RINGCENTRAL"] = "ringcentral";
|
|
1801
|
+
Provider2["SALESFORCE"] = "salesforce";
|
|
1525
1802
|
Provider2["SENTRY"] = "sentry";
|
|
1803
|
+
Provider2["SLACK"] = "slack";
|
|
1804
|
+
Provider2["SNAPCHAT"] = "snapchat";
|
|
1805
|
+
Provider2["SPOTIFY"] = "spotify";
|
|
1806
|
+
Provider2["SQUARE"] = "square";
|
|
1807
|
+
Provider2["SQUARESPACE"] = "squarespace";
|
|
1808
|
+
Provider2["STRIPE"] = "stripe";
|
|
1809
|
+
Provider2["TIKTOK"] = "tiktok";
|
|
1810
|
+
Provider2["TODOIST"] = "todoist";
|
|
1811
|
+
Provider2["TWITTER"] = "twitter";
|
|
1812
|
+
Provider2["TYPEFORM"] = "typeform";
|
|
1813
|
+
Provider2["WEBEX"] = "webex";
|
|
1814
|
+
Provider2["WEBFLOW"] = "webflow";
|
|
1526
1815
|
return Provider2;
|
|
1527
1816
|
})(Provider || {});
|
|
1528
1817
|
var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
@@ -1540,20 +1829,26 @@ export {
|
|
|
1540
1829
|
ActorType,
|
|
1541
1830
|
AlterSDKError,
|
|
1542
1831
|
AlterVault,
|
|
1832
|
+
BackendError,
|
|
1833
|
+
ConnectConfigError,
|
|
1834
|
+
ConnectDeniedError,
|
|
1543
1835
|
ConnectFlowError,
|
|
1544
1836
|
ConnectResult,
|
|
1545
1837
|
ConnectSession,
|
|
1546
1838
|
ConnectTimeoutError,
|
|
1839
|
+
ConnectionDeletedError,
|
|
1840
|
+
ConnectionExpiredError,
|
|
1547
1841
|
ConnectionInfo,
|
|
1548
1842
|
ConnectionListResult,
|
|
1549
1843
|
ConnectionNotFoundError,
|
|
1844
|
+
ConnectionRevokedError,
|
|
1550
1845
|
HttpMethod,
|
|
1551
1846
|
NetworkError,
|
|
1552
1847
|
PolicyViolationError,
|
|
1553
1848
|
Provider,
|
|
1554
1849
|
ProviderAPIError,
|
|
1850
|
+
ReAuthRequiredError,
|
|
1851
|
+
ScopeReauthRequiredError,
|
|
1555
1852
|
TimeoutError,
|
|
1556
|
-
|
|
1557
|
-
TokenResponse,
|
|
1558
|
-
TokenRetrievalError
|
|
1853
|
+
TokenResponse
|
|
1559
1854
|
};
|