Skip to content

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

Email

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

type ThreatLevel = 'safe' | 'low' | 'medium' | 'high' | 'critical';

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

@spot:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${GITLAB_TOKEN}

Troubleshooting

Cannot find package

Ensure npm is configured to use the GitLab registry:

npm config set @spot:registry https://registry.npmjs.org/
npm install @spot/contracts

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:

{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

Support