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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@seaspark/assets-sdk

v0.1.0

Published

SeaVerse Assets API SDK - Unified multi-tenant asset and credit management service

Readme

@seaspark/assets-sdk

SeaVerse Asset Hub SDK - Unified multi-tenant credit management service (4-pool system)

Overview

Asset Hub SDK provides comprehensive credit management capabilities, supporting a 4-pool credit system (daily/event/monthly/permanent) and traditional credit account queries. All applications (including the SeaVerse platform itself) use the same SDK routing and multi-tenant architecture.

Main API Endpoints:

  • /sdk/v1/credits/detail - Get 4-pool credit details (recommended)
  • /sdk/v1/credits/account - Get credit account information (deprecated)
  • /sdk/v1/credits/transactions - Query transaction history

Core Concepts

Application ID

Each application has a unique app_id:

  • SeaVerse Platform: appId = "seaverse" (official platform apps like ops-frontend, meta-product)
  • Third-party Apps: appId = "game-abc123", appId = "social-xyz789", etc.

Multi-tenant Isolation

  • Credit accounts are isolated by app_id
  • Each application has its own credit pools and transaction history
  • The same user can have different credit accounts across different applications

Installation

npm install @seaspark/assets-sdk
# or
pnpm add @seaspark/assets-sdk

Features

  • 4-Pool Credit System: Supports daily/event/monthly/permanent credit pools
  • Expiration Time Tracking: Each credit pool has independent expiration timestamps
  • Consumption Priority: Daily → Event → Monthly → Permanent
  • Query user credit account information with multi-tenant support
  • List transaction history with filtering and pagination
  • TypeScript support with complete type definitions
  • Read-only API (credit consumption/distribution handled by platform backend)

Quick Start

import { AssetsClient } from '@seaspark/assets-sdk';

// Initialize client for SeaVerse platform
const client = new AssetsClient({
  appId: 'seaverse',
  token: 'your-bearer-token'
});

// Or initialize for third-party application
const gameClient = new AssetsClient({
  appId: 'game-abc123',
  token: 'your-bearer-token'
});

// Get credit details (recommended - 4-pool system)
const detail = await client.getCreditDetail();
console.log(`Total Balance: ${detail.total_balance} credits`);

// Display details for each credit pool
detail.pools.forEach(pool => {
  const expiryLabel = pool.expires_at === 0
    ? 'Never expires'
    : `Expires in ${Math.floor((pool.expires_at - Date.now()) / 86400000)} days`;
  console.log(`${pool.type}: ${pool.balance} (${expiryLabel})`);
});

// List recent transactions
const result = await client.listTransactions({ page_size: 10 });
console.log(`Found ${result.transactions.length} transactions`);
console.log(`Has more: ${result.has_more}`);

4-Pool Credit System

Asset Hub SDK uses a 4-pool credit system, each with different expiration rules and purposes:

Credit Pool Types

| Pool Type | Description | Expiration Rules | |-----------|-------------|------------------| | daily | Daily credits | Expire at 00:00 UTC next day | | event | Event credits | Expire at event deadline | | monthly | Monthly credits | Expire 30 days after last grant | | permanent | General credits | Never expire |

Consumption Priority

When users consume credits, the system deducts in the following priority:

Daily → Event → Monthly → Permanent

This ensures credits about to expire are consumed first, maximizing user credit value.

Usage Example

const detail = await client.getCreditDetail();

// Calculate total balance
console.log(`Total: ${detail.total_balance} credits`);

// Iterate through credit pools
for (const pool of detail.pools) {
  const remaining = pool.expires_at === 0
    ? Infinity
    : pool.expires_at - Date.now();

  if (remaining === Infinity) {
    console.log(`${pool.type}: ${pool.balance} credits (Never expires)`);
  } else {
    const days = Math.floor(remaining / 86400000);
    const hours = Math.floor((remaining % 86400000) / 3600000);
    console.log(`${pool.type}: ${pool.balance} credits (Expires in ${days}d ${hours}h)`);
  }
}

// Check for credits expiring soon
const expiringPools = detail.pools.filter(pool => {
  if (pool.expires_at === 0) return false;
  const daysLeft = (pool.expires_at - Date.now()) / 86400000;
  return daysLeft < 3; // Expiring within 3 days
});

