@alter-ai/alter-sdk 0.5.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 CHANGED
@@ -13,7 +13,7 @@ Official TypeScript SDK for [Alter Vault](https://alterai.dev) -Credential manag
13
13
  - **Automatic Token Refresh**: Tokens refreshed transparently by the backend
14
14
  - **API Key and Custom Credential Support**: Handles OAuth tokens, API keys, and custom credential formats automatically
15
15
  - **Actor Tracking**: First-class support for AI agent and MCP server observability
16
- - **HMAC Request Signing**: All SDK-to-backend requests are signed with a derived HMAC-SHA256 key for integrity, authenticity, and replay protection
16
+ - **HMAC Request Signing**: All SDK-to-backend requests are cryptographically signed for integrity, authenticity, and replay protection
17
17
  - **Native Promises**: Built on native `fetch` -no heavy dependencies
18
18
 
19
19
  ## Installation
@@ -37,7 +37,7 @@ const vault = new AlterVault({
37
37
  const response = await vault.request(
38
38
  "CONNECTION_ID", // from Alter Connect (see below)
39
39
  HttpMethod.GET,
40
- "https://www.googleapis.com/calendar/v3/calendars/primary/events",
40
+ "https://api.example.com/v1/resource",
41
41
  {
42
42
  queryParams: { maxResults: "10" },
43
43
  },
@@ -66,7 +66,7 @@ await vault.close();
66
66
  // You can also discover connectionIds programmatically:
67
67
  const result = await vault.listConnections({ providerId: "google" });
68
68
  for (const conn of result.connections) {
69
- console.log(`${conn.id}: ${conn.accountDisplayName}`);
69
+ console.log(`${conn.connectionId}: ${conn.accountDisplayName}`);
70
70
  }
71
71
  ```
72
72
 
@@ -80,7 +80,7 @@ The `request()` method returns the raw `Response` object. The token is injected
80
80
  const response = await vault.request(
81
81
  connectionId,
82
82
  HttpMethod.GET,
83
- "https://www.googleapis.com/calendar/v3/calendars/primary/events",
83
+ "https://api.example.com/v1/resource",
84
84
  );
85
85
  ```
86
86
 
@@ -221,7 +221,7 @@ for (const result of results) {
221
221
  const response = await vault.request(
222
222
  results[0].connectionId,
223
223
  HttpMethod.GET,
224
- "https://www.googleapis.com/calendar/v3/calendars/primary/events",
224
+ "https://api.example.com/v1/resource",
225
225
  );
226
226
  ```
227
227
 
@@ -254,7 +254,7 @@ const vault = new AlterVault({
254
254
  const response = await vault.request(
255
255
  connectionId,
256
256
  HttpMethod.GET,
257
- "https://www.googleapis.com/calendar/v3/calendars/primary/events",
257
+ "https://api.example.com/v1/resource",
258
258
  {
259
259
  runId: "550e8400-e29b-41d4-a716-446655440000", // auto-generated UUID if omitted
260
260
  threadId: "thread-xyz",
@@ -289,12 +289,12 @@ const calendarAgent = new AlterVault({
289
289
  await emailAgent.request(
290
290
  gmailConnectionId, // from Alter Connect
291
291
  HttpMethod.GET,
292
- "https://gmail.googleapis.com/gmail/v1/users/me/messages",
292
+ "https://api.example.com/v1/messages",
293
293
  );
294
294
  await calendarAgent.request(
295
295
  calendarConnectionId, // from Alter Connect
296
296
  HttpMethod.GET,
297
- "https://www.googleapis.com/calendar/v3/calendars/primary/events",
297
+ "https://api.example.com/v1/resource",
298
298
  );
299
299
 
300
300
  // Clean up each instance
@@ -370,7 +370,7 @@ try {
370
370
  const response = await vault.request(
371
371
  connectionId,
372
372
  HttpMethod.GET,
373
- "https://www.googleapis.com/calendar/v3/calendars/primary/events",
373
+ "https://api.example.com/v1/resource",
374
374
  );
375
375
  } catch (error) {
376
376
  // --- Connection unusable — user must re-authorize via Alter Connect ---
@@ -390,7 +390,7 @@ try {
390
390
 
391
391
  // --- Policy restrictions — may resolve on its own ---
392
392
  } else if (error instanceof PolicyViolationError) {
393
- // Business hours, IP allowlist, or other Cerbos policy denial
393
+ // Business hours, IP allowlist, or other policy denial
394
394
  console.error(`Policy denied: ${error.message} (reason: ${error.policyError})`);
395
395
 
396
396
  // --- Transient / infrastructure errors — safe to retry ---
@@ -404,6 +404,8 @@ try {
404
404
  // Create a new Connect session so the user can grant updated scopes
405
405
  } else if (error instanceof ProviderAPIError) {
406
406
  console.error(`Provider error ${error.statusCode}: ${error.responseBody}`);
407
+ } else {
408
+ throw error;
407
409
  }
408
410
  }
409
411
  ```
package/dist/index.cjs CHANGED
@@ -270,7 +270,8 @@ var TokenResponse = class _TokenResponse {
270
270
  expires_in: this.expiresIn,
271
271
  expires_at: this.expiresAt?.toISOString() ?? null,
272
272
  scopes: this.scopes,
273
- connection_id: this.connectionId
273
+ connection_id: this.connectionId,
274
+ provider_id: this.providerId
274
275
  };
275
276
  }
276
277
  /**
@@ -287,7 +288,7 @@ var TokenResponse = class _TokenResponse {
287
288
  }
288
289
  };
289
290
  var ConnectionInfo = class {
290
- id;
291
+ connectionId;
291
292
  providerId;
292
293
  scopes;
293
294
  accountIdentifier;
@@ -298,7 +299,7 @@ var ConnectionInfo = class {
298
299
  createdAt;
299
300
  lastUsedAt;
300
301
  constructor(data) {
301
- this.id = data.id;
302
+ this.connectionId = data.connection_id;
302
303
  this.providerId = data.provider_id;
303
304
  this.scopes = data.scopes ?? [];
304
305
  this.accountIdentifier = data.account_identifier ?? null;
@@ -312,7 +313,7 @@ var ConnectionInfo = class {
312
313
  }
313
314
  toJSON() {
314
315
  return {
315
- id: this.id,
316
+ connection_id: this.connectionId,
316
317
  provider_id: this.providerId,
317
318
  scopes: this.scopes,
318
319
  account_identifier: this.accountIdentifier,
@@ -325,7 +326,7 @@ var ConnectionInfo = class {
325
326
  };
326
327
  }
327
328
  toString() {
328
- return `ConnectionInfo(id=${this.id}, provider=${this.providerId}, status=${this.status})`;
329
+ return `ConnectionInfo(connection_id=${this.connectionId}, provider=${this.providerId}, status=${this.status})`;
329
330
  }
330
331
  };
331
332
  var ConnectSession = class {
@@ -378,7 +379,17 @@ var ConnectResult = class {
378
379
  this.providerId = data.provider_id;
379
380
  this.accountIdentifier = data.account_identifier ?? null;
380
381
  this.scopes = data.scopes ?? [];
381
- this.connectionPolicy = data.connection_policy ?? null;
382
+ const rawPolicy = data.connection_policy;
383
+ if (rawPolicy) {
384
+ const raw = rawPolicy;
385
+ this.connectionPolicy = Object.freeze({
386
+ expiresAt: raw["expires_at"] !== void 0 ? raw["expires_at"] : rawPolicy.expiresAt ?? null,
387
+ createdBy: raw["created_by"] !== void 0 ? raw["created_by"] : rawPolicy.createdBy ?? null,
388
+ createdAt: raw["created_at"] !== void 0 ? raw["created_at"] : rawPolicy.createdAt ?? null
389
+ });
390
+ } else {
391
+ this.connectionPolicy = null;
392
+ }
382
393
  Object.freeze(this);
383
394
  }
384
395
  toJSON() {
@@ -387,7 +398,11 @@ var ConnectResult = class {
387
398
  provider_id: this.providerId,
388
399
  account_identifier: this.accountIdentifier,
389
400
  scopes: this.scopes,
390
- connection_policy: this.connectionPolicy
401
+ connection_policy: this.connectionPolicy ? {
402
+ expires_at: this.connectionPolicy.expiresAt ?? null,
403
+ created_by: this.connectionPolicy.createdBy ?? null,
404
+ created_at: this.connectionPolicy.createdAt ?? null
405
+ } : null
391
406
  };
392
407
  }
393
408
  toString() {
@@ -401,7 +416,8 @@ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
401
416
  "x-api-key",
402
417
  "x-auth-token",
403
418
  "x-amz-date",
404
- "x-amz-content-sha256"
419
+ "x-amz-content-sha256",
420
+ "x-amz-security-token"
405
421
  ]);
406
422
  var APICallAuditLog = class {
407
423
  connectionId;
@@ -480,6 +496,7 @@ var import_node_crypto = require("crypto");
480
496
  var ALGORITHM = "AWS4-HMAC-SHA256";
481
497
  var AWS_HOST_RE = /^(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
482
498
  var S3_VIRTUAL_HOST_RE = /^[^.]+\.s3\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
499
+ var VPCE_HOST_RE = /^vpce-[a-z0-9-]+\.(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.vpce\.amazonaws\.com$/;
483
500
  function hmacSha256(key, message) {
484
501
  return (0, import_node_crypto.createHmac)("sha256", key).update(message, "utf8").digest();
485
502
  }
@@ -502,6 +519,10 @@ function detectServiceAndRegion(hostname) {
502
519
  if (s3m?.groups) {
503
520
  return { service: "s3", region: s3m.groups.region };
504
521
  }
522
+ const vpcem = VPCE_HOST_RE.exec(lower);
523
+ if (vpcem?.groups) {
524
+ return { service: vpcem.groups.service, region: vpcem.groups.region };
525
+ }
505
526
  return { service: null, region: null };
506
527
  }
507
528
  function deriveSigningKey(secretKey, dateStamp, region, service) {
@@ -536,18 +557,21 @@ function canonicalQueryString(query) {
536
557
  ]);
537
558
  }
538
559
  }
539
- sorted.sort((a, b) => {
560
+ const sigv4Encode = (s) => encodeURIComponent(s).replace(
561
+ /[!'()*]/g,
562
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
563
+ );
564
+ const encoded = sorted.map(
565
+ ([k, v]) => [sigv4Encode(k), sigv4Encode(v)]
566
+ );
567
+ encoded.sort((a, b) => {
540
568
  if (a[0] < b[0]) return -1;
541
569
  if (a[0] > b[0]) return 1;
542
570
  if (a[1] < b[1]) return -1;
543
571
  if (a[1] > b[1]) return 1;
544
572
  return 0;
545
573
  });
546
- const sigv4Encode = (s) => encodeURIComponent(s).replace(
547
- /[!'()*]/g,
548
- (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
549
- );
550
- return sorted.map(([k, v]) => `${sigv4Encode(k)}=${sigv4Encode(v)}`).join("&");
574
+ return encoded.map(([k, v]) => `${k}=${v}`).join("&");
551
575
  }
552
576
  function canonicalHeadersAndSigned(headers) {
553
577
  const canonical = {};
@@ -566,11 +590,9 @@ function signAwsRequest(opts) {
566
590
  const parsed = new URL(opts.url);
567
591
  const hostname = parsed.hostname;
568
592
  let { region, service } = opts;
569
- if (region == null || service == null) {
570
- const detected = detectServiceAndRegion(hostname);
571
- if (region == null) region = detected.region;
572
- if (service == null) service = detected.service;
573
- }
593
+ const detected = detectServiceAndRegion(hostname);
594
+ if (detected.region != null) region = detected.region;
595
+ if (detected.service != null) service = detected.service;
574
596
  if (!region) {
575
597
  throw new Error(
576
598
  `Cannot determine AWS region from URL '${opts.url}'. Pass region explicitly via additional_credentials.`
@@ -653,7 +675,7 @@ function _extractAdditionalCredentials(token) {
653
675
  return _additionalCredsStore.get(token);
654
676
  }
655
677
  var _fetch;
656
- var SDK_VERSION = "0.5.0";
678
+ var SDK_VERSION = "0.6.0";
657
679
  var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
658
680
  var HTTP_FORBIDDEN = 403;
659
681
  var HTTP_NOT_FOUND = 404;
package/dist/index.d.cts CHANGED
@@ -109,7 +109,7 @@ declare class TokenResponse {
109
109
  * Returned by listConnections(). Contains metadata only — no tokens.
110
110
  */
111
111
  declare class ConnectionInfo {
112
- readonly id: string;
112
+ readonly connectionId: string;
113
113
  readonly providerId: string;
114
114
  readonly scopes: string[];
115
115
  readonly accountIdentifier: string | null;
@@ -120,7 +120,7 @@ declare class ConnectionInfo {
120
120
  readonly createdAt: string;
121
121
  readonly lastUsedAt: string | null;
122
122
  constructor(data: {
123
- id: string;
123
+ connection_id: string;
124
124
  provider_id: string;
125
125
  scopes?: string[];
126
126
  account_identifier?: string | null;
@@ -185,11 +185,11 @@ declare class ConnectionListResult {
185
185
  */
186
186
  interface ConnectionPolicy {
187
187
  /** Hard expiry timestamp (ISO 8601 UTC) */
188
- expires_at?: string | null;
188
+ readonly expiresAt?: string | null;
189
189
  /** Who set the policy: 'developer' or 'end_user' */
190
- created_by?: string | null;
190
+ readonly createdBy?: string | null;
191
191
  /** When the policy was set (ISO 8601 UTC) */
192
- created_at?: string | null;
192
+ readonly createdAt?: string | null;
193
193
  }
194
194
  declare class ConnectResult {
195
195
  readonly connectionId: string;
@@ -767,4 +767,4 @@ declare class TimeoutError extends NetworkError {
767
767
  constructor(message: string, details?: Record<string, unknown>);
768
768
  }
769
769
 
770
- export { APICallAuditLog, ActorType, AlterSDKError, AlterVault, type AlterVaultOptions, BackendError, ConnectConfigError, ConnectDeniedError, ConnectFlowError, type ConnectOptions, ConnectResult, ConnectSession, ConnectTimeoutError, ConnectionDeletedError, ConnectionExpiredError, ConnectionInfo, ConnectionListResult, ConnectionNotFoundError, ConnectionRevokedError, type CreateConnectSessionOptions, HttpMethod, type ListConnectionsOptions, NetworkError, PolicyViolationError, Provider, ProviderAPIError, ReAuthRequiredError, type RequestOptions, ScopeReauthRequiredError, TimeoutError, TokenResponse };
770
+ export { APICallAuditLog, ActorType, AlterSDKError, AlterVault, type AlterVaultOptions, BackendError, ConnectConfigError, ConnectDeniedError, ConnectFlowError, type ConnectOptions, ConnectResult, ConnectSession, ConnectTimeoutError, ConnectionDeletedError, ConnectionExpiredError, ConnectionInfo, ConnectionListResult, ConnectionNotFoundError, type ConnectionPolicy, ConnectionRevokedError, type CreateConnectSessionOptions, HttpMethod, type ListConnectionsOptions, NetworkError, PolicyViolationError, Provider, ProviderAPIError, ReAuthRequiredError, type RequestOptions, ScopeReauthRequiredError, TimeoutError, TokenResponse };
package/dist/index.d.ts CHANGED
@@ -109,7 +109,7 @@ declare class TokenResponse {
109
109
  * Returned by listConnections(). Contains metadata only — no tokens.
110
110
  */
111
111
  declare class ConnectionInfo {
112
- readonly id: string;
112
+ readonly connectionId: string;
113
113
  readonly providerId: string;
114
114
  readonly scopes: string[];
115
115
  readonly accountIdentifier: string | null;
@@ -120,7 +120,7 @@ declare class ConnectionInfo {
120
120
  readonly createdAt: string;
121
121
  readonly lastUsedAt: string | null;
122
122
  constructor(data: {
123
- id: string;
123
+ connection_id: string;
124
124
  provider_id: string;
125
125
  scopes?: string[];
126
126
  account_identifier?: string | null;
@@ -185,11 +185,11 @@ declare class ConnectionListResult {
185
185
  */
186
186
  interface ConnectionPolicy {
187
187
  /** Hard expiry timestamp (ISO 8601 UTC) */
188
- expires_at?: string | null;
188
+ readonly expiresAt?: string | null;
189
189
  /** Who set the policy: 'developer' or 'end_user' */
190
- created_by?: string | null;
190
+ readonly createdBy?: string | null;
191
191
  /** When the policy was set (ISO 8601 UTC) */
192
- created_at?: string | null;
192
+ readonly createdAt?: string | null;
193
193
  }
194
194
  declare class ConnectResult {
195
195
  readonly connectionId: string;
@@ -767,4 +767,4 @@ declare class TimeoutError extends NetworkError {
767
767
  constructor(message: string, details?: Record<string, unknown>);
768
768
  }
769
769
 
770
- export { APICallAuditLog, ActorType, AlterSDKError, AlterVault, type AlterVaultOptions, BackendError, ConnectConfigError, ConnectDeniedError, ConnectFlowError, type ConnectOptions, ConnectResult, ConnectSession, ConnectTimeoutError, ConnectionDeletedError, ConnectionExpiredError, ConnectionInfo, ConnectionListResult, ConnectionNotFoundError, ConnectionRevokedError, type CreateConnectSessionOptions, HttpMethod, type ListConnectionsOptions, NetworkError, PolicyViolationError, Provider, ProviderAPIError, ReAuthRequiredError, type RequestOptions, ScopeReauthRequiredError, TimeoutError, TokenResponse };
770
+ export { APICallAuditLog, ActorType, AlterSDKError, AlterVault, type AlterVaultOptions, BackendError, ConnectConfigError, ConnectDeniedError, ConnectFlowError, type ConnectOptions, ConnectResult, ConnectSession, ConnectTimeoutError, ConnectionDeletedError, ConnectionExpiredError, ConnectionInfo, ConnectionListResult, ConnectionNotFoundError, type ConnectionPolicy, ConnectionRevokedError, type CreateConnectSessionOptions, HttpMethod, type ListConnectionsOptions, NetworkError, PolicyViolationError, Provider, ProviderAPIError, ReAuthRequiredError, type RequestOptions, ScopeReauthRequiredError, TimeoutError, TokenResponse };
package/dist/index.js CHANGED
@@ -209,7 +209,8 @@ var TokenResponse = class _TokenResponse {
209
209
  expires_in: this.expiresIn,
210
210
  expires_at: this.expiresAt?.toISOString() ?? null,
211
211
  scopes: this.scopes,
212
- connection_id: this.connectionId
212
+ connection_id: this.connectionId,
213
+ provider_id: this.providerId
213
214
  };
214
215
  }
215
216
  /**
@@ -226,7 +227,7 @@ var TokenResponse = class _TokenResponse {
226
227
  }
227
228
  };
228
229
  var ConnectionInfo = class {
229
- id;
230
+ connectionId;
230
231
  providerId;
231
232
  scopes;
232
233
  accountIdentifier;
@@ -237,7 +238,7 @@ var ConnectionInfo = class {
237
238
  createdAt;
238
239
  lastUsedAt;
239
240
  constructor(data) {
240
- this.id = data.id;
241
+ this.connectionId = data.connection_id;
241
242
  this.providerId = data.provider_id;
242
243
  this.scopes = data.scopes ?? [];
243
244
  this.accountIdentifier = data.account_identifier ?? null;
@@ -251,7 +252,7 @@ var ConnectionInfo = class {
251
252
  }
252
253
  toJSON() {
253
254
  return {
254
- id: this.id,
255
+ connection_id: this.connectionId,
255
256
  provider_id: this.providerId,
256
257
  scopes: this.scopes,
257
258
  account_identifier: this.accountIdentifier,
@@ -264,7 +265,7 @@ var ConnectionInfo = class {
264
265
  };
265
266
  }
266
267
  toString() {
267
- return `ConnectionInfo(id=${this.id}, provider=${this.providerId}, status=${this.status})`;
268
+ return `ConnectionInfo(connection_id=${this.connectionId}, provider=${this.providerId}, status=${this.status})`;
268
269
  }
269
270
  };
270
271
  var ConnectSession = class {
@@ -317,7 +318,17 @@ var ConnectResult = class {
317
318
  this.providerId = data.provider_id;
318
319
  this.accountIdentifier = data.account_identifier ?? null;
319
320
  this.scopes = data.scopes ?? [];
320
- this.connectionPolicy = data.connection_policy ?? null;
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
+ }
321
332
  Object.freeze(this);
322
333
  }
323
334
  toJSON() {
@@ -326,7 +337,11 @@ var ConnectResult = class {
326
337
  provider_id: this.providerId,
327
338
  account_identifier: this.accountIdentifier,
328
339
  scopes: this.scopes,
329
- connection_policy: this.connectionPolicy
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
330
345
  };
331
346
  }
332
347
  toString() {
@@ -340,7 +355,8 @@ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
340
355
  "x-api-key",
341
356
  "x-auth-token",
342
357
  "x-amz-date",
343
- "x-amz-content-sha256"
358
+ "x-amz-content-sha256",
359
+ "x-amz-security-token"
344
360
  ]);
345
361
  var APICallAuditLog = class {
346
362
  connectionId;
@@ -419,6 +435,7 @@ import { createHash, createHmac } from "crypto";
419
435
  var ALGORITHM = "AWS4-HMAC-SHA256";
420
436
  var AWS_HOST_RE = /^(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
421
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$/;
422
439
  function hmacSha256(key, message) {
423
440
  return createHmac("sha256", key).update(message, "utf8").digest();
424
441
  }
@@ -441,6 +458,10 @@ function detectServiceAndRegion(hostname) {
441
458
  if (s3m?.groups) {
442
459
  return { service: "s3", region: s3m.groups.region };
443
460
  }
461
+ const vpcem = VPCE_HOST_RE.exec(lower);
462
+ if (vpcem?.groups) {
463
+ return { service: vpcem.groups.service, region: vpcem.groups.region };
464
+ }
444
465
  return { service: null, region: null };
445
466
  }
446
467
  function deriveSigningKey(secretKey, dateStamp, region, service) {
@@ -475,18 +496,21 @@ function canonicalQueryString(query) {
475
496
  ]);
476
497
  }
477
498
  }
478
- sorted.sort((a, b) => {
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) => {
479
507
  if (a[0] < b[0]) return -1;
480
508
  if (a[0] > b[0]) return 1;
481
509
  if (a[1] < b[1]) return -1;
482
510
  if (a[1] > b[1]) return 1;
483
511
  return 0;
484
512
  });
485
- const sigv4Encode = (s) => encodeURIComponent(s).replace(
486
- /[!'()*]/g,
487
- (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
488
- );
489
- return sorted.map(([k, v]) => `${sigv4Encode(k)}=${sigv4Encode(v)}`).join("&");
513
+ return encoded.map(([k, v]) => `${k}=${v}`).join("&");
490
514
  }
491
515
  function canonicalHeadersAndSigned(headers) {
492
516
  const canonical = {};
@@ -505,11 +529,9 @@ function signAwsRequest(opts) {
505
529
  const parsed = new URL(opts.url);
506
530
  const hostname = parsed.hostname;
507
531
  let { region, service } = opts;
508
- if (region == null || service == null) {
509
- const detected = detectServiceAndRegion(hostname);
510
- if (region == null) region = detected.region;
511
- if (service == null) service = detected.service;
512
- }
532
+ const detected = detectServiceAndRegion(hostname);
533
+ if (detected.region != null) region = detected.region;
534
+ if (detected.service != null) service = detected.service;
513
535
  if (!region) {
514
536
  throw new Error(
515
537
  `Cannot determine AWS region from URL '${opts.url}'. Pass region explicitly via additional_credentials.`
@@ -592,7 +614,7 @@ function _extractAdditionalCredentials(token) {
592
614
  return _additionalCredsStore.get(token);
593
615
  }
594
616
  var _fetch;
595
- var SDK_VERSION = "0.5.0";
617
+ var SDK_VERSION = "0.6.0";
596
618
  var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
597
619
  var HTTP_FORBIDDEN = 403;
598
620
  var HTTP_NOT_FOUND = 404;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alter-ai/alter-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Official TypeScript SDK for Alter Vault — OAuth token management with policy enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",