npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lgriffin/esi.ts

v2.0.0

Published

This is a TypeScript Implementation for the EVE Online API offered through ESI

Readme

ESI.ts

npm version License: GPL v3 TypeScript

A modern, type-safe TypeScript implementation for the EVE Online ESI API. Built with clean architecture principles, comprehensive error handling, and extensive testing.

🚀 Features

  • Type-Safe: Full TypeScript support with comprehensive type definitions
  • Clean Architecture: Separation of concerns with dependency injection
  • Resilient: Built-in error handling, retry logic, and circuit breakers
  • High Performance: Intelligent ETag caching for optimal bandwidth usage
  • Testable: Extensive test coverage with BDD scenarios
  • Modern: Uses latest TypeScript features and best practices
  • Comprehensive: Covers all ESI API endpoints with organized client structure

📦 Installation

npm install @lgriffin/esi.ts

🔧 Getting Started

ESI.ts offers multiple ways to use the API depending on your needs:

1. Full ESI Client (All APIs)

The complete EsiClient gives you access to all ESI endpoints:

import { EsiClient } from '@lgriffin/esi.ts';

// Full client with all APIs available
const client = new EsiClient({
  clientId: 'your-app-name',
  accessToken: 'your-access-token', // Optional - required for authenticated endpoints
  timeout: 30000,                   // Optional - request timeout in ms
  retryAttempts: 3                  // Optional - number of retry attempts
});

2. Custom Lightweight Client (Selected APIs)

Create a lightweight client with only the APIs you need:

import { CustomEsiClient } from '@lgriffin/esi.ts';

// Lightweight client with only specific APIs
const customClient = new CustomEsiClient({
  clientId: 'my-trading-bot',
  clients: ['characters', 'market', 'universe'] // Only load what you need
});

// Access your selected APIs
const character = await customClient.characters?.getCharacterPublicInfo(123456);
const prices = await customClient.market?.getMarketPrices();

3. Builder Pattern for Custom Clients

Use the builder pattern for more readable client construction:

import { EsiClientBuilder } from '@lgriffin/esi.ts';

// Build a custom client step by step
const client = new EsiClientBuilder()
  .addClient('characters')
  .addClient('corporations')
  .addClients(['market', 'universe'])
  .withClientId('my-corp-manager')
  .withAccessToken('your-token')
  .build();

4. Individual API Clients (Ultra Lightweight)

Create standalone clients for single API groups:

import { EsiApiFactory } from '@lgriffin/esi.ts';

// Just the Character API
const characterClient = EsiApiFactory.createCharacterClient({
  clientId: 'character-lookup-tool'
});

const character = await characterClient.getCharacterPublicInfo(123456);

// Just the Market API
const marketClient = EsiApiFactory.createMarketClient({
  clientId: 'market-analyzer'
});

const prices = await marketClient.getMarketPrices();

5. Direct API Class Instantiation

For maximum control, instantiate API clients directly:

import { CharacterClient, ApiClient, ApiClientBuilder } from '@lgriffin/esi.ts';

// Create the underlying API client
const apiClient = new ApiClientBuilder()
  .withClientId('direct-api-client')
  .withBaseUrl('https://esi.evetech.net')
  .build();

// Create the Character client directly
const characterClient = new CharacterClient(apiClient);
const character = await characterClient.getCharacterPublicInfo(123456);

📊 Client Architecture

Available Client Types

All approaches above give you access to these organized API clients:

// Available clients:
client.alliance      // Alliance information
client.characters    // Character data
client.corporations  // Corporation management
client.market        // Market data and trading
client.universe      // Universe information (systems, stations, items)
client.fleets        // Fleet management
client.industry      // Manufacturing and industry
client.mail          // In-game mail
client.contacts      // Contact management
client.assets        // Asset management
client.wallet        // Wallet operations
client.killmails     // Killmail data
client.location      // Character location
client.skills        // Character skills (if available)
client.factions      // Faction warfare
client.wars          // War information
client.sovereignty   // Sovereignty data
client.incursions    // Incursion information
client.opportunities // Opportunities system
client.fittings      // Ship fittings
client.clones        // Clone management
client.loyalty       // Loyalty points
client.bookmarks     // Bookmark management
client.calendar      // Calendar events
client.contracts     // Contract system
client.insurance     // Insurance information
client.route         // Route planning
client.search        // Search functionality
client.status        // Server status
client.ui            // UI interactions