if (expiringPools.length > 0) {
  console.log('⚠️ Some credits are expiring soon, please use them!');
  expiringPools.forEach(pool => {
    console.log(`  - ${pool.type}: ${pool.balance} credits`);
  });
}

API Reference

AssetsClient

Constructor

new AssetsClient(options: AssetsClientOptions)

Parameters:

  • appId (string, required): Application ID for multi-tenant isolation
    • 'seaverse' for SeaVerse platform
    • 'game-abc123' for third-party applications
  • token (string, required): Bearer token for authentication
  • baseURL (string, optional): Base URL for Asset Hub API
    • Default: 'https://seaspark-wallet-service.dev.seaspark.ai'
    • Pass empty string '' to use relative URLs (useful with dev proxy)
  • timeout (number, optional): Request timeout in milliseconds, default 30000

Methods

getCreditDetail()

Get credit details (4-pool system) - Recommended

Returns detailed information for 4 credit pools, each with balance and expiration timestamp.

API Endpoint: GET /sdk/v1/credits/detail

Multi-tenant Behavior:

  • Credit pools belong to the application specified by appId
  • Each application has independent 4-pool system
  • Pool data is isolated by app_id

Request Headers:

  • Authorization: Bearer <token> - Required
  • X-App-ID: <app_id> - Required
async getCreditDetail(): Promise<CreditDetailResponse>

Returns:

{
  total_balance: string           // Total balance across all pools (string for precision)
  pools: [                        // Array of credit pools (only pools with balance > 0)
    {
      type: 'daily' | 'event' | 'monthly' | 'permanent'
      balance: string             // Pool balance (string for precision)
      expires_at: number          // Expiration timestamp in milliseconds
                                  // 0 = never expires
                                  // > 0 = specific expiration time
    }
  ]
}

Credit Pool Types:

  • daily - Daily credits (expire at 00:00 UTC next day)
  • event - Event credits (expire at event deadline)
  • monthly - Monthly credits (expire 30 days after last grant)
  • permanent - General credits (never expire)

Response Features:

  • Only returns credit pools with balance > 0
  • expires_at is in milliseconds timestamp (0 means never expires)
  • Frontend can calculate remaining time based on timestamp

Recommended Refresh Frequency: 30 seconds

Example:

const detail = await client.getCreditDetail();
console.log(`Total Balance: ${detail.total_balance}`);

detail.pools.forEach(pool => {
  const label = pool.expires_at === 0
    ? 'Never expires'
    : `Expires in ${Math.floor((pool.expires_at - Date.now()) / 86400000)} days`;
  console.log(`${pool.type}: ${pool.balance} (${label})`);
});

// Example output:
// Total Balance: 5240
// daily: 150 (Expires in 0 days)
// event: 500 (Expires in 5 days)
// monthly: 800 (Expires in 28 days)
// permanent: 3790 (Never expires)
getCreditAccount()

⚠️ Deprecated: Use getCreditDetail() for the new 4-pool credit system.

Get authenticated user's credit account information for the specified application.

API Endpoint: GET /sdk/v1/credits/account

Multi-tenant Behavior:

  • Credit account belongs to the application specified by appId
  • Each application has isolated credit pools
  • Account data is isolated by app_id

Request Headers:

  • Authorization: Bearer <token> - Required
  • X-App-ID: <app_id> - Required
async getCreditAccount(): Promise<CreditAccount>

Returns:

{
  id: string
  user_id: string
  app_id: string | null           // Application ID (null for platform users)
  balance: number                  // Current available balance
  total_earned: number             // Total credits earned
  total_spent: number              // Total credits spent
  frozen_balance: number           // Frozen balance (unavailable)
  last_transaction_id?: string
  status: 'active' | 'suspended' | 'frozen'
  status_reason?: string           // Only present in non-active states
  created_at: string               // UTC timestamp
  updated_at: string               // UTC timestamp
  last_activity_at?: string        // UTC timestamp
}

Account Status:

  • active - Normal, available
  • suspended - Suspended (contact support)
  • frozen - Frozen (risk control)

Notes:

  • Some fields may be null or undefined, handle null values properly
  • Number fields may be returned as strings, SDK automatically handles type conversion
  • Use optional chaining operator ?. to access optional fields

