@itm-studio/partner-sdk 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -5380,24 +5380,46 @@ assertNodeServerRuntime();
5380
5380
  var DEFAULT_API_URL = "https://itm-backend-production.up.railway.app/graphql";
5381
5381
  var RESERVED_AGENT_HEADERS = /* @__PURE__ */ new Set([
5382
5382
  "authorization",
5383
+ "x-itm-integration-access-token",
5383
5384
  "x-itm-agent-session-id",
5384
5385
  "x-itm-agent-bootstrap-token",
5385
5386
  "x-itm-agent-access-token",
5386
5387
  "x-partner-api-key"
5387
5388
  ]);
5388
5389
  function createITMPartnerClient(config) {
5389
- const { baseUrl, headers: extraHeaders } = config;
5390
+ const { baseUrl, headers: extraHeaders, signal } = config;
5390
5391
  const token = "token" in config ? config.token : void 0;
5391
5392
  const agentSession = "agentSession" in config ? config.agentSession : void 0;
5392
5393
  const agentAccess = "agentAccess" in config ? config.agentAccess : void 0;
5393
- const providedModes = [token, agentSession, agentAccess].filter(
5394
+ const integration = "integration" in config ? config.integration : void 0;
5395
+ const providedModes = [token, agentSession, agentAccess, integration].filter(
5394
5396
  (mode) => mode !== void 0
5395
5397
  );
5396
5398
  if (providedModes.length > 1) {
5397
5399
  throw new Error(
5398
- "ITM Partner SDK: provide exactly one of token, agentSession, or agentAccess authentication."
5400
+ "ITM Partner SDK: provide exactly one of token, agentSession, agentAccess, or integration authentication."
5399
5401
  );
5400
5402
  }
5403
+ if (integration !== void 0) {
5404
+ if (typeof integration.accessToken !== "function" && (typeof integration.accessToken !== "string" || !integration.accessToken.trim())) {
5405
+ throw new Error("ITM Partner SDK: integration.accessToken is required.");
5406
+ }
5407
+ return createSafeClient({
5408
+ url: baseUrl ?? DEFAULT_API_URL,
5409
+ signal,
5410
+ headers: async () => {
5411
+ const extra = typeof extraHeaders === "function" ? await extraHeaders() : extraHeaders;
5412
+ let credential;
5413
+ try {
5414
+ credential = typeof integration.accessToken === "function" ? await integration.accessToken() : integration.accessToken;
5415
+ } catch {
5416
+ throw new Error("ITM Partner SDK: integration credential is unavailable.");
5417
+ }
5418
+ if (typeof credential !== "string" || !credential.trim()) throw new Error("ITM Partner SDK: integration credential is unavailable.");
5419
+ return { ...sanitizeHeaders(extra), "x-itm-integration-access-token": credential, "Content-Type": "application/json" };
5420
+ }
5421
+ });
5422
+ }
5401
5423
  if (agentAccess !== void 0) {
5402
5424
  if (typeof agentAccess.workloadToken !== "function") {
5403
5425
  throw new Error(
@@ -5407,8 +5429,9 @@ function createITMPartnerClient(config) {
5407
5429
  if (!agentAccess.accessToken) {
5408
5430
  throw new Error("ITM Partner SDK: agentAccess.accessToken is required.");
5409
5431
  }
5410
- return createClient2({
5432
+ return createSafeClient({
5411
5433
  url: baseUrl ?? DEFAULT_API_URL,
5434
+ signal,
5412
5435
  headers: async () => {
5413
5436
  const resolvedExtraHeaders = typeof extraHeaders === "function" ? await extraHeaders() : extraHeaders;
5414
5437
  const sanitizedExtraHeaders = Object.fromEntries(
@@ -5440,8 +5463,9 @@ function createITMPartnerClient(config) {
5440
5463
  if (!agentSession.sessionUid) {
5441
5464
  throw new Error("ITM Partner SDK: agentSession.sessionUid is required.");
5442
5465
  }
5443
- return createClient2({
5466
+ return createSafeClient({
5444
5467
  url: baseUrl ?? DEFAULT_API_URL,
5468
+ signal,
5445
5469
  headers: async () => {
5446
5470
  const resolvedExtraHeaders = typeof extraHeaders === "function" ? await extraHeaders() : extraHeaders;
5447
5471
  const sanitizedExtraHeaders = Object.fromEntries(
@@ -5470,19 +5494,77 @@ function createITMPartnerClient(config) {
5470
5494
  "ITM Partner SDK: token is required. Get one from backstage: Settings > Partner API."
5471
5495
  );
5472
5496
  }
5473
- return createClient2({
5497
+ return createSafeClient({
5474
5498
  url: baseUrl ?? DEFAULT_API_URL,
5499
+ signal,
5475
5500
  headers: async () => {
5476
5501
  const resolvedExtraHeaders = typeof extraHeaders === "function" ? await extraHeaders() : extraHeaders;
5477
5502
  return {
5478
- ...resolvedExtraHeaders ?? {},
5503
+ ...sanitizeHeaders(resolvedExtraHeaders),
5479
5504
  "x-partner-api-key": token,
5480
5505
  "Content-Type": "application/json"
5481
5506
  };
5482
5507
  }
5483
5508
  });
5484
5509
  }
5510
+ function sanitizeHeaders(headers) {
5511
+ return Object.fromEntries(Object.entries(headers ?? {}).filter(([name]) => !RESERVED_AGENT_HEADERS.has(name.toLowerCase())));
5512
+ }
5513
+ var ITMPartnerTransportError = class extends Error {
5514
+ constructor(status, requestId) {
5515
+ super(status ? `ITM Partner API request failed (HTTP ${status}).` : "ITM Partner API request failed.");
5516
+ this.status = status;
5517
+ this.requestId = requestId;
5518
+ }
5519
+ name = "ITMPartnerTransportError";
5520
+ };
5521
+ function createSafeClient(options) {
5522
+ return createClient2({ fetcher: async (operation) => {
5523
+ options.signal?.throwIfAborted();
5524
+ const headers = await withCancellation(options.headers, options.signal);
5525
+ options.signal?.throwIfAborted();
5526
+ let response;
5527
+ try {
5528
+ response = await fetch(options.url, { method: "POST", headers, body: JSON.stringify(operation), signal: options.signal, redirect: "error" });
5529
+ } catch {
5530
+ options.signal?.throwIfAborted();
5531
+ throw new ITMPartnerTransportError(void 0);
5532
+ }
5533
+ if (!response.ok) throw new ITMPartnerTransportError(response.status, response.headers.get("x-request-id") ?? void 0);
5534
+ let value;
5535
+ try {
5536
+ value = await response.json();
5537
+ } catch {
5538
+ options.signal?.throwIfAborted();
5539
+ throw new ITMPartnerTransportError(response.status);
5540
+ }
5541
+ if (!value || typeof value !== "object" || !("data" in value || "errors" in value)) throw new ITMPartnerTransportError(response.status);
5542
+ const result = value;
5543
+ const secrets = Object.entries(headers).filter(([key]) => RESERVED_AGENT_HEADERS.has(key.toLowerCase())).flatMap(([, v]) => [v, v.replace(/^Bearer /i, "")]).filter(Boolean);
5544
+ const redact = (message) => secrets.reduce((text, secret) => text.split(secret).join("[redacted]"), message);
5545
+ if (result.errors) result.errors = result.errors.map((error) => ({ name: "GraphQLError", message: redact(typeof error.message === "string" ? error.message : "GraphQL request failed"), extensions: typeof error.extensions?.code === "string" ? { code: redact(error.extensions.code) } : void 0 }));
5546
+ return result;
5547
+ } });
5548
+ }
5549
+ async function withCancellation(operation, signal) {
5550
+ signal?.throwIfAborted();
5551
+ if (!signal) return operation();
5552
+ let abort = () => {
5553
+ };
5554
+ try {
5555
+ return await Promise.race([
5556
+ operation(),
5557
+ new Promise((_resolve, reject) => {
5558
+ abort = () => reject(signal.reason);
5559
+ signal.addEventListener("abort", abort, { once: true });
5560
+ if (signal.aborted) abort();
5561
+ })
5562
+ ]);
5563
+ } finally {
5564
+ signal.removeEventListener("abort", abort);
5565
+ }
5566
+ }
5485
5567
 
5486
- export { DEFAULT_API_URL, GenqlError, createITMPartnerClient, everything, generateMutationOp, generateQueryOp };
5568
+ export { DEFAULT_API_URL, GenqlError, ITMPartnerTransportError, createITMPartnerClient, everything, generateMutationOp, generateQueryOp };
5487
5569
  //# sourceMappingURL=index.mjs.map
5488
5570
  //# sourceMappingURL=index.mjs.map