🎯 Choosing the Right Approach

When to Use Each Method

| Approach | Best For | Memory Usage | Startup Time | |----------|----------|--------------|--------------| | Full EsiClient | Complete applications, multiple API usage | High | Slower | | CustomEsiClient | Focused applications, selected APIs | Medium | Medium | | EsiApiFactory | Single-purpose tools, microservices | Low | Fast | | Direct Instantiation | Libraries, embedded usage | Minimal | Fastest |

Practical Examples by Use Case

Character Lookup Tool (Ultra Lightweight)

import { EsiApiFactory } from '@lgriffin/esi.ts';

const characterClient = EsiApiFactory.createCharacterClient({
  clientId: 'character-lookup-v1'
});

// Just character operations
const character = await characterClient.getCharacterPublicInfo(123456);
const portrait = await characterClient.getCharacterPortrait(123456);
console.log(`${character.body.name} - ${portrait.body.px128x128}`);

Trading Bot (Selected APIs)

import { EsiClientBuilder } from '@lgriffin/esi.ts';

const tradingBot = new EsiClientBuilder()
  .addClients(['market', 'characters', 'universe', 'wallet'])
  .withClientId('trading-bot-v2')
  .withAccessToken(process.env.EVE_ACCESS_TOKEN)
  .build();

// Only the APIs you need are loaded
const prices = await tradingBot.market?.getMarketPrices();
const wallet = await tradingBot.wallet?.getCharacterWallet(characterId);

Corporation Management Dashboard (Custom Client)

import { CustomEsiClient } from '@lgriffin/esi.ts';

const corpManager = new CustomEsiClient({
  clientId: 'corp-dashboard',
  accessToken: directorToken,
  clients: ['corporations', 'characters', 'assets', 'wallet', 'mail']
});

// Efficient corp management with only needed APIs
const corp = await corpManager.corporations?.getCorporationInfo(corpId);
const members = await corpManager.corporations?.getCorporationMembers(corpId);

📋 Common Usage Patterns

Public Data (No Authentication Required)

// Get alliance information
const alliance = await client.alliance.getAllianceById(99005338);
console.log(`Alliance: ${alliance.name} [${alliance.ticker}]`);

// Get character public information
const character = await client.characters.getCharacterPublicInfo(1689391488);
console.log(`Character: ${character.name}`);

// Get corporation information
const corporation = await client.corporations.getCorporationInfo(98742334);
console.log(`Corporation: ${corporation.name} [${corporation.ticker}]`);

// Get market prices
const prices = await client.market.getMarketPrices();
console.log(`Found ${prices.length} market prices`);

// Get solar system information
const system = await client.universe.getSystemById(30000142);
console.log(`System: ${system.name} (Security: ${system.security_status})`);

Authenticated Data (Access Token Required)

// Initialize with access token
const authenticatedClient = new EsiClient({
  clientId: 'your-app-name',
  accessToken: 'your-character-access-token'
});

const characterId = 1689391488;

// Get character's assets
const assets = await authenticatedClient.assets.getCharacterAssets(characterId);
console.log(`Character has ${assets.length} assets`);

// Get character's wallet balance
const wallet = await authenticatedClient.wallet.getCharacterWallet(characterId);
console.log(`Wallet balance: ${wallet.toLocaleString()} ISK`);

// Get character's mail
const mail = await authenticatedClient.mail.getCharacterMail(characterId);
console.log(`Character has ${mail.length} mail messages`);

// Get character's market orders
const orders = await authenticatedClient.market.getCharacterOrders(characterId);
console.log(`Character has ${orders.length} active market orders`);

Complex Workflows

// Character Profile Assembly
async function getCompleteCharacterProfile(characterId: number) {
  const [character, portrait, corporation, location] = await Promise.all([
    client.characters.getCharacterPublicInfo(characterId),
    client.characters.getCharacterPortrait(characterId),
    client.characters.getCharacterPublicInfo(characterId).then(char => 
      client.corporations.getCorporationInfo(char.corporation_id)
    ),
    client.location.getCharacterLocation(characterId)
  ]);

  return {
    character,
    portrait,
    corporation,
    location
  };
}

