swcombine-sdk
v1.2.1
Published
Comprehensive TypeScript SDK for Star Wars Combine API v2.0
Maintainers
Readme
SW Combine SDK for Node.js
Comprehensive TypeScript SDK for the Star Wars Combine API v2.0
Features • Installation • Quick Start • Documentation • Examples
Features
- Full API Coverage - All 60+ endpoints across 11 resource categories
- OAuth 2.0 Built-in - Complete OAuth flow with automatic token refresh
- TypeScript First - Full type definitions with IntelliSense support
- Type-Safe Scopes - 170+ OAuth scope constants with autocomplete
- Automatic Retries - Exponential backoff for failed requests
- Modern & Universal - ES Modules + CommonJS, Node.js 18+
- Developer Tools - Helper scripts for OAuth and testing
- Zero Dependencies (except axios)
Installation
npm install swcombine-sdk
# or
yarn add swcombine-sdk
# or
pnpm add swcombine-sdkQuick Start
1. Initialize the Client
import { SWCombine } from 'swcombine-sdk';
const client = new SWCombine({
clientId: process.env.SWC_CLIENT_ID!,
clientSecret: process.env.SWC_CLIENT_SECRET!,
token: process.env.SWC_ACCESS_TOKEN, // Optional - can be string or OAuthToken object
});2. Make API Calls
// Get public character information (no auth required)
const character = await client.character.getByHandle({
handle: 'character-handle',
});
console.log(character.uid); // "1:12345"
console.log(character.name); // "Character Name"3. Authenticated Endpoints
// For authenticated endpoints, provide an access token
const authenticatedClient = new SWCombine({
clientId: process.env.SWC_CLIENT_ID!,
clientSecret: process.env.SWC_CLIENT_SECRET!,
token: process.env.SWC_ACCESS_TOKEN!,
});
// Get character details
const character = await authenticatedClient.character.get({
uid: '1:12345',
});
// Get character messages
const messages = await authenticatedClient.character.messages.list({
uid: '1:12345',
mode: 'received',
});
// Get faction information
const faction = await authenticatedClient.faction.get({
uid: '20:123',
});OAuth Authentication
Quick OAuth Setup
Use the included helper script to get an access token:
# 1. Add your credentials to .env
echo "SWC_CLIENT_ID=your_client_id" >> .env
echo "SWC_CLIENT_SECRET=your_client_secret" >> .env
# 2. Run the OAuth helper
npm run get-token
# 3. Visit http://localhost:3000 in your browser
# 4. Authorize the app and copy the token to .envManual OAuth Flow
import { SWCombine, CharacterScopes, MessageScopes } from 'swcombine-sdk';
const client = new SWCombine({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/callback',
accessType: 'offline', // Get refresh token
});
// 1. Generate authorization URL
const authUrl = client.auth.getAuthorizationUrl({
scopes: [
CharacterScopes.READ,
CharacterScopes.STATS,
MessageScopes.READ,
MessageScopes.SEND,
],
state: 'random-csrf-token',
});
// 2. Redirect user to authUrl...
// 3. Handle callback
const result = await client.auth.handleCallback(req.query);
if (result.success) {
const token = result.token!;
console.log('Access Token:', token.accessToken);
console.log('Refresh Token:', token.refreshToken);
}Type-Safe OAuth Scopes
import {
CharacterScopes,
MessageScopes,
Scopes,
getAllScopes,
getReadOnlyScopes,
} from 'swcombine-sdk';
// Use constants with autocomplete
const scopes = [
CharacterScopes.READ, // TypeScript suggests all scopes
CharacterScopes.STATS,
MessageScopes.SEND,
Scopes.PersonalInventory.SHIPS.READ,
];
// Or use helpers
const readOnly = getReadOnlyScopes();
const everything = getAllScopes();See OAuth Scopes Guide for all 170+ available scopes.
API Resources
The SDK provides access to all SW Combine API resources:
API Utilities
// Get list of available API resources
await client.api.getResources();
// Test connectivity
await client.api.helloWorld();
await client.api.helloAuth(); // Requires authentication
// Get available permissions and rate limits
await client.api.permissions();
await client.api.rateLimits();
// Time conversion (CGT = Combine Galactic Time)
await client.api.time(); // Current time: { years, days, hours, mins, secs }
await client.api.time({ cgt: 'Y26D100' }); // CGT to Unix timestamp
await client.api.time({ time: 1701432000 }); // Unix timestamp to CGTCharacters
// Public endpoints
await client.character.getByHandle({ handle: 'character-handle' });
// Authenticated endpoints
await client.character.me(); // Get authenticated user's character
await client.character.get({ uid: '1:12345' });
await client.character.skills.list({ uid: '1:12345' });
await client.character.privileges.list({ uid: '1:12345' });
await client.character.privileges.get({ uid: '1:12345', privilegeGroup: 'group', privilege: 'name' });
await client.character.credits.get({ uid: '1:12345' }); // Returns credit balance as number
await client.character.credits.transfer({ uid: '1:12345', amount: 1000, recipient: '1:67890' });
await client.character.creditlog.list({ uid: '1:12345' });
await client.character.permissions.list({ uid: '1:12345' });
await client.character.hasPermission({ uid: '1:12345', permission: 'CHARACTER_READ' });
// Messages
await client.character.messages.list({ uid: '1:12345' }); // All messages (sent + received)
await client.character.messages.list({ uid: '1:12345', mode: 'received' }); // Only received
await client.character.messages.list({ uid: '1:12345', mode: 'sent' }); // Only sent
await client.character.messages.get({ uid: '1:12345', messageId: 'msg-123' });
await client.character.messages.create({ uid: '1:12345', receivers: 'recipient1;recipient2', communication: 'Hello!' });
await client.character.messages.delete({ uid: '1:12345', messageId: 'msg-123' });Factions
await client.faction.list();
await client.faction.get({ uid: '20:123' });
await client.faction.members.list({ factionId: '20:123' });
await client.faction.budgets.list({ factionId: '20:123' });
await client.faction.budgets.get({ factionId: '20:123', budgetId: 'budget-uid' });
await client.faction.stockholders.list({ factionId: '20:123' });
await client.faction.credits.get({ factionId: '20:123' });
await client.faction.credits.update({ factionId: '20:123', amount: 1000, recipient: '1:12345' });
await client.faction.creditlog.list({ factionId: '20:123' });Inventory
await client.inventory.get({ uid: '1:12345' });
await client.inventory.entities.list({
uid: '1:12345',
entityType: 'ships', // ships, vehicles, stations, cities, facilities, planets, items, npcs, droids, creatures, materials
assignType: 'owner', // owner, commander, pilot
});
await client.inventory.entities.get({ entityType: 'ships', uid: '5:12345' });
// Entity management
await client.inventory.entities.updateProperty({
entityType: 'ships',
uid: '5:12345',
property: 'name', // name, owner, commander, pilot, infotext, etc.
new_value: 'New Ship Name',
});
await client.inventory.entities.addTag({ entityType: 'ships', uid: '5:12345', tag: 'favorite' });
await client.inventory.entities.removeTag({ entityType: 'ships', uid: '5:12345', tag: 'favorite' });Galaxy
// Systems, planets, sectors
await client.galaxy.systems.list();
await client.galaxy.systems.get({ uid: '24:123' });
await client.galaxy.planets.list();
await client.galaxy.planets.get({ uid: '23:456' });
await client.galaxy.sectors.list();
await client.galaxy.sectors.get({ uid: 'seswenna' }); // Use lowercase sector name
// Stations and cities
await client.galaxy.stations.list();
await client.galaxy.stations.get({ uid: '6:789' });
await client.galaxy.cities.list();
await client.galaxy.cities.get({ uid: '22:101' });Events
// Note: Events uses 0-based indexing unlike other endpoints
await client.events.list({ eventMode: 'personal' }); // personal, faction, inventory, combat
await client.events.list({ eventMode: 'personal', start_index: 0, item_count: 100 }); // omit eventType to get all
await client.events.list({ eventMode: 'personal', eventType: 'xp' }); // filter by specific type
await client.events.get({ uid: 'event-uid' });Location
await client.location.get({ entityType: 'characters', uid: '1:12345' });
await client.location.get({ entityType: 'ships', uid: '5:12345' });Datacards
await client.datacard.list({ factionId: '20:123' });
await client.datacard.get({ uid: 'datacard-uid' });
await client.datacard.create({ uid: 'datacard-uid', production_entity_uid: '6:789', uses: 10 });
await client.datacard.delete({ uid: 'datacard-uid', production_entity_uid: '6:789' });Market
await client.market.vendors.list();
await client.market.vendors.get({ uid: 'vendor-uid' });News
// Galactic News Service (GNS)
await client.news.gns.list();
await client.news.gns.list({ category: 'economy', search: 'battle', author: 'John Doe' });
await client.news.gns.get({ id: 'news-id' });
// Sim News
await client.news.simNews.list();
await client.news.simNews.list({ category: 'player' });
await client.news.simNews.get({ id: 'news-id' });Types
// List all entity types
await client.types.listEntityTypes();
// Get entity classes for a specific type
await client.types.classes.list({ entityType: 'vehicles' });
await client.types.classes.list({ entityType: 'ships' });
// Get entities by type
await client.types.entities.list({ entityType: 'ships' });
await client.types.entities.list({ entityType: 'ships', class: 'fighter' });
await client.types.entities.get({ entityType: 'ships', uid: 'type-uid' });See API Documentation for complete reference.
Rate Limiting
The SW Combine API has a rate limit of 600 requests per hour. The SDK provides tools to monitor and handle rate limits:
// Check current rate limit status after any API call
const rateLimit = client.getRateLimitInfo();
if (rateLimit) {
console.log(`${rateLimit.remaining}/${rateLimit.limit} requests remaining`);
console.log(`Resets at: ${rateLimit.resetTime}`);
}
// Set up a callback to monitor rate limits in real-time
client.onRateLimitUpdate((info) => {
if (info.remaining < 100) {
console.warn(`Warning: Only ${info.remaining} API requests remaining!`);
}
});
// Or check via API endpoint for detailed per-endpoint limits
const limits = await client.api.rateLimits();The SDK automatically handles rate limit errors with exponential backoff and respects the Retry-After header when provided.
Error Handling
import { SWCError } from 'swcombine-sdk';
try {
const character = await client.character.get({ uid: '1:12345' });
} catch (error) {
if (error instanceof SWCError) {
console.error('Status:', error.statusCode); // 404
console.error('Message:', error.message); // "Resource not found"
console.error('Type:', error.type); // "not_found"
console.error('Retryable:', error.retryable); // false
}
}TypeScript Support
Full TypeScript support with intelligent type inference:
// Types are automatically inferred
const character = await client.character.get({ uid: '1:12345' });
// character: Character
// Request parameters are typed
await client.character.messages.list({
uid: '1:12345',
mode: 'received', // Optional - TypeScript knows valid values: 'sent' | 'received'
// mode: 'invalid', // TypeScript error
});Configuration Options
interface ClientConfig {
// Required OAuth credentials
clientId: string;
clientSecret: string;
// Optional authentication - can be access token string or full OAuthToken object
token?: string | OAuthToken;
// Optional OAuth settings
redirectUri?: string;
accessType?: 'online' | 'offline';
// Optional HTTP settings
baseURL?: string; // Default: https://www.swcombine.com/ws/v2.0/
timeout?: number; // Default: 30000 (30 seconds)
maxRetries?: number; // Default: 3
retryDelay?: number; // Default: 1000ms
debug?: boolean; // Default: false
}
interface OAuthToken {
accessToken: string;
refreshToken?: string;
expiresAt: number; // Timestamp in milliseconds
}Examples
See the examples directory for complete working examples:
- OAuth Scopes - 10 examples of scope usage
Basic Usage
import { SWCombine } from 'swcombine-sdk';
const client = new SWCombine({
clientId: process.env.SWC_CLIENT_ID!,
clientSecret: process.env.SWC_CLIENT_SECRET!,
});
// Get character info
const character = await client.character.getByHandle({
handle: 'character-name',
});
console.log(`${character.name} (${character.uid})`);Developer Tools
Get OAuth Token
Interactive OAuth flow to obtain access tokens:
npm run get-tokenGet Character UID
Quickly get a character's UID from their handle:
npm run get-character-uid YourHandleRun Integration Tests
npm run test:integrationDocumentation
- Getting Started Guide - Detailed setup and usage
- Authentication Guide - OAuth 2.0 setup and token management
- OAuth Scopes Reference - Complete scope documentation
- API Reference - Detailed API endpoint documentation
- Examples - Working code examples
Development
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Run integration tests
npm run test:integration
# Lint
npm run lint
# Format code
npm run formatRequirements
- Node.js 18 or higher
- TypeScript 5.0 or higher (for TypeScript projects)
Links
License
MIT © Dreks Selmur aka JonMarkGo
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
Support
- Check the documentation
- Submit issues on GitHub
- Contact support
