klingex 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1061 @@
1
+ // src/types/index.ts
2
+ var KlingExError = class extends Error {
3
+ constructor(message, code, statusCode, details) {
4
+ super(message);
5
+ this.code = code;
6
+ this.statusCode = statusCode;
7
+ this.details = details;
8
+ this.name = "KlingExError";
9
+ }
10
+ };
11
+ var AuthenticationError = class extends KlingExError {
12
+ constructor(message = "Authentication failed") {
13
+ super(message, "AUTH_ERROR", 401);
14
+ this.name = "AuthenticationError";
15
+ }
16
+ };
17
+ var RateLimitError = class extends KlingExError {
18
+ constructor(message = "Rate limit exceeded", retryAfter) {
19
+ super(message, "RATE_LIMIT", 429);
20
+ this.retryAfter = retryAfter;
21
+ this.name = "RateLimitError";
22
+ }
23
+ };
24
+ var ValidationError = class extends KlingExError {
25
+ constructor(message, details) {
26
+ super(message, "VALIDATION_ERROR", 400, details);
27
+ this.name = "ValidationError";
28
+ }
29
+ };
30
+ var InsufficientFundsError = class extends KlingExError {
31
+ constructor(message = "Insufficient funds") {
32
+ super(message, "INSUFFICIENT_FUNDS", 400);
33
+ this.name = "InsufficientFundsError";
34
+ }
35
+ };
36
+
37
+ // src/http.ts
38
+ var HttpClient = class {
39
+ constructor(config) {
40
+ this.config = config;
41
+ }
42
+ /**
43
+ * Update authentication credentials
44
+ */
45
+ setAuth(auth) {
46
+ if (auth.apiKey) this.config.apiKey = auth.apiKey;
47
+ if (auth.jwt) this.config.jwt = auth.jwt;
48
+ }
49
+ /**
50
+ * Make an HTTP request to the API
51
+ */
52
+ async request(endpoint, options = {}) {
53
+ const { method = "GET", body, params, headers = {} } = options;
54
+ let url = `${this.config.baseUrl}${endpoint}`;
55
+ if (params) {
56
+ const searchParams = new URLSearchParams();
57
+ for (const [key, value] of Object.entries(params)) {
58
+ if (value !== void 0 && value !== null) {
59
+ searchParams.append(key, String(value));
60
+ }
61
+ }
62
+ const queryString = searchParams.toString();
63
+ if (queryString) {
64
+ url += `?${queryString}`;
65
+ }
66
+ }
67
+ const requestHeaders = {
68
+ "Content-Type": "application/json",
69
+ ...headers
70
+ };
71
+ if (this.config.apiKey) {
72
+ requestHeaders["X-API-Key"] = this.config.apiKey;
73
+ } else if (this.config.jwt) {
74
+ requestHeaders["Authorization"] = `Bearer ${this.config.jwt}`;
75
+ }
76
+ const controller = new AbortController();
77
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
78
+ try {
79
+ const response = await fetch(url, {
80
+ method,
81
+ headers: requestHeaders,
82
+ body: body ? JSON.stringify(body) : void 0,
83
+ signal: controller.signal
84
+ });
85
+ clearTimeout(timeoutId);
86
+ const contentType = response.headers.get("content-type");
87
+ let data;
88
+ if (contentType?.includes("application/json")) {
89
+ data = await response.json();
90
+ } else if (contentType?.includes("application/pdf")) {
91
+ data = await response.blob();
92
+ } else {
93
+ data = await response.text();
94
+ }
95
+ if (!response.ok) {
96
+ this.handleError(response.status, data);
97
+ }
98
+ return data;
99
+ } catch (error) {
100
+ clearTimeout(timeoutId);
101
+ if (error instanceof KlingExError) {
102
+ throw error;
103
+ }
104
+ if (error instanceof Error) {
105
+ if (error.name === "AbortError") {
106
+ throw new KlingExError("Request timeout", "TIMEOUT", 408);
107
+ }
108
+ throw new KlingExError(error.message, "NETWORK_ERROR");
109
+ }
110
+ throw new KlingExError("Unknown error occurred", "UNKNOWN");
111
+ }
112
+ }
113
+ /**
114
+ * Handle HTTP error responses
115
+ */
116
+ handleError(status, data) {
117
+ const errorMessage = this.extractErrorMessage(data);
118
+ switch (status) {
119
+ case 401:
120
+ throw new AuthenticationError(errorMessage);
121
+ case 429:
122
+ const retryAfter = this.extractRetryAfter(data);
123
+ throw new RateLimitError(errorMessage, retryAfter);
124
+ case 400:
125
+ if (errorMessage.toLowerCase().includes("insufficient")) {
126
+ throw new InsufficientFundsError(errorMessage);
127
+ }
128
+ throw new ValidationError(errorMessage, data);
129
+ default:
130
+ throw new KlingExError(errorMessage, "API_ERROR", status, data);
131
+ }
132
+ }
133
+ /**
134
+ * Extract error message from response
135
+ */
136
+ extractErrorMessage(data) {
137
+ if (typeof data === "string") return data;
138
+ if (typeof data === "object" && data !== null) {
139
+ const obj = data;
140
+ return String(obj.error || obj.message || obj.detail || "Unknown error");
141
+ }
142
+ return "Unknown error";
143
+ }
144
+ /**
145
+ * Extract retry-after value from rate limit response
146
+ */
147
+ extractRetryAfter(data) {
148
+ if (typeof data === "object" && data !== null) {
149
+ const obj = data;
150
+ if (typeof obj.retry_after === "number") {
151
+ return obj.retry_after;
152
+ }
153
+ }
154
+ return void 0;
155
+ }
156
+ // Convenience methods
157
+ async get(endpoint, params) {
158
+ return this.request(endpoint, { method: "GET", params });
159
+ }
160
+ async post(endpoint, body) {
161
+ return this.request(endpoint, { method: "POST", body });
162
+ }
163
+ async put(endpoint, body) {
164
+ return this.request(endpoint, { method: "PUT", body });
165
+ }
166
+ async delete(endpoint, body) {
167
+ return this.request(endpoint, { method: "DELETE", body });
168
+ }
169
+ };
170
+
171
+ // src/websocket.ts
172
+ var KlingExWebSocket = class {
173
+ constructor(url, auth, options = {}) {
174
+ this.ws = null;
175
+ this.subscriptions = /* @__PURE__ */ new Map();
176
+ this.reconnectAttempts = 0;
177
+ this.reconnectTimeout = null;
178
+ this.pingInterval = null;
179
+ this.isConnecting = false;
180
+ this.url = url;
181
+ this.apiKey = auth.apiKey;
182
+ this.jwt = auth.jwt;
183
+ this.options = {
184
+ reconnect: options.reconnect ?? true,
185
+ reconnectInterval: options.reconnectInterval ?? 5e3,
186
+ maxReconnectAttempts: options.maxReconnectAttempts ?? 10
187
+ };
188
+ }
189
+ /**
190
+ * Connect to WebSocket server
191
+ */
192
+ async connect() {
193
+ if (this.ws?.readyState === WebSocket.OPEN || this.isConnecting) {
194
+ return;
195
+ }
196
+ this.isConnecting = true;
197
+ return new Promise((resolve, reject) => {
198
+ try {
199
+ const wsUrl = new URL(this.url);
200
+ if (this.apiKey) {
201
+ wsUrl.searchParams.set("apiKey", this.apiKey);
202
+ } else if (this.jwt) {
203
+ wsUrl.searchParams.set("token", this.jwt);
204
+ }
205
+ this.ws = new WebSocket(wsUrl.toString());
206
+ this.ws.onopen = () => {
207
+ this.isConnecting = false;
208
+ this.reconnectAttempts = 0;
209
+ this.startPingInterval();
210
+ this.resubscribeAll();
211
+ resolve();
212
+ };
213
+ this.ws.onclose = (event) => {
214
+ this.isConnecting = false;
215
+ this.stopPingInterval();
216
+ if (this.options.reconnect && !event.wasClean) {
217
+ this.scheduleReconnect();
218
+ }
219
+ };
220
+ this.ws.onerror = (_event) => {
221
+ this.isConnecting = false;
222
+ const err = new Error("WebSocket error");
223
+ this.errorHandler?.(err);
224
+ reject(err);
225
+ };
226
+ this.ws.onmessage = (event) => {
227
+ this.handleMessage(event.data);
228
+ };
229
+ } catch (error) {
230
+ this.isConnecting = false;
231
+ reject(error);
232
+ }
233
+ });
234
+ }
235
+ /**
236
+ * Disconnect from WebSocket server
237
+ */
238
+ disconnect() {
239
+ this.options.reconnect = false;
240
+ this.stopPingInterval();
241
+ if (this.reconnectTimeout) {
242
+ clearTimeout(this.reconnectTimeout);
243
+ this.reconnectTimeout = null;
244
+ }
245
+ if (this.ws) {
246
+ this.ws.close(1e3, "Client disconnect");
247
+ this.ws = null;
248
+ }
249
+ this.subscriptions.clear();
250
+ }
251
+ /**
252
+ * Set error handler
253
+ */
254
+ onError(handler) {
255
+ this.errorHandler = handler;
256
+ }
257
+ /**
258
+ * Subscribe to orderbook updates
259
+ * @param symbol - Trading pair symbol
260
+ * @param handler - Callback for orderbook updates
261
+ */
262
+ orderbook(symbol, handler) {
263
+ return this.subscribe("orderbook", symbol, handler);
264
+ }
265
+ /**
266
+ * Subscribe to trade updates
267
+ * @param symbol - Trading pair symbol
268
+ * @param handler - Callback for trade updates
269
+ */
270
+ trades(symbol, handler) {
271
+ return this.subscribe("trades", symbol, handler);
272
+ }
273
+ /**
274
+ * Subscribe to ticker updates
275
+ * @param symbol - Trading pair symbol
276
+ * @param handler - Callback for ticker updates
277
+ */
278
+ ticker(symbol, handler) {
279
+ return this.subscribe("ticker", symbol, handler);
280
+ }
281
+ /**
282
+ * Subscribe to user order updates (requires auth)
283
+ * @param handler - Callback for order updates
284
+ */
285
+ userOrders(handler) {
286
+ return this.subscribe("user.orders", void 0, handler);
287
+ }
288
+ /**
289
+ * Subscribe to user balance updates (requires auth)
290
+ * @param handler - Callback for balance updates
291
+ */
292
+ userBalances(handler) {
293
+ return this.subscribe("user.balances", void 0, handler);
294
+ }
295
+ // =========================================================================
296
+ // Private methods
297
+ // =========================================================================
298
+ subscribe(channel, symbol, handler) {
299
+ const key = symbol ? `${channel}:${symbol}` : channel;
300
+ this.subscriptions.set(key, {
301
+ channel,
302
+ symbol,
303
+ handler
304
+ });
305
+ if (this.ws?.readyState === WebSocket.OPEN) {
306
+ this.sendSubscribe(channel, symbol);
307
+ }
308
+ return () => {
309
+ this.subscriptions.delete(key);
310
+ if (this.ws?.readyState === WebSocket.OPEN) {
311
+ this.sendUnsubscribe(channel, symbol);
312
+ }
313
+ };
314
+ }
315
+ sendSubscribe(channel, symbol) {
316
+ this.send({
317
+ action: "subscribe",
318
+ channel,
319
+ symbol
320
+ });
321
+ }
322
+ sendUnsubscribe(channel, symbol) {
323
+ this.send({
324
+ action: "unsubscribe",
325
+ channel,
326
+ symbol
327
+ });
328
+ }
329
+ send(data) {
330
+ if (this.ws?.readyState === WebSocket.OPEN) {
331
+ this.ws.send(JSON.stringify(data));
332
+ }
333
+ }
334
+ handleMessage(data) {
335
+ try {
336
+ const message = JSON.parse(data);
337
+ if (message.type === "pong") {
338
+ return;
339
+ }
340
+ const key = message.data && typeof message.data === "object" && "symbol" in message.data ? `${message.channel}:${message.data.symbol}` : message.channel;
341
+ const subscription = this.subscriptions.get(key) || this.subscriptions.get(message.channel);
342
+ if (subscription) {
343
+ subscription.handler(message.data);
344
+ }
345
+ } catch (error) {
346
+ this.errorHandler?.(error instanceof Error ? error : new Error("Failed to parse message"));
347
+ }
348
+ }
349
+ resubscribeAll() {
350
+ for (const [, subscription] of this.subscriptions) {
351
+ this.sendSubscribe(subscription.channel, subscription.symbol);
352
+ }
353
+ }
354
+ scheduleReconnect() {
355
+ if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
356
+ this.errorHandler?.(new Error("Max reconnection attempts reached"));
357
+ return;
358
+ }
359
+ this.reconnectAttempts++;
360
+ const delay = this.options.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1);
361
+ this.reconnectTimeout = setTimeout(() => {
362
+ this.connect().catch((error) => {
363
+ this.errorHandler?.(error instanceof Error ? error : new Error("Reconnection failed"));
364
+ });
365
+ }, delay);
366
+ }
367
+ startPingInterval() {
368
+ this.pingInterval = setInterval(() => {
369
+ this.send({ type: "ping" });
370
+ }, 3e4);
371
+ }
372
+ stopPingInterval() {
373
+ if (this.pingInterval) {
374
+ clearInterval(this.pingInterval);
375
+ this.pingInterval = null;
376
+ }
377
+ }
378
+ };
379
+
380
+ // src/endpoints/markets.ts
381
+ var MarketsEndpoint = class {
382
+ constructor(http) {
383
+ this.http = http;
384
+ }
385
+ /**
386
+ * Get all available markets/trading pairs
387
+ * @example
388
+ * const markets = await client.markets.list();
389
+ * // Returns full market info including:
390
+ * // - base_asset_symbol, quote_asset_symbol
391
+ * // - maker_fee_rate, taker_fee_rate
392
+ * // - last_price, volume_24h, priceChange24h
393
+ */
394
+ async list() {
395
+ return this.http.get("/api/markets");
396
+ }
397
+ /**
398
+ * Get a specific market by ID
399
+ * @param marketId - The market/trading pair ID
400
+ */
401
+ async get(marketId) {
402
+ const markets = await this.list();
403
+ return markets.find((m) => m.id === marketId);
404
+ }
405
+ /**
406
+ * Find a market by symbol pair
407
+ * @param baseSymbol - Base asset symbol (e.g., "BTC")
408
+ * @param quoteSymbol - Quote asset symbol (e.g., "USDT")
409
+ */
410
+ async findBySymbols(baseSymbol, quoteSymbol) {
411
+ const markets = await this.list();
412
+ return markets.find(
413
+ (m) => m.base_asset_symbol.toUpperCase() === baseSymbol.toUpperCase() && m.quote_asset_symbol.toUpperCase() === quoteSymbol.toUpperCase()
414
+ );
415
+ }
416
+ /**
417
+ * Get all tickers with 24h price data (CMC format)
418
+ * @example
419
+ * const tickers = await client.markets.tickers();
420
+ * // Returns ticker_id as "BTC_USDT" format
421
+ */
422
+ async tickers() {
423
+ return this.http.get("/api/tickers");
424
+ }
425
+ /**
426
+ * Get ticker for a specific symbol pair
427
+ * @param tickerId - Ticker ID in CMC format (e.g., "BTC_USDT")
428
+ */
429
+ async ticker(tickerId) {
430
+ const tickers = await this.tickers();
431
+ return tickers.find((t) => t.ticker_id === tickerId);
432
+ }
433
+ /**
434
+ * Get ticker by base and quote currency
435
+ * @param baseCurrency - Base currency symbol (e.g., "BTC")
436
+ * @param targetCurrency - Target/quote currency symbol (e.g., "USDT")
437
+ */
438
+ async tickerByPair(baseCurrency, targetCurrency) {
439
+ const tickers = await this.tickers();
440
+ return tickers.find(
441
+ (t) => t.base_currency.toUpperCase() === baseCurrency.toUpperCase() && t.target_currency.toUpperCase() === targetCurrency.toUpperCase()
442
+ );
443
+ }
444
+ /**
445
+ * Get orderbook for a trading pair
446
+ * @param marketId - The market/trading pair ID
447
+ * @param options - Options for the orderbook request
448
+ * @example
449
+ * const orderbook = await client.markets.orderbook(1);
450
+ * console.log('Best bid:', orderbook.bids[0]);
451
+ * console.log('Best ask:', orderbook.asks[0]);
452
+ */
453
+ async orderbook(marketId, options = {}) {
454
+ const response = await this.http.get("/api/orderbook", {
455
+ marketId,
456
+ isCmc: options.isCmc ?? false
457
+ });
458
+ return {
459
+ trading_pair_id: response.trading_pair_id,
460
+ base_symbol: response.base_symbol,
461
+ quote_symbol: response.quote_symbol,
462
+ bids: (response.bids || []).map(([price, quantity]) => ({ price, quantity })),
463
+ asks: (response.asks || []).map(([price, quantity]) => ({ price, quantity }))
464
+ };
465
+ }
466
+ /**
467
+ * Get raw orderbook (original API format with arrays)
468
+ * @param marketId - The market/trading pair ID
469
+ */
470
+ async orderbookRaw(marketId, options = {}) {
471
+ return this.http.get("/api/orderbook", {
472
+ marketId,
473
+ isCmc: options.isCmc ?? false
474
+ });
475
+ }
476
+ /**
477
+ * Get OHLCV (candlestick) data
478
+ * @param marketId - Trading pair ID
479
+ * @param timeframe - Candle timeframe (e.g., "1h", "1d")
480
+ * @param options - Additional options
481
+ * @example
482
+ * const candles = await client.markets.ohlcv(1, '1h');
483
+ */
484
+ async ohlcv(marketId, timeframe, options = {}) {
485
+ return this.http.get("/api/ohlcv", {
486
+ marketId,
487
+ timeframe,
488
+ limit: options.limit,
489
+ startDate: options.startDate,
490
+ endDate: options.endDate
491
+ });
492
+ }
493
+ /**
494
+ * Get all available assets
495
+ * @example
496
+ * const assets = await client.markets.assets();
497
+ */
498
+ async assets() {
499
+ const response = await this.http.get("/api/assets");
500
+ return response.assets || [];
501
+ }
502
+ /**
503
+ * Get a specific asset by symbol
504
+ * @param symbol - Asset symbol (e.g., "BTC")
505
+ */
506
+ async asset(symbol) {
507
+ const assets = await this.assets();
508
+ return assets.find((a) => a.symbol.toUpperCase() === symbol.toUpperCase());
509
+ }
510
+ /**
511
+ * Get recent trades for a trading pair
512
+ * @param marketId - Trading pair ID
513
+ * @param limit - Number of trades to return
514
+ */
515
+ async trades(marketId, limit = 50) {
516
+ return this.http.get("/api/trades", { marketId, limit });
517
+ }
518
+ };
519
+
520
+ // src/endpoints/orders.ts
521
+ var OrdersEndpoint = class {
522
+ constructor(http, humanReadableDefault = true) {
523
+ this.http = http;
524
+ this.humanReadableDefault = humanReadableDefault;
525
+ }
526
+ /**
527
+ * Submit a new order
528
+ * @param params - Order parameters
529
+ * @example
530
+ * // Buy 1.5 BTC at $50,000 (human-readable values)
531
+ * const order = await client.orders.submit({
532
+ * symbol: 'BTC-USDT',
533
+ * tradingPairId: 1,
534
+ * side: 'BUY',
535
+ * quantity: '1.5',
536
+ * price: '50000.00'
537
+ * });
538
+ *
539
+ * @example
540
+ * // Market order (price = 0)
541
+ * const order = await client.orders.submit({
542
+ * symbol: 'BTC-USDT',
543
+ * tradingPairId: 1,
544
+ * side: 'BUY',
545
+ * quantity: '1.0',
546
+ * price: '0',
547
+ * slippage: 0.01 // 1% slippage tolerance
548
+ * });
549
+ */
550
+ async submit(params) {
551
+ const rawValues = params.rawValues ?? !this.humanReadableDefault;
552
+ return this.http.post("/api/submit-order", {
553
+ symbol: params.symbol,
554
+ tradingPairId: params.tradingPairId,
555
+ side: params.side.toUpperCase(),
556
+ quantity: params.quantity,
557
+ price: params.price,
558
+ rawValues,
559
+ slippage: params.slippage
560
+ });
561
+ }
562
+ /**
563
+ * Cancel an existing order
564
+ * @param params - Order ID and trading pair ID
565
+ * @example
566
+ * const result = await client.orders.cancel({
567
+ * orderId: '7c9e6679-7425-40de-944b-e07fc1f90ae7',
568
+ * tradingPairId: 1
569
+ * });
570
+ * console.log(`Released balance: ${result.released_balance}`);
571
+ */
572
+ async cancel(params) {
573
+ return this.http.post("/api/cancel-order", {
574
+ orderId: params.orderId,
575
+ tradingPairId: params.tradingPairId
576
+ });
577
+ }
578
+ /**
579
+ * Cancel all open orders for a trading pair
580
+ * @param tradingPairId - Trading pair ID
581
+ * @example
582
+ * const result = await client.orders.cancelAll(1);
583
+ * console.log(`Cancelled ${result.cancelledCount} orders`);
584
+ */
585
+ async cancelAll(tradingPairId) {
586
+ return this.http.post("/api/cancel-all-orders", { tradingPairId });
587
+ }
588
+ /**
589
+ * Get your open orders
590
+ * @param params - Filter parameters
591
+ * @example
592
+ * // Get all open orders
593
+ * const orders = await client.orders.list();
594
+ *
595
+ * // Get orders for specific trading pair
596
+ * const orders = await client.orders.list({ tradingPairId: 1 });
597
+ */
598
+ async list(params = {}) {
599
+ const response = await this.http.get("/api/user-orders", {
600
+ tradingPairId: params.tradingPairId,
601
+ status: params.status,
602
+ limit: params.limit || 50
603
+ });
604
+ return response.orders || [];
605
+ }
606
+ /**
607
+ * Get order history (including filled/cancelled orders)
608
+ * @param params - Filter and pagination parameters
609
+ */
610
+ async history(params = {}) {
611
+ return this.http.get("/api/orders-history", {
612
+ tradingPairId: params.tradingPairId,
613
+ status: params.status,
614
+ limit: params.limit || 50,
615
+ offset: params.offset || 0
616
+ });
617
+ }
618
+ /**
619
+ * Get a specific order by ID
620
+ * @param orderId - Order UUID
621
+ */
622
+ async get(orderId) {
623
+ const orders = await this.list();
624
+ return orders.find((o) => o.id === orderId);
625
+ }
626
+ // =========================================================================
627
+ // Convenience methods for common order types
628
+ // =========================================================================
629
+ /**
630
+ * Place a limit buy order (human-readable values)
631
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
632
+ * @param tradingPairId - Trading pair ID
633
+ * @param quantity - Amount to buy
634
+ * @param price - Price per unit
635
+ */
636
+ async limitBuy(symbol, tradingPairId, quantity, price) {
637
+ return this.submit({
638
+ symbol,
639
+ tradingPairId,
640
+ side: "BUY",
641
+ quantity,
642
+ price
643
+ });
644
+ }
645
+ /**
646
+ * Place a limit sell order (human-readable values)
647
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
648
+ * @param tradingPairId - Trading pair ID
649
+ * @param quantity - Amount to sell
650
+ * @param price - Price per unit
651
+ */
652
+ async limitSell(symbol, tradingPairId, quantity, price) {
653
+ return this.submit({
654
+ symbol,
655
+ tradingPairId,
656
+ side: "SELL",
657
+ quantity,
658
+ price
659
+ });
660
+ }
661
+ /**
662
+ * Place a market buy order
663
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
664
+ * @param tradingPairId - Trading pair ID
665
+ * @param quantity - Amount to buy
666
+ * @param slippage - Slippage tolerance (0-1, e.g., 0.01 for 1%)
667
+ */
668
+ async marketBuy(symbol, tradingPairId, quantity, slippage = 0.01) {
669
+ return this.submit({
670
+ symbol,
671
+ tradingPairId,
672
+ side: "BUY",
673
+ quantity,
674
+ price: "0",
675
+ slippage
676
+ });
677
+ }
678
+ /**
679
+ * Place a market sell order
680
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
681
+ * @param tradingPairId - Trading pair ID
682
+ * @param quantity - Amount to sell
683
+ * @param slippage - Slippage tolerance (0-1, e.g., 0.01 for 1%)
684
+ */
685
+ async marketSell(symbol, tradingPairId, quantity, slippage = 0.01) {
686
+ return this.submit({
687
+ symbol,
688
+ tradingPairId,
689
+ side: "SELL",
690
+ quantity,
691
+ price: "0",
692
+ slippage
693
+ });
694
+ }
695
+ };
696
+
697
+ // src/endpoints/wallet.ts
698
+ var WalletEndpoint = class {
699
+ constructor(http) {
700
+ this.http = http;
701
+ }
702
+ /**
703
+ * Get all wallet balances
704
+ * Each balance includes:
705
+ * - balance: Total balance (raw value)
706
+ * - locked_balance: Locked in orders (raw value)
707
+ * - wallet_id: UUID of the wallet
708
+ * - deposit_address: Deposit address for this asset
709
+ * - id, symbol, name, decimals: Asset info
710
+ * - min_deposit, min_withdrawal, withdrawal_fee: Limits
711
+ *
712
+ * @example
713
+ * const balances = await client.wallet.balances();
714
+ * const ethBalance = balances.find(b => b.symbol === 'ETH');
715
+ * console.log(`ETH Balance: ${ethBalance?.balance}`);
716
+ * console.log(`Deposit Address: ${ethBalance?.deposit_address}`);
717
+ */
718
+ async balances() {
719
+ const balances = await this.http.get("/api/user-balances");
720
+ return balances.map((b) => {
721
+ const balance = BigInt(b.balance);
722
+ const locked = BigInt(b.locked_balance);
723
+ const available = balance - locked;
724
+ const decimals = b.decimals;
725
+ return {
726
+ ...b,
727
+ available_balance: available.toString(),
728
+ human_balance: this.formatUnits(b.balance, decimals),
729
+ human_locked: this.formatUnits(b.locked_balance, decimals),
730
+ human_available: this.formatUnits(available.toString(), decimals)
731
+ };
732
+ });
733
+ }
734
+ /**
735
+ * Get balance for a specific asset
736
+ * @param symbol - Asset symbol (e.g., "BTC", "ETH", "USDT")
737
+ */
738
+ async balance(symbol) {
739
+ const balances = await this.balances();
740
+ return balances.find((b) => b.symbol.toUpperCase() === symbol.toUpperCase());
741
+ }
742
+ /**
743
+ * Get deposit address for an asset (from balance data)
744
+ * The deposit_address is included in the balance response
745
+ * @param symbol - Asset symbol (e.g., "BTC")
746
+ * @example
747
+ * const address = await client.wallet.depositAddress('ETH');
748
+ * console.log(`Send ETH to: ${address.address}`);
749
+ */
750
+ async depositAddress(symbol) {
751
+ const balances = await this.http.get("/api/user-balances");
752
+ const balance = balances.find((b) => b.symbol.toUpperCase() === symbol.toUpperCase());
753
+ if (!balance) {
754
+ return void 0;
755
+ }
756
+ return {
757
+ address: balance.deposit_address
758
+ };
759
+ }
760
+ /**
761
+ * Generate a new deposit address (if supported)
762
+ * @param assetId - Asset ID
763
+ */
764
+ async generateDepositAddress(assetId) {
765
+ const response = await this.http.post("/api/generate-deposit-address", {
766
+ assetId
767
+ });
768
+ return {
769
+ address: response.address,
770
+ memo: response.memo
771
+ };
772
+ }
773
+ /**
774
+ * Submit a withdrawal request
775
+ * @param params - Withdrawal parameters
776
+ * @example
777
+ * const result = await client.wallet.withdraw({
778
+ * assetId: 2,
779
+ * symbol: 'ETH',
780
+ * address: '0x...',
781
+ * amount: '0.1'
782
+ * });
783
+ *
784
+ * if (result.requires_2fa) {
785
+ * // Need to complete 2FA verification
786
+ * await client.wallet.confirm2FA(result.session_token, '123456');
787
+ * }
788
+ */
789
+ async withdraw(params) {
790
+ return this.http.post("/api/submit-withdraw", {
791
+ assetId: params.assetId,
792
+ symbol: params.symbol,
793
+ address: params.address,
794
+ amount: params.amount,
795
+ destinationTag: params.memo
796
+ });
797
+ }
798
+ /**
799
+ * Complete withdrawal 2FA verification
800
+ * @param sessionToken - Session token from withdraw response
801
+ * @param code - 2FA code from authenticator app
802
+ */
803
+ async confirm2FA(sessionToken, code) {
804
+ return this.http.post("/api/withdrawal/complete-2fa", {
805
+ sessionToken,
806
+ code
807
+ });
808
+ }
809
+ /**
810
+ * Get deposit history
811
+ * @param options - Pagination options
812
+ */
813
+ async deposits(options = {}) {
814
+ return this.http.get("/api/deposits", {
815
+ limit: options.limit || 50,
816
+ offset: options.offset || 0
817
+ });
818
+ }
819
+ /**
820
+ * Get withdrawal history
821
+ * @param options - Pagination options
822
+ */
823
+ async withdrawals(options = {}) {
824
+ return this.http.get("/api/withdrawals", {
825
+ limit: options.limit || 50,
826
+ offset: options.offset || 0
827
+ });
828
+ }
829
+ /**
830
+ * Get transaction history (deposits + withdrawals + trades)
831
+ */
832
+ async history() {
833
+ return this.http.get("/api/history");
834
+ }
835
+ /**
836
+ * Format raw units to human-readable decimal string
837
+ * @param value - Raw value as string
838
+ * @param decimals - Number of decimals
839
+ */
840
+ formatUnits(value, decimals) {
841
+ if (decimals === 0) return value;
842
+ const str = value.padStart(decimals + 1, "0");
843
+ const intPart = str.slice(0, -decimals) || "0";
844
+ const decPart = str.slice(-decimals);
845
+ const trimmedDec = decPart.replace(/0+$/, "");
846
+ return trimmedDec ? `${intPart}.${trimmedDec}` : intPart;
847
+ }
848
+ };
849
+
850
+ // src/endpoints/invoices.ts
851
+ var InvoicesEndpoint = class {
852
+ constructor(http) {
853
+ this.http = http;
854
+ }
855
+ /**
856
+ * Create a new payment invoice
857
+ * @param params - Invoice parameters
858
+ * @example
859
+ * const invoice = await client.invoices.create({
860
+ * amount: '100.00',
861
+ * asset: 'USDT',
862
+ * description: 'Order #12345',
863
+ * external_id: 'order-12345',
864
+ * webhook_url: 'https://yoursite.com/webhook'
865
+ * });
866
+ *
867
+ * console.log(`Payment address: ${invoice.payment_address}`);
868
+ * console.log(`Expires: ${invoice.expires_at}`);
869
+ */
870
+ async create(params) {
871
+ const response = await this.http.post("/api/invoices", {
872
+ amount: params.amount,
873
+ asset: params.asset,
874
+ description: params.description,
875
+ external_id: params.external_id,
876
+ webhook_url: params.webhook_url,
877
+ redirect_url: params.redirect_url,
878
+ expires_in: params.expires_in
879
+ });
880
+ if (!response.data) {
881
+ throw new Error("Failed to create invoice");
882
+ }
883
+ return response.data;
884
+ }
885
+ /**
886
+ * Get all invoices
887
+ * @param options - Pagination and filter options
888
+ */
889
+ async list(options = {}) {
890
+ return this.http.get("/api/invoices", {
891
+ limit: options.limit || 50,
892
+ offset: options.offset || 0,
893
+ status: options.status
894
+ });
895
+ }
896
+ /**
897
+ * Get a specific invoice by ID
898
+ * @param invoiceId - Invoice UUID
899
+ */
900
+ async get(invoiceId) {
901
+ const response = await this.http.get(`/api/invoices/${invoiceId}`);
902
+ if (!response.data) {
903
+ throw new Error("Invoice not found");
904
+ }
905
+ return response.data;
906
+ }
907
+ /**
908
+ * Get invoice status (for polling)
909
+ * @param invoiceId - Invoice UUID
910
+ */
911
+ async status(invoiceId) {
912
+ return this.http.get(`/api/invoices/${invoiceId}/status`);
913
+ }
914
+ /**
915
+ * Cancel a pending invoice
916
+ * @param invoiceId - Invoice UUID
917
+ */
918
+ async cancel(invoiceId) {
919
+ return this.http.post(`/api/invoices/${invoiceId}/cancel`);
920
+ }
921
+ /**
922
+ * Get invoice PDF
923
+ * @param invoiceId - Invoice UUID
924
+ * @returns PDF blob
925
+ */
926
+ async pdf(invoiceId) {
927
+ return this.http.get(`/api/invoices/${invoiceId}/pdf`);
928
+ }
929
+ /**
930
+ * Get estimated fees for an invoice
931
+ * @param asset - Asset symbol
932
+ * @param amount - Invoice amount
933
+ */
934
+ async fees(asset, amount) {
935
+ const response = await this.http.get("/api/invoices/fees", {
936
+ asset,
937
+ amount
938
+ });
939
+ if (!response.data) {
940
+ return {
941
+ network_fee: "0",
942
+ service_fee: "0",
943
+ total_fee: "0"
944
+ };
945
+ }
946
+ return response.data;
947
+ }
948
+ /**
949
+ * Get public invoice payment page data (no auth required)
950
+ * @param invoiceId - Invoice UUID
951
+ */
952
+ async paymentPage(invoiceId) {
953
+ return this.http.get(`/api/invoices/${invoiceId}/pay`);
954
+ }
955
+ };
956
+
957
+ // src/client.ts
958
+ var DEFAULT_BASE_URL = "https://api.klingex.io";
959
+ var DEFAULT_WS_URL = "wss://api.klingex.io/ws";
960
+ var DEFAULT_TIMEOUT = 3e4;
961
+ var KlingEx = class {
962
+ constructor(config = {}) {
963
+ this._ws = null;
964
+ this.config = {
965
+ apiKey: config.apiKey,
966
+ jwt: config.jwt,
967
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL,
968
+ wsUrl: config.wsUrl || DEFAULT_WS_URL,
969
+ timeout: config.timeout || DEFAULT_TIMEOUT,
970
+ humanReadable: config.humanReadable ?? true
971
+ };
972
+ this.http = new HttpClient({
973
+ baseUrl: this.config.baseUrl,
974
+ apiKey: this.config.apiKey,
975
+ jwt: this.config.jwt,
976
+ timeout: this.config.timeout
977
+ });
978
+ this.markets = new MarketsEndpoint(this.http);
979
+ this.orders = new OrdersEndpoint(this.http, this.config.humanReadable);
980
+ this.wallet = new WalletEndpoint(this.http);
981
+ this.invoices = new InvoicesEndpoint(this.http);
982
+ }
983
+ /**
984
+ * WebSocket client for real-time data
985
+ * Note: Call ws.connect() before subscribing to channels
986
+ */
987
+ get ws() {
988
+ if (!this._ws) {
989
+ this._ws = new KlingExWebSocket(
990
+ this.config.wsUrl,
991
+ { apiKey: this.config.apiKey, jwt: this.config.jwt }
992
+ );
993
+ }
994
+ return this._ws;
995
+ }
996
+ /**
997
+ * Create WebSocket connection with custom options
998
+ */
999
+ createWebSocket(options) {
1000
+ return new KlingExWebSocket(
1001
+ this.config.wsUrl,
1002
+ { apiKey: this.config.apiKey, jwt: this.config.jwt },
1003
+ options
1004
+ );
1005
+ }
1006
+ /**
1007
+ * Update authentication credentials
1008
+ */
1009
+ setAuth(auth) {
1010
+ if (auth.apiKey) this.config.apiKey = auth.apiKey;
1011
+ if (auth.jwt) this.config.jwt = auth.jwt;
1012
+ this.http.setAuth(auth);
1013
+ }
1014
+ /**
1015
+ * Check if client has authentication configured
1016
+ */
1017
+ get isAuthenticated() {
1018
+ return !!(this.config.apiKey || this.config.jwt);
1019
+ }
1020
+ /**
1021
+ * Get the configured base URL
1022
+ */
1023
+ get baseUrl() {
1024
+ return this.config.baseUrl;
1025
+ }
1026
+ // =========================================================================
1027
+ // Account endpoints (directly on client for convenience)
1028
+ // =========================================================================
1029
+ /**
1030
+ * Get current user profile
1031
+ */
1032
+ async getProfile() {
1033
+ return this.http.get("/api/profile");
1034
+ }
1035
+ /**
1036
+ * Get current user info
1037
+ */
1038
+ async getUser() {
1039
+ return this.http.get("/api/user");
1040
+ }
1041
+ /**
1042
+ * Get API key statistics
1043
+ */
1044
+ async getApiKeyStats() {
1045
+ return this.http.get("/api/api-keys/stats");
1046
+ }
1047
+ };
1048
+ export {
1049
+ AuthenticationError,
1050
+ InsufficientFundsError,
1051
+ InvoicesEndpoint,
1052
+ KlingEx,
1053
+ KlingExError,
1054
+ KlingExWebSocket,
1055
+ MarketsEndpoint,
1056
+ OrdersEndpoint,
1057
+ RateLimitError,
1058
+ ValidationError,
1059
+ WalletEndpoint,
1060
+ KlingEx as default
1061
+ };