You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1061 lines
30 KiB

// src/types/index.ts
var KlingExError = class extends Error {
constructor(message, code, statusCode, details) {
super(message);
this.code = code;
this.statusCode = statusCode;
this.details = details;
this.name = "KlingExError";
}
};
var AuthenticationError = class extends KlingExError {
constructor(message = "Authentication failed") {
super(message, "AUTH_ERROR", 401);
this.name = "AuthenticationError";
}
};
var RateLimitError = class extends KlingExError {
constructor(message = "Rate limit exceeded", retryAfter) {
super(message, "RATE_LIMIT", 429);
this.retryAfter = retryAfter;
this.name = "RateLimitError";
}
};
var ValidationError = class extends KlingExError {
constructor(message, details) {
super(message, "VALIDATION_ERROR", 400, details);
this.name = "ValidationError";
}
};
var InsufficientFundsError = class extends KlingExError {
constructor(message = "Insufficient funds") {
super(message, "INSUFFICIENT_FUNDS", 400);
this.name = "InsufficientFundsError";
}
};
// src/http.ts
var HttpClient = class {
constructor(config) {
this.config = config;
}
/**
* Update authentication credentials
*/
setAuth(auth) {
if (auth.apiKey) this.config.apiKey = auth.apiKey;
if (auth.jwt) this.config.jwt = auth.jwt;
}
/**
* Make an HTTP request to the API
*/
async request(endpoint, options = {}) {
const { method = "GET", body, params, headers = {} } = options;
let url = `${this.config.baseUrl}${endpoint}`;
if (params) {
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value !== void 0 && value !== null) {
searchParams.append(key, String(value));
}
}
const queryString = searchParams.toString();
if (queryString) {
url += `?${queryString}`;
}
}
const requestHeaders = {
"Content-Type": "application/json",
...headers
};
if (this.config.apiKey) {
requestHeaders["X-API-Key"] = this.config.apiKey;
} else if (this.config.jwt) {
requestHeaders["Authorization"] = `Bearer ${this.config.jwt}`;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
try {
const response = await fetch(url, {
method,
headers: requestHeaders,
body: body ? JSON.stringify(body) : void 0,
signal: controller.signal
});
clearTimeout(timeoutId);
const contentType = response.headers.get("content-type");
let data;
if (contentType?.includes("application/json")) {
data = await response.json();
} else if (contentType?.includes("application/pdf")) {
data = await response.blob();
} else {
data = await response.text();
}
if (!response.ok) {
this.handleError(response.status, data);
}
return data;
} catch (error) {
clearTimeout(timeoutId);
if (error instanceof KlingExError) {
throw error;
}
if (error instanceof Error) {
if (error.name === "AbortError") {
throw new KlingExError("Request timeout", "TIMEOUT", 408);
}
throw new KlingExError(error.message, "NETWORK_ERROR");
}
throw new KlingExError("Unknown error occurred", "UNKNOWN");
}
}
/**
* Handle HTTP error responses
*/
handleError(status, data) {
const errorMessage = this.extractErrorMessage(data);
switch (status) {
case 401:
throw new AuthenticationError(errorMessage);
case 429:
const retryAfter = this.extractRetryAfter(data);
throw new RateLimitError(errorMessage, retryAfter);
case 400:
if (errorMessage.toLowerCase().includes("insufficient")) {
throw new InsufficientFundsError(errorMessage);
}
throw new ValidationError(errorMessage, data);
default:
throw new KlingExError(errorMessage, "API_ERROR", status, data);
}
}
/**
* Extract error message from response
*/
extractErrorMessage(data) {
if (typeof data === "string") return data;
if (typeof data === "object" && data !== null) {
const obj = data;
return String(obj.error || obj.message || obj.detail || "Unknown error");
}
return "Unknown error";
}
/**
* Extract retry-after value from rate limit response
*/
extractRetryAfter(data) {
if (typeof data === "object" && data !== null) {
const obj = data;
if (typeof obj.retry_after === "number") {
return obj.retry_after;
}
}
return void 0;
}
// Convenience methods
async get(endpoint, params) {
return this.request(endpoint, { method: "GET", params });
}
async post(endpoint, body) {
return this.request(endpoint, { method: "POST", body });
}
async put(endpoint, body) {
return this.request(endpoint, { method: "PUT", body });
}
async delete(endpoint, body) {
return this.request(endpoint, { method: "DELETE", body });
}
};
// src/websocket.ts
var KlingExWebSocket = class {
constructor(url, auth, options = {}) {
this.ws = null;
this.subscriptions = /* @__PURE__ */ new Map();
this.reconnectAttempts = 0;
this.reconnectTimeout = null;
this.pingInterval = null;
this.isConnecting = false;
this.url = url;
this.apiKey = auth.apiKey;
this.jwt = auth.jwt;
this.options = {
reconnect: options.reconnect ?? true,
reconnectInterval: options.reconnectInterval ?? 5e3,
maxReconnectAttempts: options.maxReconnectAttempts ?? 10
};
}
/**
* Connect to WebSocket server
*/
async connect() {
if (this.ws?.readyState === WebSocket.OPEN || this.isConnecting) {
return;
}
this.isConnecting = true;
return new Promise((resolve, reject) => {
try {
const wsUrl = new URL(this.url);
if (this.apiKey) {
wsUrl.searchParams.set("apiKey", this.apiKey);
} else if (this.jwt) {
wsUrl.searchParams.set("token", this.jwt);
}
this.ws = new WebSocket(wsUrl.toString());
this.ws.onopen = () => {
this.isConnecting = false;
this.reconnectAttempts = 0;
this.startPingInterval();
this.resubscribeAll();
resolve();
};
this.ws.onclose = (event) => {
this.isConnecting = false;
this.stopPingInterval();
if (this.options.reconnect && !event.wasClean) {
this.scheduleReconnect();
}
};
this.ws.onerror = (_event) => {
this.isConnecting = false;
const err = new Error("WebSocket error");
this.errorHandler?.(err);
reject(err);
};
this.ws.onmessage = (event) => {
this.handleMessage(event.data);
};
} catch (error) {
this.isConnecting = false;
reject(error);
}
});
}
/**
* Disconnect from WebSocket server
*/
disconnect() {
this.options.reconnect = false;
this.stopPingInterval();
if (this.reconnectTimeout) {
clearTimeout(this.reconnectTimeout);
this.reconnectTimeout = null;
}
if (this.ws) {
this.ws.close(1e3, "Client disconnect");
this.ws = null;
}
this.subscriptions.clear();
}
/**
* Set error handler
*/
onError(handler) {
this.errorHandler = handler;
}
/**
* Subscribe to orderbook updates
* @param symbol - Trading pair symbol
* @param handler - Callback for orderbook updates
*/
orderbook(symbol, handler) {
return this.subscribe("orderbook", symbol, handler);
}
/**
* Subscribe to trade updates
* @param symbol - Trading pair symbol
* @param handler - Callback for trade updates
*/
trades(symbol, handler) {
return this.subscribe("trades", symbol, handler);
}
/**
* Subscribe to ticker updates
* @param symbol - Trading pair symbol
* @param handler - Callback for ticker updates
*/
ticker(symbol, handler) {
return this.subscribe("ticker", symbol, handler);
}
/**
* Subscribe to user order updates (requires auth)
* @param handler - Callback for order updates
*/
userOrders(handler) {
return this.subscribe("user.orders", void 0, handler);
}
/**
* Subscribe to user balance updates (requires auth)
* @param handler - Callback for balance updates
*/
userBalances(handler) {
return this.subscribe("user.balances", void 0, handler);
}
// =========================================================================
// Private methods
// =========================================================================
subscribe(channel, symbol, handler) {
const key = symbol ? `${channel}:${symbol}` : channel;
this.subscriptions.set(key, {
channel,
symbol,
handler
});
if (this.ws?.readyState === WebSocket.OPEN) {
this.sendSubscribe(channel, symbol);
}
return () => {
this.subscriptions.delete(key);
if (this.ws?.readyState === WebSocket.OPEN) {
this.sendUnsubscribe(channel, symbol);
}
};
}
sendSubscribe(channel, symbol) {
this.send({
action: "subscribe",
channel,
symbol
});
}
sendUnsubscribe(channel, symbol) {
this.send({
action: "unsubscribe",
channel,
symbol
});
}
send(data) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
handleMessage(data) {
try {
const message = JSON.parse(data);
if (message.type === "pong") {
return;
}
const key = message.data && typeof message.data === "object" && "symbol" in message.data ? `${message.channel}:${message.data.symbol}` : message.channel;
const subscription = this.subscriptions.get(key) || this.subscriptions.get(message.channel);
if (subscription) {
subscription.handler(message.data);
}
} catch (error) {
this.errorHandler?.(error instanceof Error ? error : new Error("Failed to parse message"));
}
}
resubscribeAll() {
for (const [, subscription] of this.subscriptions) {
this.sendSubscribe(subscription.channel, subscription.symbol);
}
}
scheduleReconnect() {
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
this.errorHandler?.(new Error("Max reconnection attempts reached"));
return;
}
this.reconnectAttempts++;
const delay = this.options.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1);
this.reconnectTimeout = setTimeout(() => {
this.connect().catch((error) => {
this.errorHandler?.(error instanceof Error ? error : new Error("Reconnection failed"));
});
}, delay);
}
startPingInterval() {
this.pingInterval = setInterval(() => {
this.send({ type: "ping" });
}, 3e4);
}
stopPingInterval() {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
}
};
// src/endpoints/markets.ts
var MarketsEndpoint = class {
constructor(http) {
this.http = http;
}
/**
* Get all available markets/trading pairs
* @example
* const markets = await client.markets.list();
* // Returns full market info including:
* // - base_asset_symbol, quote_asset_symbol
* // - maker_fee_rate, taker_fee_rate
* // - last_price, volume_24h, priceChange24h
*/
async list() {
return this.http.get("/api/markets");
}
/**
* Get a specific market by ID
* @param marketId - The market/trading pair ID
*/
async get(marketId) {
const markets = await this.list();
return markets.find((m) => m.id === marketId);
}
/**
* Find a market by symbol pair
* @param baseSymbol - Base asset symbol (e.g., "BTC")
* @param quoteSymbol - Quote asset symbol (e.g., "USDT")
*/
async findBySymbols(baseSymbol, quoteSymbol) {
const markets = await this.list();
return markets.find(
(m) => m.base_asset_symbol.toUpperCase() === baseSymbol.toUpperCase() && m.quote_asset_symbol.toUpperCase() === quoteSymbol.toUpperCase()
);
}
/**
* Get all tickers with 24h price data (CMC format)
* @example
* const tickers = await client.markets.tickers();
* // Returns ticker_id as "BTC_USDT" format
*/
async tickers() {
return this.http.get("/api/tickers");
}
/**
* Get ticker for a specific symbol pair
* @param tickerId - Ticker ID in CMC format (e.g., "BTC_USDT")
*/
async ticker(tickerId) {
const tickers = await this.tickers();
return tickers.find((t) => t.ticker_id === tickerId);
}
/**
* Get ticker by base and quote currency
* @param baseCurrency - Base currency symbol (e.g., "BTC")
* @param targetCurrency - Target/quote currency symbol (e.g., "USDT")
*/
async tickerByPair(baseCurrency, targetCurrency) {
const tickers = await this.tickers();
return tickers.find(
(t) => t.base_currency.toUpperCase() === baseCurrency.toUpperCase() && t.target_currency.toUpperCase() === targetCurrency.toUpperCase()
);
}
/**
* Get orderbook for a trading pair
* @param marketId - The market/trading pair ID
* @param options - Options for the orderbook request
* @example
* const orderbook = await client.markets.orderbook(1);
* console.log('Best bid:', orderbook.bids[0]);
* console.log('Best ask:', orderbook.asks[0]);
*/
async orderbook(marketId, options = {}) {
const response = await this.http.get("/api/orderbook", {
marketId,
isCmc: options.isCmc ?? false
});
return {
trading_pair_id: response.trading_pair_id,
base_symbol: response.base_symbol,
quote_symbol: response.quote_symbol,
bids: (response.bids || []).map(([price, quantity]) => ({ price, quantity })),
asks: (response.asks || []).map(([price, quantity]) => ({ price, quantity }))
};
}
/**
* Get raw orderbook (original API format with arrays)
* @param marketId - The market/trading pair ID
*/
async orderbookRaw(marketId, options = {}) {
return this.http.get("/api/orderbook", {
marketId,
isCmc: options.isCmc ?? false
});
}
/**
* Get OHLCV (candlestick) data
* @param marketId - Trading pair ID
* @param timeframe - Candle timeframe (e.g., "1h", "1d")
* @param options - Additional options
* @example
* const candles = await client.markets.ohlcv(1, '1h');
*/
async ohlcv(marketId, timeframe, options = {}) {
return this.http.get("/api/ohlcv", {
marketId,
timeframe,
limit: options.limit,
startDate: options.startDate,
endDate: options.endDate
});
}
/**
* Get all available assets
* @example
* const assets = await client.markets.assets();
*/
async assets() {
const response = await this.http.get("/api/assets");
return response.assets || [];
}
/**
* Get a specific asset by symbol
* @param symbol - Asset symbol (e.g., "BTC")
*/
async asset(symbol) {
const assets = await this.assets();
return assets.find((a) => a.symbol.toUpperCase() === symbol.toUpperCase());
}
/**
* Get recent trades for a trading pair
* @param marketId - Trading pair ID
* @param limit - Number of trades to return
*/
async trades(marketId, limit = 50) {
return this.http.get("/api/trades", { marketId, limit });
}
};
// src/endpoints/orders.ts
var OrdersEndpoint = class {
constructor(http, humanReadableDefault = true) {
this.http = http;
this.humanReadableDefault = humanReadableDefault;
}
/**
* Submit a new order
* @param params - Order parameters
* @example
* // Buy 1.5 BTC at $50,000 (human-readable values)
* const order = await client.orders.submit({
* symbol: 'BTC-USDT',
* tradingPairId: 1,
* side: 'BUY',
* quantity: '1.5',
* price: '50000.00'
* });
*
* @example
* // Market order (price = 0)
* const order = await client.orders.submit({
* symbol: 'BTC-USDT',
* tradingPairId: 1,
* side: 'BUY',
* quantity: '1.0',
* price: '0',
* slippage: 0.01 // 1% slippage tolerance
* });
*/
async submit(params) {
const rawValues = params.rawValues ?? !this.humanReadableDefault;
return this.http.post("/api/submit-order", {
symbol: params.symbol,
tradingPairId: params.tradingPairId,
side: params.side.toUpperCase(),
quantity: params.quantity,
price: params.price,
rawValues,
slippage: params.slippage
});
}
/**
* Cancel an existing order
* @param params - Order ID and trading pair ID
* @example
* const result = await client.orders.cancel({
* orderId: '7c9e6679-7425-40de-944b-e07fc1f90ae7',
* tradingPairId: 1
* });
* console.log(`Released balance: ${result.released_balance}`);
*/
async cancel(params) {
return this.http.post("/api/cancel-order", {
orderId: params.orderId,
tradingPairId: params.tradingPairId
});
}
/**
* Cancel all open orders for a trading pair
* @param tradingPairId - Trading pair ID
* @example
* const result = await client.orders.cancelAll(1);
* console.log(`Cancelled ${result.cancelledCount} orders`);
*/
async cancelAll(tradingPairId) {
return this.http.post("/api/cancel-all-orders", { tradingPairId });
}
/**
* Get your open orders
* @param params - Filter parameters
* @example
* // Get all open orders
* const orders = await client.orders.list();
*
* // Get orders for specific trading pair
* const orders = await client.orders.list({ tradingPairId: 1 });
*/
async list(params = {}) {
const response = await this.http.get("/api/user-orders", {
tradingPairId: params.tradingPairId,
status: params.status,
limit: params.limit || 50
});
return response.orders || [];
}
/**
* Get order history (including filled/cancelled orders)
* @param params - Filter and pagination parameters
*/
async history(params = {}) {
return this.http.get("/api/orders-history", {
tradingPairId: params.tradingPairId,
status: params.status,
limit: params.limit || 50,
offset: params.offset || 0
});
}
/**
* Get a specific order by ID
* @param orderId - Order UUID
*/
async get(orderId) {
const orders = await this.list();
return orders.find((o) => o.id === orderId);
}
// =========================================================================
// Convenience methods for common order types
// =========================================================================
/**
* Place a limit buy order (human-readable values)
* @param symbol - Trading pair symbol (e.g., "BTC-USDT")
* @param tradingPairId - Trading pair ID
* @param quantity - Amount to buy
* @param price - Price per unit
*/
async limitBuy(symbol, tradingPairId, quantity, price) {
return this.submit({
symbol,
tradingPairId,
side: "BUY",
quantity,
price
});
}
/**
* Place a limit sell order (human-readable values)
* @param symbol - Trading pair symbol (e.g., "BTC-USDT")
* @param tradingPairId - Trading pair ID
* @param quantity - Amount to sell
* @param price - Price per unit
*/
async limitSell(symbol, tradingPairId, quantity, price) {
return this.submit({
symbol,
tradingPairId,
side: "SELL",
quantity,
price
});
}
/**
* Place a market buy order
* @param symbol - Trading pair symbol (e.g., "BTC-USDT")
* @param tradingPairId - Trading pair ID
* @param quantity - Amount to buy
* @param slippage - Slippage tolerance (0-1, e.g., 0.01 for 1%)
*/
async marketBuy(symbol, tradingPairId, quantity, slippage = 0.01) {
return this.submit({
symbol,
tradingPairId,
side: "BUY",
quantity,
price: "0",
slippage
});
}
/**
* Place a market sell order
* @param symbol - Trading pair symbol (e.g., "BTC-USDT")
* @param tradingPairId - Trading pair ID
* @param quantity - Amount to sell
* @param slippage - Slippage tolerance (0-1, e.g., 0.01 for 1%)
*/
async marketSell(symbol, tradingPairId, quantity, slippage = 0.01) {
return this.submit({
symbol,
tradingPairId,
side: "SELL",
quantity,
price: "0",
slippage
});
}
};
// src/endpoints/wallet.ts
var WalletEndpoint = class {
constructor(http) {
this.http = http;
}
/**
* Get all wallet balances
* Each balance includes:
* - balance: Total balance (raw value)
* - locked_balance: Locked in orders (raw value)
* - wallet_id: UUID of the wallet
* - deposit_address: Deposit address for this asset
* - id, symbol, name, decimals: Asset info
* - min_deposit, min_withdrawal, withdrawal_fee: Limits
*
* @example
* const balances = await client.wallet.balances();
* const ethBalance = balances.find(b => b.symbol === 'ETH');
* console.log(`ETH Balance: ${ethBalance?.balance}`);
* console.log(`Deposit Address: ${ethBalance?.deposit_address}`);
*/
async balances() {
const balances = await this.http.get("/api/user-balances");
return balances.map((b) => {
const balance = BigInt(b.balance);
const locked = BigInt(b.locked_balance);
const available = balance - locked;
const decimals = b.decimals;
return {
...b,
available_balance: available.toString(),
human_balance: this.formatUnits(b.balance, decimals),
human_locked: this.formatUnits(b.locked_balance, decimals),
human_available: this.formatUnits(available.toString(), decimals)
};
});
}
/**
* Get balance for a specific asset
* @param symbol - Asset symbol (e.g., "BTC", "ETH", "USDT")
*/
async balance(symbol) {
const balances = await this.balances();
return balances.find((b) => b.symbol.toUpperCase() === symbol.toUpperCase());
}
/**
* Get deposit address for an asset (from balance data)
* The deposit_address is included in the balance response
* @param symbol - Asset symbol (e.g., "BTC")
* @example
* const address = await client.wallet.depositAddress('ETH');
* console.log(`Send ETH to: ${address.address}`);
*/
async depositAddress(symbol) {
const balances = await this.http.get("/api/user-balances");
const balance = balances.find((b) => b.symbol.toUpperCase() === symbol.toUpperCase());
if (!balance) {
return void 0;
}
return {
address: balance.deposit_address
};
}
/**
* Generate a new deposit address (if supported)
* @param assetId - Asset ID
*/
async generateDepositAddress(assetId) {
const response = await this.http.post("/api/generate-deposit-address", {
assetId
});
return {
address: response.address,
memo: response.memo
};
}
/**
* Submit a withdrawal request
* @param params - Withdrawal parameters
* @example
* const result = await client.wallet.withdraw({
* assetId: 2,
* symbol: 'ETH',
* address: '0x...',
* amount: '0.1'
* });
*
* if (result.requires_2fa) {
* // Need to complete 2FA verification
* await client.wallet.confirm2FA(result.session_token, '123456');
* }
*/
async withdraw(params) {
return this.http.post("/api/submit-withdraw", {
assetId: params.assetId,
symbol: params.symbol,
address: params.address,
amount: params.amount,
destinationTag: params.memo
});
}
/**
* Complete withdrawal 2FA verification
* @param sessionToken - Session token from withdraw response
* @param code - 2FA code from authenticator app
*/
async confirm2FA(sessionToken, code) {
return this.http.post("/api/withdrawal/complete-2fa", {
sessionToken,
code
});
}
/**
* Get deposit history
* @param options - Pagination options
*/
async deposits(options = {}) {
return this.http.get("/api/deposits", {
limit: options.limit || 50,
offset: options.offset || 0
});
}
/**
* Get withdrawal history
* @param options - Pagination options
*/
async withdrawals(options = {}) {
return this.http.get("/api/withdrawals", {
limit: options.limit || 50,
offset: options.offset || 0
});
}
/**
* Get transaction history (deposits + withdrawals + trades)
*/
async history() {
return this.http.get("/api/history");
}
/**
* Format raw units to human-readable decimal string
* @param value - Raw value as string
* @param decimals - Number of decimals
*/
formatUnits(value, decimals) {
if (decimals === 0) return value;
const str = value.padStart(decimals + 1, "0");
const intPart = str.slice(0, -decimals) || "0";
const decPart = str.slice(-decimals);
const trimmedDec = decPart.replace(/0+$/, "");
return trimmedDec ? `${intPart}.${trimmedDec}` : intPart;
}
};
// src/endpoints/invoices.ts
var InvoicesEndpoint = class {
constructor(http) {
this.http = http;
}
/**
* Create a new payment invoice
* @param params - Invoice parameters
* @example
* const invoice = await client.invoices.create({
* amount: '100.00',
* asset: 'USDT',
* description: 'Order #12345',
* external_id: 'order-12345',
* webhook_url: 'https://yoursite.com/webhook'
* });
*
* console.log(`Payment address: ${invoice.payment_address}`);
* console.log(`Expires: ${invoice.expires_at}`);
*/
async create(params) {
const response = await this.http.post("/api/invoices", {
amount: params.amount,
asset: params.asset,
description: params.description,
external_id: params.external_id,
webhook_url: params.webhook_url,
redirect_url: params.redirect_url,
expires_in: params.expires_in
});
if (!response.data) {
throw new Error("Failed to create invoice");
}
return response.data;
}
/**
* Get all invoices
* @param options - Pagination and filter options
*/
async list(options = {}) {
return this.http.get("/api/invoices", {
limit: options.limit || 50,
offset: options.offset || 0,
status: options.status
});
}
/**
* Get a specific invoice by ID
* @param invoiceId - Invoice UUID
*/
async get(invoiceId) {
const response = await this.http.get(`/api/invoices/${invoiceId}`);
if (!response.data) {
throw new Error("Invoice not found");
}
return response.data;
}
/**
* Get invoice status (for polling)
* @param invoiceId - Invoice UUID
*/
async status(invoiceId) {
return this.http.get(`/api/invoices/${invoiceId}/status`);
}
/**
* Cancel a pending invoice
* @param invoiceId - Invoice UUID
*/
async cancel(invoiceId) {
return this.http.post(`/api/invoices/${invoiceId}/cancel`);
}
/**
* Get invoice PDF
* @param invoiceId - Invoice UUID
* @returns PDF blob
*/
async pdf(invoiceId) {
return this.http.get(`/api/invoices/${invoiceId}/pdf`);
}
/**
* Get estimated fees for an invoice
* @param asset - Asset symbol
* @param amount - Invoice amount
*/
async fees(asset, amount) {
const response = await this.http.get("/api/invoices/fees", {
asset,
amount
});
if (!response.data) {
return {
network_fee: "0",
service_fee: "0",
total_fee: "0"
};
}
return response.data;
}
/**
* Get public invoice payment page data (no auth required)
* @param invoiceId - Invoice UUID
*/
async paymentPage(invoiceId) {
return this.http.get(`/api/invoices/${invoiceId}/pay`);
}
};
// src/client.ts
var DEFAULT_BASE_URL = "https://api.klingex.io";
var DEFAULT_WS_URL = "wss://api.klingex.io/ws";
var DEFAULT_TIMEOUT = 3e4;
var KlingEx = class {
constructor(config = {}) {
this._ws = null;
this.config = {
apiKey: config.apiKey,
jwt: config.jwt,
baseUrl: config.baseUrl || DEFAULT_BASE_URL,
wsUrl: config.wsUrl || DEFAULT_WS_URL,
timeout: config.timeout || DEFAULT_TIMEOUT,
humanReadable: config.humanReadable ?? true
};
this.http = new HttpClient({
baseUrl: this.config.baseUrl,
apiKey: this.config.apiKey,
jwt: this.config.jwt,
timeout: this.config.timeout
});
this.markets = new MarketsEndpoint(this.http);
this.orders = new OrdersEndpoint(this.http, this.config.humanReadable);
this.wallet = new WalletEndpoint(this.http);
this.invoices = new InvoicesEndpoint(this.http);
}
/**
* WebSocket client for real-time data
* Note: Call ws.connect() before subscribing to channels
*/
get ws() {
if (!this._ws) {
this._ws = new KlingExWebSocket(
this.config.wsUrl,
{ apiKey: this.config.apiKey, jwt: this.config.jwt }
);
}
return this._ws;
}
/**
* Create WebSocket connection with custom options
*/
createWebSocket(options) {
return new KlingExWebSocket(
this.config.wsUrl,
{ apiKey: this.config.apiKey, jwt: this.config.jwt },
options
);
}
/**
* Update authentication credentials
*/
setAuth(auth) {
if (auth.apiKey) this.config.apiKey = auth.apiKey;
if (auth.jwt) this.config.jwt = auth.jwt;
this.http.setAuth(auth);
}
/**
* Check if client has authentication configured
*/
get isAuthenticated() {
return !!(this.config.apiKey || this.config.jwt);
}
/**
* Get the configured base URL
*/
get baseUrl() {
return this.config.baseUrl;
}
// =========================================================================
// Account endpoints (directly on client for convenience)
// =========================================================================
/**
* Get current user profile
*/
async getProfile() {
return this.http.get("/api/profile");
}
/**
* Get current user info
*/
async getUser() {
return this.http.get("/api/user");
}
/**
* Get API key statistics
*/
async getApiKeyStats() {
return this.http.get("/api/api-keys/stats");
}
};
export {
AuthenticationError,
InsufficientFundsError,
InvoicesEndpoint,
KlingEx,
KlingExError,
KlingExWebSocket,
MarketsEndpoint,
OrdersEndpoint,
RateLimitError,
ValidationError,
WalletEndpoint,
KlingEx as default
};