Example:

const account = await client.getCreditAccount();
console.log(`Balance: ${account.balance ?? 0}`);
console.log(`App: ${account.app_id ?? 'N/A'}`);
console.log(`Status: ${account.status}`);

// Safely access optional fields
if (account.last_activity_at) {
  console.log(`Last Activity: ${account.last_activity_at}`);
}
listTransactions()

List credit transactions for the specified application, with filtering and pagination support.

API Endpoint: GET /sdk/v1/credits/transactions

Multi-tenant Behavior:

  • Transactions are filtered by the application specified by appId
  • Only returns transactions for the authenticated user in this application
  • Transaction history is isolated by app_id

Request Headers:

  • Authorization: Bearer <token> - Required
  • X-App-ID: <app_id> - Required
async listTransactions(request?: ListTransactionsRequest): Promise<TransactionListResponse>

Request Parameters:

  • type (optional): Filter by transaction type
    • 'earn' - Credit earned
    • 'spend' - Credit deducted
    • 'freeze' - Credit frozen
    • 'unfreeze' - Credit unfrozen
    • 'refund' - Refund returned
    • 'adjust' - Admin adjustment
  • source (optional): Filter by transaction source (e.g., 'api_call', 'purchase')
  • status (optional): Filter by status
    • 'pending' - Processing
    • 'completed' - Completed
    • 'failed' - Failed
    • 'cancelled' - Cancelled
  • start_date (optional): Start date filter (RFC3339 format, e.g., '2024-01-01T00:00:00Z')
  • end_date (optional): End date filter (RFC3339 format, e.g., '2024-12-31T23:59:59Z')
  • page (optional): Page number (starts at 1, default: 1)
  • page_size (optional): Items per page (min: 1, max: 50, default: 20)

Returns:

{
  transactions: CreditTransaction[]
  page: number
  page_size: number
  has_more: boolean
}

CreditTransaction:

{
  id: string
  user_id: string
  app_id: string | null              // Application ID (null for platform users)
  type: 'spend' | 'earn' | 'refund' | 'adjust' | 'freeze' | 'unfreeze'
  amount: number                     // Negative for deduction, positive for addition
  balance_before: number
  balance_after: number
  source: string                     // Transaction source identifier
  source_id?: string                 // Associated source ID (order/task/request ID)
  description: string
  status: 'pending' | 'completed' | 'failed' | 'cancelled'
  status_reason?: string             // Only present in non-completed states
  metadata?: Record<string, any>     // Custom metadata
  created_at: string                 // UTC timestamp
  completed_at?: string              // UTC timestamp
}

Example:

// Get recent transactions
const result = await client.listTransactions({ page_size: 20 });

// Filter by type
const spending = await client.listTransactions({
  type: 'spend',
  page: 1,
  page_size: 10
});

// Filter by date range
const recent = await client.listTransactions({
  start_date: '2024-01-01T00:00:00Z',
  end_date: '2024-12-31T23:59:59Z',
  status: 'completed'
});

// Pagination
const page2 = await client.listTransactions({
  page: 2,
  page_size: 20
});
if (page2.has_more) {
  // More results available
}
setToken()

Update the bearer token for authentication.

Use this method when the token expires or needs to be refreshed.

setToken(token: string): void

Example:

client.setToken('new-bearer-token');
setBaseURL()

Update the API base URL.

Use this method to switch between different environments (development/testing/production).

setBaseURL(baseURL: string): void

Example:

client.setBaseURL('https://seaspark-wallet-service.dev.seaspark.ai');
setAppId()

Update the application ID for multi-tenant isolation.

Use this method to switch between different applications at runtime.

setAppId(appId: string): void

Example:

// Switch to SeaVerse platform
client.setAppId('seaverse');

// Switch to third-party application
client.setAppId('game-abc123');

Error Handling

SDK throws AssetsAPIError for API errors:

import { AssetsAPIError } from '@seaspark/assets-sdk';

try {
  const account = await client.getCreditAccount();
} catch (error) {
  if (error instanceof AssetsAPIError) {
    console.error(`API Error [${error.code}]: ${error.message}`);
    console.error('Response:', error.response);
  } else {
    console.error('Unexpected error:', error);
  }
}

