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.
npm install @attestd/sdknew Client(options)
All options are passed as a single object. Only apiKey is required.
| Option | Description |
|---|---|
apiKey | Required. Your Attestd API key. |
baseUrl | Base URL override. Defaults to "https://api.attestd.io". |
timeoutMs | Request timeout in milliseconds. Default: 10000. |
maxRetries | Retry attempts on 5xx errors. Default: 3. Set to 0 to disable. |
fetch | fetch override for testing. Pass mock.fn from @attestd/sdk/testing. |
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.
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.
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;
}SupplyChainSignal
Supply chain integrity data for monitored PyPI packages. Present on RiskResult as the supplyChain field. null for CVE-only products (nginx, PostgreSQL, etc.).
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:
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 === nullNote: riskState can be 'none' while supplyChain.compromised is true. These are two independent signals. See the Supply Chain Integrity guide.
Error types
All error classes extend AttestdError extends Error. Each uses Object.setPrototypeOf in its constructor so instanceof works correctly in transpiled CommonJS environments.
| Class | When thrown |
|---|---|
AttestdAuthError | HTTP 401. Invalid or missing API key. |
AttestdRateLimitError | HTTP 429. Has retryAfter property (seconds). |
AttestdUnsupportedProductError | supported: false. Has product and version properties. |
AttestdAPIError | HTTP 5xx, network failure, timeout, or malformed response. statusCode is 0 for transport errors. |
AttestdError | Base 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).
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}`);
}
}@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.
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.
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.
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' });