// Market Analysis
async function analyzeMarketData(regionId: number, typeId: number) {
  const [prices, orders, history] = await Promise.all([
    client.market.getMarketPrices(),
    client.market.getMarketOrders(regionId, { type_id: typeId }),
    client.market.getMarketHistory(regionId, typeId)
  ]);

  const currentPrice = prices.find(p => p.type_id === typeId)?.average_price;
  const buyOrders = orders.filter(o => o.is_buy_order);
  const sellOrders = orders.filter(o => !o.is_buy_order);

  return {
    currentPrice,
    bestBuyPrice: Math.max(...buyOrders.map(o => o.price)),
    bestSellPrice: Math.min(...sellOrders.map(o => o.price)),
    dailyVolume: history[0]?.volume || 0
  };
}

📊 Error Handling

The library provides comprehensive error handling with specific error types:

import { ApiError, ApiErrorType } from '@lgriffin/esi.ts';

try {
  const alliance = await client.alliance.getAllianceById(99999999); // Invalid ID
} catch (error) {
  if (error instanceof ApiError) {
    switch (error.type) {
      case ApiErrorType.NOT_FOUND:
        console.log('Alliance not found');
        break;
      case ApiErrorType.RATE_LIMITED:
        console.log('Rate limited - retry after:', error.retryAfter);
        break;
      case ApiErrorType.SERVER_ERROR:
        console.log('ESI server error:', error.statusCode);
        break;
      case ApiErrorType.NETWORK_ERROR:
        console.log('Network connectivity issue');
        break;
      case ApiErrorType.AUTHENTICATION_ERROR:
        console.log('Invalid or expired access token');
        break;
      default:
        console.log('Unexpected error:', error.message);
    }
  }
}

Graceful Error Handling in Complex Workflows

async function safeCharacterLookup(characterId: number) {
  try {
    // Use Promise.allSettled for partial success scenarios
    const results = await Promise.allSettled([
      client.characters.getCharacterPublicInfo(characterId),
      client.characters.getCharacterPortrait(characterId),
      client.location.getCharacterLocation(characterId)
    ]);

    const profile: any = {};

    if (results[0].status === 'fulfilled') {
      profile.character = results[0].value;
    }
    
    if (results[1].status === 'fulfilled') {
      profile.portrait = results[1].value;
    }
    
    if (results[2].status === 'fulfilled') {
      profile.location = results[2].value;
    } else if (results[2].status === 'rejected') {
      console.log('Location unavailable (character may be offline)');
    }

    return profile;
  } catch (error) {
    console.error('Failed to get character data:', error);
    return null;
  }
}

⚡ Performance & Caching

Intelligent ETag Caching

ESI.ts includes a sophisticated ETag caching system that automatically optimizes API calls by avoiding unnecessary data transfers. This feature is enabled by default and works transparently with all GET requests.

How ETag Caching Works

ETags (Entity Tags) are unique identifiers returned by ESI servers that represent the current version of a resource. When data hasn't changed, the server returns a 304 Not Modified status instead of the full data, dramatically reducing bandwidth usage and improving response times.

Basic Usage (Automatic)

import { EsiClient } from '@lgriffin/esi.ts';

// ETag caching is enabled by default
const client = new EsiClient({
  clientId: 'my-eve-app'
});

// First call - downloads and caches data
const alliances1 = await client.alliance.getAlliances();

// Second call - returns cached data if unchanged (304 response)
const alliances2 = await client.alliance.getAlliances(); // ⚡ Lightning fast!

Custom Cache Configuration

const client = new EsiClient({
  clientId: 'my-eve-app',
  enableETagCache: true, // Default: true
  etagCacheConfig: {
    maxEntries: 1000,      // Max cached responses (default: 1000)
    defaultTtl: 300000,    // Cache TTL in ms (default: 5 minutes)
    cleanupInterval: 60000, // Cleanup frequency (default: 1 minute)
    persistToStorage: true, // Save to localStorage (default: false)
    storageKey: 'my-esi-cache' // Custom storage key
  }
});

Cache Management

// Get cache statistics
const stats = client.getCacheStats();
console.log(`Cache: ${stats.totalEntries}/${stats.maxEntries} entries`);
console.log(`Hit rate optimization: ${stats.hitRate}%`);

// Clear cache manually
client.clearCache();

// Update cache settings at runtime
client.updateCacheConfig({
  maxEntries: 2000,
  defaultTtl: 600000 // 10 minutes
});

// Disable caching for specific use cases
const client = new EsiClient({
  enableETagCache: false // Disable caching entirely
});

