TypeScript SDK¶
Complete guide for using the SPOT Contracts TypeScript SDK.
Installation¶
# Configure registry
npm config set @spot:registry https://registry.npmjs.org/
# Install package
npm install @spot/contracts
Dependencies¶
- axios >= 1.6.0
- TypeScript >= 5.3.0
Quick Start¶
import {
Email,
EmailHeader,
AnalysisResult,
AnalysisIndicator,
ThreatLevel
} from '@spot/contracts';
// Create email object
const email: Email = {
id: 'test-123',
headers: {
subject: 'Test email',
sender: 'test@example.com',
recipients: ['user@company.com']
},
body_text: 'Email content here'
};
// Create analysis result
const result: AnalysisResult = {
is_phishing: true,
threat_level: 'high',
confidence: 0.85,
explanation: 'Found suspicious indicators',
indicators: [
{
type: 'suspicious_links',
description: 'Suspicious domain detected',
severity: 'high',
confidence: 0.9
}
],
metadata: {
analyzer_id: 'my-analyzer',
analyzer_version: '1.0.0',
analysis_duration_ms: 250
}
};
// Serialize to JSON
const jsonData = JSON.stringify(result, null, 2);
console.log(jsonData);
Available Types¶
Email Types¶
interface Email {
id: string;
headers: EmailHeader;
body_text?: string;
body_html?: string;
attachments?: Attachment[];
}
EmailHeader
interface EmailHeader {
subject: string;
sender: string;
recipients: string[];
cc?: string[];
bcc?: string[];
date?: string;
message_id?: string;
in_reply_to?: string;
references?: string[];
}
Attachment
interface Attachment {
filename: string;
content_type: string;
size: number;
content: string; // base64-encoded
content_id?: string;
}
Analysis Types¶
AnalysisResult
interface AnalysisResult {
is_phishing: boolean;
threat_level: ThreatLevel; // 'safe' | 'low' | 'medium' | 'high' | 'critical'
confidence: number; // 0.0-1.0
explanation: string;
indicators: AnalysisIndicator[];
metadata: AnalysisMetadata;
raw_output?: Record<string, any>;
}
AnalysisIndicator
interface AnalysisIndicator {
type: IndicatorType | string;
description: string;
severity: 'low' | 'medium' | 'high';
confidence: number; // 0.0-1.0
evidence?: string;
location?: string;
}
ThreatLevel
IndicatorType
type IndicatorType =
| 'suspicious_links'
| 'domain_spoofing'
| 'urgent_language'
| 'spelling_grammar_errors'
| 'social_engineering'
| 'suspicious_attachments'
| 'context_mismatch'
| 'header_anomalies';
AnalysisMetadata
interface AnalysisMetadata {
analyzer_id: string;
analyzer_version: string;
analysis_duration_ms: number;
model_version?: string;
timestamp?: string;
}
Workflow Types¶
Workflow
interface Workflow {
id: string;
name: string;
description?: string;
stages: WorkflowStage[];
enabled: boolean;
}
WorkflowStage
interface WorkflowStage {
name: string;
analyzers: AnalyzerConfig[];
min_success?: number;
continue_on_failure?: boolean;
}
Orchestrator Types¶
OrchestrationResult
interface OrchestrationResult {
job_id: string;
email_id: string;
workflow_id: string;
overall_verdict: string;
overall_confidence: number;
analyzer_results: AnalyzerResult[];
timestamp: string;
}
AnalyzerResult
interface AnalyzerResult {
analyzer_id: string;
status: 'success' | 'failed' | 'timeout';
result?: AnalysisResult;
error?: string;
duration_ms: number;
}
API Client Example¶
import axios, { AxiosInstance, AxiosError } from 'axios';
import {
Email,
EmailHeader,
AnalysisResult,
AnalysisResponse
} from '@spot/contracts';
class SpotApiClient {
private client: AxiosInstance;
constructor(
baseURL: string = 'http://localhost:8001',
token?: string
) {
const headers: Record<string, string> = {
'Content-Type': 'application/json'
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
this.client = axios.create({
baseURL,
headers,
timeout: 30000
});
}
async analyzeEmail(email: Email): Promise<AnalysisResponse> {
try {
const response = await this.client.post<AnalysisResponse>(
'/api/v1/analyze',
{ email }
);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(
`Analysis failed: ${error.response?.data?.message || error.message}`
);
}
throw error;
}
}
async getStatus(jobId: string): Promise<any> {
try {
const response = await this.client.get(`/api/v1/analyze/${jobId}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(
`Status check failed: ${error.response?.data?.message || error.message}`
);
}
throw error;
}
}
async healthCheck(): Promise<any> {
const response = await this.client.get('/health');
return response.data;
}
}
// Usage example
async function main() {
const client = new SpotApiClient('http://localhost:8001', 'your-token');
// Create email
const email: Email = {
id: 'test-123',
headers: {
subject: 'Urgent: Verify your account',
sender: 'noreply@suspicious-domain.com',
recipients: ['user@company.com']
},
body_text: 'Click here to verify: http://bit.ly/xyz123'
};
try {
// Analyze email
const response = await client.analyzeEmail(email);
console.log(`Job ID: ${response.job_id}`);
console.log(`Status: ${response.status}`);
// Check status
const status = await client.getStatus(response.job_id);
console.log(`Analysis complete: ${status.result?.is_phishing}`);
console.log(`Threat Level: ${status.result?.threat_level}`);
console.log(`Confidence: ${status.result?.confidence}`);
// Print indicators
status.result?.indicators?.forEach((indicator: AnalysisIndicator) => {
console.log(` - ${indicator.type}: ${indicator.description}`);
});
} catch (error) {
console.error('Error:', error);
}
}
main();
React Integration Example¶
import React, { useState } from 'react';
import { Email, AnalysisResult } from '@spot/contracts';
import { SpotApiClient } from './api-client';
interface EmailAnalyzerProps {
apiUrl: string;
token: string;
}
const EmailAnalyzer: React.FC<EmailAnalyzerProps> = ({ apiUrl, token }) => {
const [email, setEmail] = useState<Email>({
id: '',
headers: {
subject: '',
sender: '',
recipients: []
},
body_text: ''
});
const [result, setResult] = useState<AnalysisResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const client = new SpotApiClient(apiUrl, token);
const handleAnalyze = async () => {
setLoading(true);
setError(null);
try {
const response = await client.analyzeEmail(email);
const status = await client.getStatus(response.job_id);
setResult(status.result);
} catch (err) {
setError(err instanceof Error ? err.message : 'Analysis failed');
} finally {
setLoading(false);
}
};
return (
<div className="email-analyzer">
<h2>Email Spear-Phishing Analyzer</h2>
<input
type="text"
placeholder="Subject"
value={email.headers.subject}
onChange={(e) =>
setEmail({
...email,
headers: { ...email.headers, subject: e.target.value }
})
}
/>
<textarea
placeholder="Email body"
value={email.body_text}
onChange={(e) => setEmail({ ...email, body_text: e.target.value })}
/>
<button onClick={handleAnalyze} disabled={loading}>
{loading ? 'Analyzing...' : 'Analyze Email'}
</button>
{error && <div className="error">{error}</div>}
{result && (
<div className="result">
<h3>Analysis Result</h3>
<p>
<strong>Phishing:</strong> {result.is_phishing ? 'Yes' : 'No'}
</p>
<p>
<strong>Threat Level:</strong> {result.threat_level}
</p>
<p>
<strong>Confidence:</strong> {(result.confidence * 100).toFixed(1)}%
</p>
<p>
<strong>Explanation:</strong> {result.explanation}
</p>
{result.indicators && result.indicators.length > 0 && (
<div>
<h4>Indicators:</h4>
<ul>
{result.indicators.map((indicator, index) => (
<li key={index}>
<strong>{indicator.type}</strong>: {indicator.description}
<span className={`severity-${indicator.severity}`}>
({indicator.severity})
</span>
</li>
))}
</ul>
</div>
)}
</div>
)}
</div>
);
};
export default EmailAnalyzer;
Type Guards¶
import { ThreatLevel, AnalysisResult } from '@spot/contracts';
// Type guard for ThreatLevel
function isThreatLevel(value: string): value is ThreatLevel {
return ['safe', 'low', 'medium', 'high', 'critical'].includes(value);
}
// Type guard for AnalysisResult
function isAnalysisResult(obj: any): obj is AnalysisResult {
return (
typeof obj === 'object' &&
typeof obj.is_phishing === 'boolean' &&
typeof obj.threat_level === 'string' &&
typeof obj.confidence === 'number' &&
typeof obj.explanation === 'string' &&
Array.isArray(obj.indicators) &&
typeof obj.metadata === 'object'
);
}
// Usage
function processResult(data: unknown) {
if (isAnalysisResult(data)) {
console.log(`Phishing: ${data.is_phishing}`);
console.log(`Threat: ${data.threat_level}`);
} else {
throw new Error('Invalid analysis result');
}
}
Testing with SDK¶
import { describe, it, expect, vi } from 'vitest';
import { Email, AnalysisResult } from '@spot/contracts';
import { SpotApiClient } from './api-client';
describe('SpotApiClient', () => {
it('should create email object', () => {
const email: Email = {
id: 'test-1',
headers: {
subject: 'Test',
sender: 'test@example.com',
recipients: ['user@example.com']
},
body_text: 'Test content'
};
expect(email.id).toBe('test-1');
expect(email.headers.subject).toBe('Test');
});
it('should analyze email', async () => {
const client = new SpotApiClient('http://mock-api');
const email: Email = {
id: 'test-1',
headers: {
subject: 'Test',
sender: 'test@example.com',
recipients: ['user@example.com']
}
};
// Mock the API response
const mockResponse = {
job_id: 'job-123',
status: 'completed',
result: {
is_phishing: false,
threat_level: 'safe',
confidence: 0.1,
explanation: 'No threats detected',
indicators: [],
metadata: {
analyzer_id: 'test',
analyzer_version: '1.0.0',
analysis_duration_ms: 100
}
}
};
vi.spyOn(client as any, 'analyzeEmail').mockResolvedValue(mockResponse);
const result = await client.analyzeEmail(email);
expect(result.job_id).toBe('job-123');
});
});
Configuration¶
Using with package.json¶
{
"dependencies": {
"@spot/contracts": "^2.0.0"
},
"resolutions": {
"@spot/contracts": "https://registry.npmjs.org/@spot/contracts/-/@spot/contracts-2.0.0.tgz"
}
}
Using with .npmrc¶
Troubleshooting¶
Cannot find package¶
Ensure npm is configured to use the GitLab registry:
Import errors¶
Check that you're using the correct import paths:
// Correct
import { Email, AnalysisResult } from '@spot/contracts';
// Incorrect
import Email from '@spot/contracts'; // Wrong syntax
Type errors¶
Enable strict type checking in tsconfig.json:
Support¶
- Issues: GitLab Issues
- Documentation: Main Documentation
- SPOT Platform: Platform Docs