sdk reference / javascript

JavaScript SDK Reference

The @attestd/sdk package is a TypeScript-first client for Node 18+. Zero runtime dependencies — wraps the native fetch API. Dual ESM + CommonJS build works in any modern Node project or bundler.

bash
npm install @attestd/sdk
client

new Client(options)

All options are passed as a single object. Only apiKey is required.

OptionDescription
apiKeyRequired. Your Attestd API key.
baseUrlBase URL override. Defaults to "https://api.attestd.io".
timeoutMsRequest timeout in milliseconds. Default: 10000.
maxRetriesRetry attempts on 5xx errors. Default: 3. Set to 0 to disable.
fetchfetch override for testing. Pass mock.fn from @attestd/sdk/testing.
check.ts
import { Client } from '@attestd/sdk';

const client = new Client({ apiKey: process.env.ATTESTD_API_KEY });

const result = await client.check('nginx', '1.24.0');
console.log(result.riskState);       // 'high'
console.log(result.activelyExploited); // false
console.log(result.cveIds);          // ['CVE-2021-23017']

client.check(product, version) returns Promise<RiskResult>. The client is stateless — a single instance is safe to reuse across concurrent calls.

models

RiskResult

Fields mirror the Python SDK in camelCase. All Date fields are parsed from ISO strings at response time — you get real Date objects, not strings. See the Response Field Reference for field semantics.

typescript
interface RiskResult {
  product: string;
  version: string;
  riskState: RiskState;          // 'critical' | 'high' | 'elevated' | 'low' | 'none'
  riskFactors: RiskFactor[];
  activelyExploited: boolean;
  remoteExploitable: boolean;
  authenticationRequired: boolean;
  patchAvailable: boolean;
  fixedVersion: string | null;
  confidence: number;
  cveIds: string[];
  supplyChain: SupplyChainSignal | null;  // null for CVE-only products
  lastUpdated: Date;
}
response

SupplyChainSignal

Supply chain integrity data for monitored PyPI packages. Present on RiskResult as the supplyChain field. null for CVE-only products (nginx, PostgreSQL, etc.).

typescript
interface SupplyChainSignal {
  compromised: boolean;
  sources: string[];
  malwareType: string | null;
  description: string | null;
  advisoryUrl: string | null;
  compromisedAt: Date | null;
  removedAt: Date | null;
}

Usage example — check for compromised PyPI packages:

check_supply_chain.ts
import { Client } from '@attestd/sdk';

const client = new Client({ apiKey: process.env.ATTESTD_API_KEY });

// Check a monitored PyPI package
const result = await client.check('langchain', '0.1.0');

if (result.supplyChain?.compromised) {
  console.error('SUPPLY CHAIN ALERT:', result.supplyChain.description);
  process.exit(1);
}

// CVE-only products have supplyChain = null
const nginx = await client.check('nginx', '1.24.0');
// nginx.supplyChain === null

Note: riskState can be 'none' while supplyChain.compromised is true. These are two independent signals. See the Supply Chain Integrity guide.

errors

Error types

All error classes extend AttestdError extends Error. Each uses Object.setPrototypeOf in its constructor so instanceof works correctly in transpiled CommonJS environments.

ClassWhen thrown
AttestdAuthErrorHTTP 401. Invalid or missing API key.
AttestdRateLimitErrorHTTP 429. Has retryAfter property (seconds).
AttestdUnsupportedProductErrorsupported: false. Has product and version properties.
AttestdAPIErrorHTTP 5xx, network failure, timeout, or malformed response. statusCode is 0 for transport errors.
AttestdErrorBase class. Catch this for a single broad handler.

401 and 429 are never retried. Only transient 5xx errors and network failures are retried (up to maxRetries, exponential backoff: 1s, 2s, 4s).

errors.ts
import {
  Client,
  AttestdAuthError,
  AttestdRateLimitError,
  AttestdUnsupportedProductError,
  AttestdAPIError,
} from '@attestd/sdk';

const client = new Client({ apiKey: process.env.ATTESTD_API_KEY });

try {
  const result = await client.check('nginx', '1.24.0');
} catch (err) {
  if (err instanceof AttestdAuthError) {
    console.error('Invalid API key');
  } else if (err instanceof AttestdRateLimitError) {
    console.error(`Rate limited. Retry in ${err.retryAfter}s`);
  } else if (err instanceof AttestdUnsupportedProductError) {
    // Product is outside coverage — not a safety clearance.
    // Make an explicit policy decision: block, warn, or skip.
    console.warn(`${err.product} is outside attestd coverage`);
    throw err;
  } else if (err instanceof AttestdAPIError) {
    console.error(`API error (${err.statusCode}): ${err.message}`);
  }
}
testing

@attestd/sdk/testing

A separate subpath export — not included in the main bundle. Import only in test files. Provides typed mock fetch implementations and fixture bodies so you can test your gate logic without making real API calls.

MockFetch

Returns the same response for every call.

gate.test.ts
import { Client } from '@attestd/sdk';
import { MockFetch, NGINX_VULNERABLE, NGINX_SAFE } from '@attestd/sdk/testing';

test('blocks deployment on high risk', async () => {
  const mock = new MockFetch(200, NGINX_VULNERABLE);
  const client = new Client({ apiKey: 'test', fetch: mock.fn });

  const result = await client.check('nginx', '1.20.0');
  expect(result.riskState).toBe('high');
  expect(mock.callCount).toBe(1);
});

test('allows deployment when safe', async () => {
  const mock = new MockFetch(200, NGINX_SAFE);
  const client = new Client({ apiKey: 'test', fetch: mock.fn });

  const result = await client.check('nginx', '1.27.4');
  expect(result.riskState).toBe('none');
});

SequentialMockFetch

Returns responses from a pre-defined sequence. Use for testing retry logic where early requests fail before a success.

retry.test.ts
import { Client } from '@attestd/sdk';
import { SequentialMockFetch, NGINX_SAFE } from '@attestd/sdk/testing';

test('retries on 503', async () => {
  const mock = new SequentialMockFetch([
    [503, {}],         // first attempt fails
    [503, {}],         // second attempt fails
    [200, NGINX_SAFE], // third attempt succeeds
  ]);
  const client = new Client({ apiKey: 'test', fetch: mock.fn, maxRetries: 2 });

  const result = await client.check('nginx', '1.27.4');
  expect(result.riskState).toBe('none');
  expect(mock.callCount).toBe(3);
});

Canned response bodies

Ready-made response fixtures for common test scenarios. All are plain objects and can be spread to override individual fields.

fixtures.ts
import {
  NGINX_SAFE,                       // riskState: 'none'
  NGINX_VULNERABLE,                 // riskState: 'high'
  LOG4J_CRITICAL,                   // riskState: 'critical', activelyExploited: true
  UNSUPPORTED,                      // supported: false
  LITELLM_COMPROMISED,              // supplyChain.compromised: true
  PYTORCH_LIGHTNING_COMPROMISED,    // supplyChain.compromised: true
} from '@attestd/sdk/testing';

// Override individual fields
const mock = new MockFetch(200, { ...NGINX_VULNERABLE, risk_state: 'critical' });
related