Performance Benefits

  • 🚀 Faster Response Times: Cached responses return instantly
  • 📉 Reduced Bandwidth: Avoid downloading unchanged data
  • 🔋 Server-Friendly: Reduces load on ESI servers
  • 💰 Cost Effective: Lower data usage for mobile/metered connections
  • 🎯 Smart Caching: Only caches GET requests with ETags

Cache Behavior

| Scenario | Behavior | Performance Impact | |----------|----------|-------------------| | First API call | Downloads data, stores ETag | Normal speed | | Data unchanged | Returns cached data (304) | ⚡ ~95% faster | | Data changed | Downloads new data, updates cache | Normal speed | | Cache expired | Downloads fresh data | Normal speed | | Cache full | Evicts oldest entries automatically | Minimal impact |

Advanced ETag Features

// Monitor cache performance
client.on('cacheHit', (url, etag) => {
  console.log(`Cache hit for ${url} with ETag ${etag}`);
});

client.on('cacheMiss', (url) => {
  console.log(`Cache miss for ${url} - downloading fresh data`);
});

// Programmatic cache inspection
const cache = client.getETagCache();
if (cache) {
  const entry = cache.get('https://esi.evetech.net/latest/alliances');
  if (entry) {
    console.log(`Cached data age: ${Date.now() - entry.timestamp}ms`);
    console.log(`ETag: ${entry.etag}`);
  }
}

When ETag Caching Helps Most

  • 📊 Market Data: Price lists that update periodically
  • 🏢 Corporation/Alliance Info: Relatively static organizational data
  • 🌌 Universe Data: Star system, station, and type information
  • 👥 Character Lists: Member rosters and public information
  • 📈 Statistics: Aggregate data that updates on intervals

Implementation Details

  • Architecture: Implemented at the core ApiRequestHandler level
  • Scope: Works with ALL GET requests automatically
  • Compatibility: Fully backward compatible - existing code works unchanged
  • Thread Safety: Uses atomic operations for cache management
  • Memory Efficient: Automatic cleanup and size management
  • Storage Options: In-memory (default) or persistent localStorage

🔧 Advanced Configuration

Custom Timeout and Retry Logic

const client = new EsiClient({
  clientId: 'my-eve-app',
  timeout: 60000,      // 60 second timeout
  retryAttempts: 5,    // Retry up to 5 times
  baseUrl: 'https://esi.evetech.net' // Custom ESI endpoint (optional)
});

Using Environment Variables

// Set environment variables
// ESI_CLIENT_ID=your-app-name
// ESI_ACCESS_TOKEN=your-token
// ESI_TIMEOUT=30000

const client = new EsiClient({
  clientId: process.env.ESI_CLIENT_ID,
  accessToken: process.env.ESI_ACCESS_TOKEN,
  timeout: parseInt(process.env.ESI_TIMEOUT || '30000')
});

🚀 Real-World Examples

EVE Market Trading Bot (Lightweight Version)

import { EsiClientBuilder } from '@lgriffin/esi.ts';

class MarketBot {
  private client: any; // CustomEsiClient

  constructor(accessToken: string) {
    // Only load the APIs we actually need
    this.client = new EsiClientBuilder()
      .addClients(['market', 'universe'])
      .withClientId('market-bot-v1')
      .withAccessToken(accessToken)
      .build();
  }

  async findArbitrageOpportunities(regionId: number, typeId: number) {
    try {
      const [orders, history] = await Promise.all([
        this.client.market.getMarketOrders(regionId, { type_id: typeId }),
        this.client.market.getMarketHistory(regionId, typeId)
      ]);

      const buyOrders = orders.filter(o => o.is_buy_order).sort((a, b) => b.price - a.price);
      const sellOrders = orders.filter(o => !o.is_buy_order).sort((a, b) => a.price - b.price);

      if (buyOrders.length > 0 && sellOrders.length > 0) {
        const spread = buyOrders[0].price - sellOrders[0].price;
        const spreadPercent = (spread / sellOrders[0].price) * 100;

        return {
          profitable: spread > 0,
          spread,
          spreadPercent,
          bestBuy: buyOrders[0],
          bestSell: sellOrders[0],
          dailyVolume: history[0]?.volume || 0
        };
      }

      return null;
    } catch (error) {
      console.error('Failed to analyze market:', error);
      return null;
    }
  }
}

Corporation Management Dashboard (Custom Client)

