@cexyio/cexy 0.1.0-dev.0 → 0.1.0-dev.3

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/README.md CHANGED
@@ -5,16 +5,17 @@ The official TypeScript/JavaScript SDK for the [CEXY.io](https://cexy.io) REST a
5
5
  - Typed models generated from the public OpenAPI spec ([cexy-api-spec](https://github.com/cexyio/cexy-api-spec)).
6
6
  - Safe by default: retries with backoff, order placement that never duplicates (via `client_order_id`), a client-side rate limiter.
7
7
  - A WebSocket client with heartbeat, reconnect and a live order book that applies the sync rules for you.
8
- - ESM and CommonJS, Node 20+, zero runtime dependencies.
8
+ - ESM and CommonJS, Node 22+, zero runtime dependencies.
9
9
 
10
10
  > **Status: 0.x.** The API is not yet frozen. It stays 0.x until the exchange ships HMAC request signing,
11
11
  > which will change how credentials are sent.
12
+ > Pre-release: `npm install @cexyio/cexy@next`; `latest` currently points at a pre-release until 1.0.
12
13
 
13
14
  ## Install
14
15
 
15
16
  ```bash
16
- npm install @cexyio/cexy
17
- # Node only, optional: lets the WebSocket client send a User-Agent (recommended before Node 22)
17
+ npm install @cexyio/cexy@next
18
+ # Node only, optional: lets the WebSocket client send a User-Agent
18
19
  npm install ws
19
20
  ```
20
21
 
@@ -102,6 +103,7 @@ Every API failure throws a `CexyApiError` (or a subclass) with `status`, `code`,
102
103
  |---|---|
103
104
  | `AuthenticationError` | 401: missing or invalid credentials |
104
105
  | `ForbiddenError` | 403: the key lacks a scope (`FORBIDDEN`), or the route is session-only (`API_KEY_NOT_ALLOWED`) |
106
+ | `JurisdictionBlockedError` | 451: `JURISDICTION_BLOCKED` (not available in the caller's jurisdiction); a `ForbiddenError` subclass |
105
107
  | `ValidationError` | 400: see `fields` |
106
108
  | `NotFoundError` | 404 |
107
109
  | `ConflictError` | 409: `ALREADY_EXISTS`, `IDEMPOTENCY_KEY_CONFLICT`, `CONCURRENT_MODIFICATION` |
@@ -213,7 +215,7 @@ use public channels and poll REST for private state. If the session is revoked,
213
215
  `authLost`; public channels keep working.
214
216
 
215
217
  In Node the client uses the optional `ws` package when installed (so it can send the SDK User-Agent),
216
- otherwise the global `WebSocket` (Node 22+, browsers).
218
+ otherwise the global `WebSocket` (Node, browsers).
217
219
 
218
220
  ## Browsers
219
221
 
@@ -227,6 +229,9 @@ endpoints from a server. Browsers do not let scripts set `User-Agent`, so the SD
227
229
  - Credentials go only in the `X-API-Key` / `X-API-Secret` headers and only on private endpoints; never in URLs.
228
230
  - Only `https://` base URLs and `wss://` WebSocket URLs are accepted. `allowInsecure: true` permits
229
231
  `http://` / `ws://` solely for `localhost`, `127.0.0.1` or `::1` (local test servers).
232
+ - The SDK **never follows HTTP redirects**. A 3xx answer throws a `CexyApiError` with code
233
+ `UNEXPECTED_REDIRECT` (not retried), so credentials are never re-sent to another host and an order
234
+ is never re-posted to a redirect target. If you pass your own `fetch`, it must honour `redirect: "manual"`.
230
235
  - The SDK redacts the secret from `toString()`, `util.inspect`, `JSON.stringify` and error messages.
231
236
  - Keep keys in environment variables or a secret manager, not in code.
232
237
 
package/dist/index.cjs CHANGED
@@ -56,6 +56,8 @@ var AuthenticationError = class extends CexyApiError {
56
56
  };
57
57
  var ForbiddenError = class extends CexyApiError {
58
58
  };
59
+ var JurisdictionBlockedError = class extends ForbiddenError {
60
+ };
59
61
  var NotFoundError = class extends CexyApiError {
60
62
  };
61
63
  var ValidationError = class extends CexyApiError {
@@ -78,8 +80,8 @@ var STATUS_CLASSES = {
78
80
  401: AuthenticationError,
79
81
  403: ForbiddenError,
80
82
  404: NotFoundError,
81
- 451: ForbiddenError,
82
- // unavailable for legal reasons (JURISDICTION_BLOCKED)
83
+ 451: JurisdictionBlockedError,
84
+ // unavailable for legal reasons
83
85
  409: ConflictError,
84
86
  422: UnprocessableError
85
87
  };
@@ -172,6 +174,7 @@ function errorFromResponse(status, body, headers, redact = (t) => t) {
172
174
  if (status === 429 || init.code === "RATE_LIMITED") {
173
175
  return new RateLimitError(init, retryAfterMs(headers, init.details));
174
176
  }
177
+ if (init.code === "JURISDICTION_BLOCKED") return new JurisdictionBlockedError(init);
175
178
  if (env && !isKnownErrorCode(env.code)) return new CexyApiError(init);
176
179
  if (status >= 500) return new ServerError(init);
177
180
  const Cls = STATUS_CLASSES[status] ?? CexyApiError;
@@ -391,7 +394,13 @@ var Transport = class {
391
394
  let res;
392
395
  let text;
393
396
  try {
394
- res = await this.config.fetch(url.toString(), { method: info.method, headers, body, signal: ctrl.signal });
397
+ res = await this.config.fetch(url.toString(), {
398
+ method: info.method,
399
+ headers,
400
+ body,
401
+ signal: ctrl.signal,
402
+ redirect: "manual"
403
+ });
395
404
  text = await res.text();
396
405
  } catch (err) {
397
406
  if (opts.signal?.aborted) throw opts.signal.reason ?? err;
@@ -403,6 +412,14 @@ var Transport = class {
403
412
  clearTimeout(timer);
404
413
  opts.signal?.removeEventListener("abort", onAbort);
405
414
  }
415
+ if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400 || res.redirected) {
416
+ throw new CexyApiError({
417
+ status: res.status,
418
+ code: "UNEXPECTED_REDIRECT",
419
+ message: `${info.method} ${info.path}: the server answered with a redirect (HTTP ${res.status}); the SDK does not follow redirects. Check baseUrl.`,
420
+ retryable: false
421
+ });
422
+ }
406
423
  this.config.limiter?.update(res.headers);
407
424
  if (!res.ok) {
408
425
  const parsed = safeJson(text);
@@ -901,7 +918,7 @@ var TradingResource = class extends Resource {
901
918
  };
902
919
 
903
920
  // src/version.ts
904
- var VERSION = "0.1.0-dev.0";
921
+ var VERSION = "0.1.0-dev.3";
905
922
  var USER_AGENT = `cexy-typescript/${VERSION}`;
906
923
 
907
924
  // src/ws/emitter.ts
@@ -1661,7 +1678,7 @@ var CexyClient = class {
1661
1678
  assertSecureUrl(parsed, "https:", "http:", options.allowInsecure === true, "baseUrl");
1662
1679
  this.#allowInsecure = options.allowInsecure === true;
1663
1680
  const fetchImpl = options.fetch ?? globalThis.fetch;
1664
- if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 20+ required); pass options.fetch");
1681
+ if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 22+ required); pass options.fetch");
1665
1682
  const timeoutMs = options.timeoutMs ?? 1e4;
1666
1683
  const maxRetries = options.maxRetries ?? 3;
1667
1684
  if (!(timeoutMs > 0)) throw new CexyConfigError("timeoutMs must be > 0");
@@ -1772,6 +1789,7 @@ exports.ExportsResource = ExportsResource;
1772
1789
  exports.FeesResource = FeesResource;
1773
1790
  exports.ForbiddenError = ForbiddenError;
1774
1791
  exports.InvalidAmountError = InvalidAmountError;
1792
+ exports.JurisdictionBlockedError = JurisdictionBlockedError;
1775
1793
  exports.KNOWN_EVENT_TYPES = KNOWN_EVENT_TYPES;
1776
1794
  exports.LiveOrderBook = LiveOrderBook;
1777
1795
  exports.MarketsResource = MarketsResource;
package/dist/index.d.cts CHANGED
@@ -3458,6 +3458,12 @@ declare class AuthenticationError extends CexyApiError {
3458
3458
  /** 403: the key lacks a scope (`FORBIDDEN`), or the route is session-only (`API_KEY_NOT_ALLOWED`). */
3459
3459
  declare class ForbiddenError extends CexyApiError {
3460
3460
  }
3461
+ /**
3462
+ * 451: not available in the caller's jurisdiction (`JURISDICTION_BLOCKED`). A `ForbiddenError`
3463
+ * subclass, so existing `instanceof ForbiddenError` checks still match. Retrying will not help.
3464
+ */
3465
+ declare class JurisdictionBlockedError extends ForbiddenError {
3466
+ }
3461
3467
  /** 404 */
3462
3468
  declare class NotFoundError extends CexyApiError {
3463
3469
  }
@@ -3838,7 +3844,7 @@ interface CexyClientOptions {
3838
3844
  timeoutMs?: number;
3839
3845
  /** Retries after the first attempt for retryable failures. Default 3. */
3840
3846
  maxRetries?: number;
3841
- /** A `fetch` implementation. Default: the global `fetch` (Node 20+, browsers). */
3847
+ /** A `fetch` implementation. Default: the global `fetch` (Node 22+, browsers). */
3842
3848
  fetch?: FetchLike;
3843
3849
  /**
3844
3850
  * Client-side rate limit in requests per minute, or `false` to disable. Default 100/min
@@ -3919,11 +3925,11 @@ declare function isAmount(value: unknown): value is Amount;
3919
3925
  declare function assertAmountFields(body: Record<string, unknown>, fields: readonly string[], context: string): void;
3920
3926
 
3921
3927
  /** SDK version, kept in sync with package.json (a test enforces this). */
3922
- declare const VERSION = "0.1.0-dev.0";
3928
+ declare const VERSION = "0.1.0-dev.3";
3923
3929
  /** Default User-Agent product token. */
3924
- declare const USER_AGENT = "cexy-typescript/0.1.0-dev.0";
3930
+ declare const USER_AGENT = "cexy-typescript/0.1.0-dev.3";
3925
3931
 
3926
3932
  /** True for loopback hosts, the only ones where plain-text transport may be allowed. */
3927
3933
  declare function isLocalHost(hostname: string): boolean;
3928
3934
 
3929
- export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
3935
+ export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, JurisdictionBlockedError, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
package/dist/index.d.ts CHANGED
@@ -3458,6 +3458,12 @@ declare class AuthenticationError extends CexyApiError {
3458
3458
  /** 403: the key lacks a scope (`FORBIDDEN`), or the route is session-only (`API_KEY_NOT_ALLOWED`). */
3459
3459
  declare class ForbiddenError extends CexyApiError {
3460
3460
  }
3461
+ /**
3462
+ * 451: not available in the caller's jurisdiction (`JURISDICTION_BLOCKED`). A `ForbiddenError`
3463
+ * subclass, so existing `instanceof ForbiddenError` checks still match. Retrying will not help.
3464
+ */
3465
+ declare class JurisdictionBlockedError extends ForbiddenError {
3466
+ }
3461
3467
  /** 404 */
3462
3468
  declare class NotFoundError extends CexyApiError {
3463
3469
  }
@@ -3838,7 +3844,7 @@ interface CexyClientOptions {
3838
3844
  timeoutMs?: number;
3839
3845
  /** Retries after the first attempt for retryable failures. Default 3. */
3840
3846
  maxRetries?: number;
3841
- /** A `fetch` implementation. Default: the global `fetch` (Node 20+, browsers). */
3847
+ /** A `fetch` implementation. Default: the global `fetch` (Node 22+, browsers). */
3842
3848
  fetch?: FetchLike;
3843
3849
  /**
3844
3850
  * Client-side rate limit in requests per minute, or `false` to disable. Default 100/min
@@ -3919,11 +3925,11 @@ declare function isAmount(value: unknown): value is Amount;
3919
3925
  declare function assertAmountFields(body: Record<string, unknown>, fields: readonly string[], context: string): void;
3920
3926
 
3921
3927
  /** SDK version, kept in sync with package.json (a test enforces this). */
3922
- declare const VERSION = "0.1.0-dev.0";
3928
+ declare const VERSION = "0.1.0-dev.3";
3923
3929
  /** Default User-Agent product token. */
3924
- declare const USER_AGENT = "cexy-typescript/0.1.0-dev.0";
3930
+ declare const USER_AGENT = "cexy-typescript/0.1.0-dev.3";
3925
3931
 
3926
3932
  /** True for loopback hosts, the only ones where plain-text transport may be allowed. */
3927
3933
  declare function isLocalHost(hostname: string): boolean;
3928
3934
 
3929
- export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
3935
+ export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, JurisdictionBlockedError, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
package/dist/index.js CHANGED
@@ -54,6 +54,8 @@ var AuthenticationError = class extends CexyApiError {
54
54
  };
55
55
  var ForbiddenError = class extends CexyApiError {
56
56
  };
57
+ var JurisdictionBlockedError = class extends ForbiddenError {
58
+ };
57
59
  var NotFoundError = class extends CexyApiError {
58
60
  };
59
61
  var ValidationError = class extends CexyApiError {
@@ -76,8 +78,8 @@ var STATUS_CLASSES = {
76
78
  401: AuthenticationError,
77
79
  403: ForbiddenError,
78
80
  404: NotFoundError,
79
- 451: ForbiddenError,
80
- // unavailable for legal reasons (JURISDICTION_BLOCKED)
81
+ 451: JurisdictionBlockedError,
82
+ // unavailable for legal reasons
81
83
  409: ConflictError,
82
84
  422: UnprocessableError
83
85
  };
@@ -170,6 +172,7 @@ function errorFromResponse(status, body, headers, redact = (t) => t) {
170
172
  if (status === 429 || init.code === "RATE_LIMITED") {
171
173
  return new RateLimitError(init, retryAfterMs(headers, init.details));
172
174
  }
175
+ if (init.code === "JURISDICTION_BLOCKED") return new JurisdictionBlockedError(init);
173
176
  if (env && !isKnownErrorCode(env.code)) return new CexyApiError(init);
174
177
  if (status >= 500) return new ServerError(init);
175
178
  const Cls = STATUS_CLASSES[status] ?? CexyApiError;
@@ -389,7 +392,13 @@ var Transport = class {
389
392
  let res;
390
393
  let text;
391
394
  try {
392
- res = await this.config.fetch(url.toString(), { method: info.method, headers, body, signal: ctrl.signal });
395
+ res = await this.config.fetch(url.toString(), {
396
+ method: info.method,
397
+ headers,
398
+ body,
399
+ signal: ctrl.signal,
400
+ redirect: "manual"
401
+ });
393
402
  text = await res.text();
394
403
  } catch (err) {
395
404
  if (opts.signal?.aborted) throw opts.signal.reason ?? err;
@@ -401,6 +410,14 @@ var Transport = class {
401
410
  clearTimeout(timer);
402
411
  opts.signal?.removeEventListener("abort", onAbort);
403
412
  }
413
+ if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400 || res.redirected) {
414
+ throw new CexyApiError({
415
+ status: res.status,
416
+ code: "UNEXPECTED_REDIRECT",
417
+ message: `${info.method} ${info.path}: the server answered with a redirect (HTTP ${res.status}); the SDK does not follow redirects. Check baseUrl.`,
418
+ retryable: false
419
+ });
420
+ }
404
421
  this.config.limiter?.update(res.headers);
405
422
  if (!res.ok) {
406
423
  const parsed = safeJson(text);
@@ -899,7 +916,7 @@ var TradingResource = class extends Resource {
899
916
  };
900
917
 
901
918
  // src/version.ts
902
- var VERSION = "0.1.0-dev.0";
919
+ var VERSION = "0.1.0-dev.3";
903
920
  var USER_AGENT = `cexy-typescript/${VERSION}`;
904
921
 
905
922
  // src/ws/emitter.ts
@@ -1659,7 +1676,7 @@ var CexyClient = class {
1659
1676
  assertSecureUrl(parsed, "https:", "http:", options.allowInsecure === true, "baseUrl");
1660
1677
  this.#allowInsecure = options.allowInsecure === true;
1661
1678
  const fetchImpl = options.fetch ?? globalThis.fetch;
1662
- if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 20+ required); pass options.fetch");
1679
+ if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 22+ required); pass options.fetch");
1663
1680
  const timeoutMs = options.timeoutMs ?? 1e4;
1664
1681
  const maxRetries = options.maxRetries ?? 3;
1665
1682
  if (!(timeoutMs > 0)) throw new CexyConfigError("timeoutMs must be > 0");
@@ -1749,4 +1766,4 @@ function canSetUserAgent() {
1749
1766
  return !(typeof g.window !== "undefined" && typeof g.window.document !== "undefined");
1750
1767
  }
1751
1768
 
1752
- export { AccountResource, ApiKeyAuthenticator, AssetsResource, AuthenticationError, CexyApiError, CexyClient, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, ConflictError, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, ExportsResource, FeesResource, ForbiddenError, InvalidAmountError, KNOWN_EVENT_TYPES, LiveOrderBook, MarketsResource, NetworksResource, NotFoundError, OPERATIONS, OrderStateUnknownError, PRIVATE_CHANNELS, PoolsResource, RateLimitError, RateLimiter, SUPPORTED_PROTOCOL_VERSION, ServerError, TradingResource, TypedEmitter, USER_AGENT, UnprocessableError, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, assertAmountFields, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, paginate };
1769
+ export { AccountResource, ApiKeyAuthenticator, AssetsResource, AuthenticationError, CexyApiError, CexyClient, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, ConflictError, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, ExportsResource, FeesResource, ForbiddenError, InvalidAmountError, JurisdictionBlockedError, KNOWN_EVENT_TYPES, LiveOrderBook, MarketsResource, NetworksResource, NotFoundError, OPERATIONS, OrderStateUnknownError, PRIVATE_CHANNELS, PoolsResource, RateLimitError, RateLimiter, SUPPORTED_PROTOCOL_VERSION, ServerError, TradingResource, TypedEmitter, USER_AGENT, UnprocessableError, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, assertAmountFields, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, paginate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cexyio/cexy",
3
- "version": "0.1.0-dev.0",
3
+ "version": "0.1.0-dev.3",
4
4
  "description": "Official TypeScript/JavaScript SDK for the CEXY.io REST and WebSocket API",
5
5
  "license": "MIT",
6
6
  "author": "CEXY.io",
@@ -43,7 +43,7 @@
43
43
  ],
44
44
  "sideEffects": false,
45
45
  "engines": {
46
- "node": ">=20"
46
+ "node": ">=22"
47
47
  },
48
48
  "scripts": {
49
49
  "generate": "node scripts/generate.mjs",
@@ -65,16 +65,16 @@
65
65
  }
66
66
  },
67
67
  "devDependencies": {
68
- "@eslint/js": "^9.39.0",
69
- "@types/node": "^20.19.0",
68
+ "@eslint/js": "^10.0.1",
69
+ "@types/node": "^22.20.4",
70
70
  "@types/ws": "^8.18.0",
71
- "eslint": "^9.39.0",
71
+ "eslint": "^10.11.0",
72
72
  "openapi-typescript": "^7.13.0",
73
73
  "tsup": "^8.5.0",
74
74
  "typescript": "~5.9.3",
75
75
  "typescript-eslint": "^8.70.0",
76
- "vite": "^6.4.0",
77
- "vitest": "^3.2.7",
76
+ "vite": "^8.3.0",
77
+ "vitest": "^5.0.2",
78
78
  "ws": "^8.18.0"
79
79
  },
80
80
  "publishConfig": {