Common Error Codes:

  • 400 - Bad Request (invalid parameters)
  • 401 - Unauthorized (invalid or expired token)
  • 403 - Forbidden
  • 500 - Internal Server Error

Environment Configuration

Different environments have different base URLs:

// Development/Testing environment (default)
const client = new AssetsClient({
  appId: 'seaverse',
  token: devToken
  // baseURL defaults to 'https://seaspark-wallet-service.dev.seaspark.ai'
});

// Production environment
const client = new AssetsClient({
  appId: 'seaverse',
  baseURL: 'https://seaspark-wallet-service.prod.seaspark.ai', // TODO: 待确认生产环境地址
  token: prodToken
});

// Local development
const client = new AssetsClient({
  appId: 'seaverse',
  baseURL: 'http://localhost:8080',
  token: localToken
});

Environment Description:

  • Development/Testing: https://seaspark-wallet-service.dev.seaspark.ai (SDK default)
  • Production: TBD (待确认)
  • Local: http://localhost:8080 (local debugging)

Multi-tenant Usage Examples

SeaVerse Platform Application

// Official SeaVerse platform (ops-frontend, meta-product, etc.)
const platformClient = new AssetsClient({
  appId: 'seaverse',
  token: userToken
});

const account = await platformClient.getCreditAccount();
console.log(`Platform Balance: ${account.balance}`);

Third-party Game Application

// Third-party game with its own credit pool
const gameClient = new AssetsClient({
  appId: 'game-abc123',
  token: userToken
});

const gameAccount = await gameClient.getCreditAccount();
console.log(`Game Balance: ${gameAccount.balance}`);

// Same user, different application - isolated credit accounts
// gameAccount.balance may differ from platformClient balance

Switching Between Applications

const client = new AssetsClient({
  appId: 'seaverse',
  token: userToken
});

// Query SeaVerse platform account
const platformAccount = await client.getCreditAccount();

// Switch to game application
client.setAppId('game-abc123');

// Query same user's game account
const gameAccount = await client.getCreditAccount();

// Both accounts belong to same user but are isolated

Credit Units

  • 1 credit = 1 unit
  • All amounts are represented as integers (no decimals)

Authentication

  • Token obtained through SeaVerse auth-service
  • Bearer token required in Authorization header
  • Default token validity: 10 days
  • Required scope: account

Security

Domain Whitelist (Recommended)

For production apps, register allowed domains to prevent unauthorized usage:

  • Origin header validated against application whitelist
  • Prevents app_id theft and abuse

Rate Limiting

  • Rate limiting applied per app_id
  • Prevents abuse

Troubleshooting

Common Issues

1. Cannot read properties of undefined error

If you encounter errors like Cannot read properties of undefined (reading 'toLocaleString'), it means some API fields are undefined or null.

Solution:

// ✅ Recommended: Use nullish coalescing operator
const balance = account.balance ?? 0;
const totalEarned = account.total_earned ?? 0;

// ✅ Recommended: Use optional chaining
const lastActivity = account.last_activity_at ?? 'N/A';

// ✅ Recommended: Check value exists before display
if (account.balance !== null && account.balance !== undefined) {
  console.log(`Balance: ${account.balance.toLocaleString()}`);
}

2. CORS errors

If you encounter CORS errors in the browser, ensure:

  • Your domain is added to the application's whitelist
  • X-App-ID header is set correctly
  • Using correct baseURL

3. 401 Unauthorized error

Possible causes:

  • Token expired (default validity 10 days)
  • Token format incorrect
  • Token missing correct scope (needs account scope)

Solution:

// Re-obtain token
const newToken = await authService.getToken();
client.setToken(newToken);

4. Data type mismatch

API may return numbers as strings, ensure your code can handle this:

// ✅ Safe type conversion
const balance = typeof account.balance === 'string'
  ? parseFloat(account.balance)
  : account.balance;

// ✅ Use SDK-provided type checking
const formatNumber = (value: string | number | null | undefined): number => {
  if (value === null || value === undefined) return 0;
  const num = typeof value === 'string' ? parseFloat(value) : value;
  return isNaN(num) ? 0 : num;
};

License

MIT