import { CustomEsiClient } from '@lgriffin/esi.ts';

class CorporationManager {
  private client: CustomEsiClient;

  constructor(accessToken: string) {
    // Only load corporation-related APIs
    this.client = new CustomEsiClient({
      clientId: 'corp-manager',
      accessToken,
      clients: ['corporations', 'characters', 'wallet']
    });
  }

  async getCorporationOverview(corporationId: number) {
    try {
      const [corp, members, wallets] = await Promise.all([
        this.client.corporations.getCorporationInfo(corporationId),
        this.client.corporations.getCorporationMembers(corporationId),
        this.client.wallet.getCorporationWallets(corporationId)
      ]);

      return {
        corporation: corp,
        memberCount: members.length,
        totalBalance: wallets.reduce((sum, wallet) => sum + wallet.balance, 0)
      };
    } catch (error) {
      if (error instanceof ApiError && error.type === ApiErrorType.AUTHENTICATION_ERROR) {
        throw new Error('Insufficient permissions to access corporation data');
      }
      throw error;
    }
  }
}

Simple Character Lookup (Direct API)

import { EsiApiFactory } from '@lgriffin/esi.ts';

// Ultra-lightweight: just one API, one function
async function lookupCharacter(characterId: number) {
  const characterClient = EsiApiFactory.createCharacterClient({
    clientId: 'simple-lookup'
  });

  const character = await characterClient.getCharacterPublicInfo(characterId);
  return character.body.name;
}

// Usage
const name = await lookupCharacter(1689391488);
console.log(name); // "deiseman"

Direct API Class Usage

import { CharacterClient, ApiClientBuilder } from '@lgriffin/esi.ts';

// Maximum control - build exactly what you need
const apiClient = new ApiClientBuilder()
  .setClientId('direct-usage')
  .setLink('https://esi.evetech.net')
  .build();

const characterClient = new CharacterClient(apiClient);

// Direct usage without any wrapper
const character = await characterClient.getCharacterPublicInfo(123456);
const portrait = await characterClient.getCharacterPortrait(123456);

Microservice Example (Single Responsibility)

import { EsiApiFactory } from '@lgriffin/esi.ts';

// A microservice that only needs market data
class PriceService {
  private marketClient;

  constructor() {
    // Only load what this service needs
    this.marketClient = EsiApiFactory.createMarketClient({
      clientId: 'price-service-v1'
    });
  }

  async getCurrentPrice(typeId: number): Promise<number> {
    const prices = await this.marketClient.getMarketPrices();
    const price = prices.body.find((p: any) => p.type_id === typeId);
    return price?.average_price || 0;
  }

  async getBestPrices(regionId: number, typeId: number) {
    const orders = await this.marketClient.getMarketOrders(regionId, { type_id: typeId });
    
    const buyOrders = orders.body.filter((o: any) => o.is_buy_order);
    const sellOrders = orders.body.filter((o: any) => !o.is_buy_order);
    
    return {
      bestBuy: Math.max(...buyOrders.map((o: any) => o.price)),
      bestSell: Math.min(...sellOrders.map((o: any) => o.price))
    };
  }
}

🧪 Testing

Running Tests

# Run unit tests
npm test

# Run unit tests with coverage
npm run coverage

# Run BDD tests (behavioral scenarios)
npm run bdd

# Run specific BDD test suites
npm run bdd:alliance
npm run bdd:character
npm run bdd:market

# Run all tests
npm run test:all

🚀 Working Examples

Try the Examples

ESI.ts includes working examples that demonstrate real API usage:

# Run the complete character profile example
npm run example

# Run flexible API usage examples (all 5 approaches)
npm run examples:flexible

Character Profile Example

The main example (npm run example) demonstrates:

  • ✅ Complete character profile assembly
  • ✅ Parallel API calls for efficiency
  • ✅ Error handling and graceful degradation
  • ✅ Resource cleanup
  • ✅ Performance timing

Sample Output:

🚀 ESI.ts Character Profile Example
=====================================

🔍 Gathering complete profile for character ID: 1689391488
📋 Fetching basic character information...
🚀 Fetching detailed profile data in parallel...

============================================================
🎯 CHARACTER PROFILE SUMMARY
============================================================
👤 Name: deiseman
🆔 Character ID: 1689391488
🎂 Birthday: 2/3/2008
⚖️  Security Status: 0.16

🏢 Corporation: Brittas Empire [BREMP]
👥 Members: 360
🤝 Alliance: Pandemic Horde [REKTD]
📊 Founded: 2/4/2015

🖼️  Portrait URLs:
   📱 64x64: https://images.evetech.net/characters/1689391488/portrait?tenant=tranquility&size=64
   🖥️  128x128: https://images.evetech.net/characters/1689391488/portrait?tenant=tranquility&size=128
   🖼️  256x256: https://images.evetech.net/characters/1689391488/portrait?tenant=tranquility&size=256
   📺 512x512: https://images.evetech.net/characters/1689391488/portrait?tenant=tranquility&size=512

📍 Current Location: Unavailable (character may be offline)
============================================================

⏱️  Total execution time: 154ms
✅ Character profile retrieved successfully!

Flexible API Examples

The flexible examples (npm run examples:flexible) demonstrate:

  • ✅ Full ESI Client (all APIs)
  • ✅ Custom lightweight client (selected APIs)
  • ✅ Builder pattern usage
  • ✅ Individual API clients
  • ✅ Direct API class instantiation
  • ✅ Performance comparisons
  • ✅ Microservice example

Sample Output:

🚀 ESI.ts Flexible API Usage Examples
=====================================

1️⃣  Full ESI Client (All APIs)
✅ Character: deiseman (using full client)

2️⃣  Custom Lightweight Client (Selected APIs)
✅ Character: deiseman (using custom client)
📊 Enabled clients: characters, corporations

3️⃣  Builder Pattern
✅ Character: deiseman (using builder pattern)

4️⃣  Individual API Client (Ultra Lightweight)
✅ Character: deiseman (using standalone client)

5️⃣  Direct API Class Instantiation
✅ Character: deiseman (using direct instantiation)

🎯 Performance Comparison
=========================
⏱️  Startup time comparison:
   Full Client: 1ms
   Custom Client (1 API): 0ms
   Individual Client: 0ms

💰 Microservice Example: Price Service
======================================
💎 Tritanium average price: 3.78 ISK

Example Files

| File | Purpose | Command | |------|---------|---------| | src/index.ts | Character profile assembly | npm run example | | demo/flexible-examples.ts | All flexible API patterns | npm run examples:flexible |

Learning Path

  1. Start Here: Run npm run example to see a complete real-world workflow
  2. Explore Options: Run npm run examples:flexible to see all the different ways to use the API
  3. Choose Your Approach: Pick the method that best fits your use case
  4. Build Your App: Use the examples as templates for your own application

Testing Your Applications

import { EsiClient } from '@lgriffin/esi.ts';
import { TestDataFactory } from '@lgriffin/esi.ts';

describe('My EVE Application', () => {
  let client: EsiClient;

  beforeEach(() => {
    client = new EsiClient({
      clientId: 'test-client'
    });
  });

  afterEach(async () => {
    await client.shutdown();
  });

  it('should handle character lookup', async () => {
    // Mock data for testing
    jest.spyOn(client.characters, 'getCharacterPublicInfo')
      .mockResolvedValue(TestDataFactory.createCharacterInfo({
        character_id: 123456,
        name: 'Test Character'
      }));

    const character = await client.characters.getCharacterPublicInfo(123456);
    expect(character.name).toBe('Test Character');
  });
});

🛠️ Resource Management

Proper Cleanup

// Always clean up resources when done
async function myApplication() {
  const client = new EsiClient({
    clientId: 'my-app'
  });

  try {
    // Your application logic here
    const alliance = await client.alliance.getAllianceById(99005338);
    console.log(alliance.name);
  } finally {
    // Important: Always shutdown the client
    await client.shutdown();
  }
}

// Or use a try-with-resources pattern
class EsiClientManager {
  private client: EsiClient;

  constructor(config: any) {
    this.client = new EsiClient(config);
  }

  async use<T>(callback: (client: EsiClient) => Promise<T>): Promise<T> {
    try {
      return await callback(this.client);
    } finally {
      await this.client.shutdown();
    }
  }
}

// Usage
const manager = new EsiClientManager({ clientId: 'my-app' });
const result = await manager.use(async (client) => {
  return await client.alliance.getAllianceById(99005338);
});

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for your changes
  4. Ensure all tests pass: npm test && npm run bdd
  5. Open a Pull Request

📄 License

GPL-3.0-or-later - see the LICENSE file for details.


Happy coding, capsuleers! o7