@cexyio/cexy 0.1.0-dev.2 → 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 +6 -3
- package/dist/index.cjs +17 -3
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +17 -3
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ 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
|
|
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.
|
|
@@ -15,7 +15,7 @@ The official TypeScript/JavaScript SDK for the [CEXY.io](https://cexy.io) REST a
|
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
npm install @cexyio/cexy@next
|
|
18
|
-
# Node only, optional: lets the WebSocket client send a User-Agent
|
|
18
|
+
# Node only, optional: lets the WebSocket client send a User-Agent
|
|
19
19
|
npm install ws
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -215,7 +215,7 @@ use public channels and poll REST for private state. If the session is revoked,
|
|
|
215
215
|
`authLost`; public channels keep working.
|
|
216
216
|
|
|
217
217
|
In Node the client uses the optional `ws` package when installed (so it can send the SDK User-Agent),
|
|
218
|
-
otherwise the global `WebSocket` (Node
|
|
218
|
+
otherwise the global `WebSocket` (Node, browsers).
|
|
219
219
|
|
|
220
220
|
## Browsers
|
|
221
221
|
|
|
@@ -229,6 +229,9 @@ endpoints from a server. Browsers do not let scripts set `User-Agent`, so the SD
|
|
|
229
229
|
- Credentials go only in the `X-API-Key` / `X-API-Secret` headers and only on private endpoints; never in URLs.
|
|
230
230
|
- Only `https://` base URLs and `wss://` WebSocket URLs are accepted. `allowInsecure: true` permits
|
|
231
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"`.
|
|
232
235
|
- The SDK redacts the secret from `toString()`, `util.inspect`, `JSON.stringify` and error messages.
|
|
233
236
|
- Keep keys in environment variables or a secret manager, not in code.
|
|
234
237
|
|
package/dist/index.cjs
CHANGED
|
@@ -394,7 +394,13 @@ var Transport = class {
|
|
|
394
394
|
let res;
|
|
395
395
|
let text;
|
|
396
396
|
try {
|
|
397
|
-
res = await this.config.fetch(url.toString(), {
|
|
397
|
+
res = await this.config.fetch(url.toString(), {
|
|
398
|
+
method: info.method,
|
|
399
|
+
headers,
|
|
400
|
+
body,
|
|
401
|
+
signal: ctrl.signal,
|
|
402
|
+
redirect: "manual"
|
|
403
|
+
});
|
|
398
404
|
text = await res.text();
|
|
399
405
|
} catch (err) {
|
|
400
406
|
if (opts.signal?.aborted) throw opts.signal.reason ?? err;
|
|
@@ -406,6 +412,14 @@ var Transport = class {
|
|
|
406
412
|
clearTimeout(timer);
|
|
407
413
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
408
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
|
+
}
|
|
409
423
|
this.config.limiter?.update(res.headers);
|
|
410
424
|
if (!res.ok) {
|
|
411
425
|
const parsed = safeJson(text);
|
|
@@ -904,7 +918,7 @@ var TradingResource = class extends Resource {
|
|
|
904
918
|
};
|
|
905
919
|
|
|
906
920
|
// src/version.ts
|
|
907
|
-
var VERSION = "0.1.0-dev.
|
|
921
|
+
var VERSION = "0.1.0-dev.3";
|
|
908
922
|
var USER_AGENT = `cexy-typescript/${VERSION}`;
|
|
909
923
|
|
|
910
924
|
// src/ws/emitter.ts
|
|
@@ -1664,7 +1678,7 @@ var CexyClient = class {
|
|
|
1664
1678
|
assertSecureUrl(parsed, "https:", "http:", options.allowInsecure === true, "baseUrl");
|
|
1665
1679
|
this.#allowInsecure = options.allowInsecure === true;
|
|
1666
1680
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1667
|
-
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node
|
|
1681
|
+
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 22+ required); pass options.fetch");
|
|
1668
1682
|
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
1669
1683
|
const maxRetries = options.maxRetries ?? 3;
|
|
1670
1684
|
if (!(timeoutMs > 0)) throw new CexyConfigError("timeoutMs must be > 0");
|
package/dist/index.d.cts
CHANGED
|
@@ -3844,7 +3844,7 @@ interface CexyClientOptions {
|
|
|
3844
3844
|
timeoutMs?: number;
|
|
3845
3845
|
/** Retries after the first attempt for retryable failures. Default 3. */
|
|
3846
3846
|
maxRetries?: number;
|
|
3847
|
-
/** A `fetch` implementation. Default: the global `fetch` (Node
|
|
3847
|
+
/** A `fetch` implementation. Default: the global `fetch` (Node 22+, browsers). */
|
|
3848
3848
|
fetch?: FetchLike;
|
|
3849
3849
|
/**
|
|
3850
3850
|
* Client-side rate limit in requests per minute, or `false` to disable. Default 100/min
|
|
@@ -3925,9 +3925,9 @@ declare function isAmount(value: unknown): value is Amount;
|
|
|
3925
3925
|
declare function assertAmountFields(body: Record<string, unknown>, fields: readonly string[], context: string): void;
|
|
3926
3926
|
|
|
3927
3927
|
/** SDK version, kept in sync with package.json (a test enforces this). */
|
|
3928
|
-
declare const VERSION = "0.1.0-dev.
|
|
3928
|
+
declare const VERSION = "0.1.0-dev.3";
|
|
3929
3929
|
/** Default User-Agent product token. */
|
|
3930
|
-
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.
|
|
3930
|
+
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.3";
|
|
3931
3931
|
|
|
3932
3932
|
/** True for loopback hosts, the only ones where plain-text transport may be allowed. */
|
|
3933
3933
|
declare function isLocalHost(hostname: string): boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -3844,7 +3844,7 @@ interface CexyClientOptions {
|
|
|
3844
3844
|
timeoutMs?: number;
|
|
3845
3845
|
/** Retries after the first attempt for retryable failures. Default 3. */
|
|
3846
3846
|
maxRetries?: number;
|
|
3847
|
-
/** A `fetch` implementation. Default: the global `fetch` (Node
|
|
3847
|
+
/** A `fetch` implementation. Default: the global `fetch` (Node 22+, browsers). */
|
|
3848
3848
|
fetch?: FetchLike;
|
|
3849
3849
|
/**
|
|
3850
3850
|
* Client-side rate limit in requests per minute, or `false` to disable. Default 100/min
|
|
@@ -3925,9 +3925,9 @@ declare function isAmount(value: unknown): value is Amount;
|
|
|
3925
3925
|
declare function assertAmountFields(body: Record<string, unknown>, fields: readonly string[], context: string): void;
|
|
3926
3926
|
|
|
3927
3927
|
/** SDK version, kept in sync with package.json (a test enforces this). */
|
|
3928
|
-
declare const VERSION = "0.1.0-dev.
|
|
3928
|
+
declare const VERSION = "0.1.0-dev.3";
|
|
3929
3929
|
/** Default User-Agent product token. */
|
|
3930
|
-
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.
|
|
3930
|
+
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.3";
|
|
3931
3931
|
|
|
3932
3932
|
/** True for loopback hosts, the only ones where plain-text transport may be allowed. */
|
|
3933
3933
|
declare function isLocalHost(hostname: string): boolean;
|
package/dist/index.js
CHANGED
|
@@ -392,7 +392,13 @@ var Transport = class {
|
|
|
392
392
|
let res;
|
|
393
393
|
let text;
|
|
394
394
|
try {
|
|
395
|
-
res = await this.config.fetch(url.toString(), {
|
|
395
|
+
res = await this.config.fetch(url.toString(), {
|
|
396
|
+
method: info.method,
|
|
397
|
+
headers,
|
|
398
|
+
body,
|
|
399
|
+
signal: ctrl.signal,
|
|
400
|
+
redirect: "manual"
|
|
401
|
+
});
|
|
396
402
|
text = await res.text();
|
|
397
403
|
} catch (err) {
|
|
398
404
|
if (opts.signal?.aborted) throw opts.signal.reason ?? err;
|
|
@@ -404,6 +410,14 @@ var Transport = class {
|
|
|
404
410
|
clearTimeout(timer);
|
|
405
411
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
406
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
|
+
}
|
|
407
421
|
this.config.limiter?.update(res.headers);
|
|
408
422
|
if (!res.ok) {
|
|
409
423
|
const parsed = safeJson(text);
|
|
@@ -902,7 +916,7 @@ var TradingResource = class extends Resource {
|
|
|
902
916
|
};
|
|
903
917
|
|
|
904
918
|
// src/version.ts
|
|
905
|
-
var VERSION = "0.1.0-dev.
|
|
919
|
+
var VERSION = "0.1.0-dev.3";
|
|
906
920
|
var USER_AGENT = `cexy-typescript/${VERSION}`;
|
|
907
921
|
|
|
908
922
|
// src/ws/emitter.ts
|
|
@@ -1662,7 +1676,7 @@ var CexyClient = class {
|
|
|
1662
1676
|
assertSecureUrl(parsed, "https:", "http:", options.allowInsecure === true, "baseUrl");
|
|
1663
1677
|
this.#allowInsecure = options.allowInsecure === true;
|
|
1664
1678
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1665
|
-
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node
|
|
1679
|
+
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 22+ required); pass options.fetch");
|
|
1666
1680
|
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
1667
1681
|
const maxRetries = options.maxRetries ?? 3;
|
|
1668
1682
|
if (!(timeoutMs > 0)) throw new CexyConfigError("timeoutMs must be > 0");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cexyio/cexy",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
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": ">=
|
|
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": "^
|
|
69
|
-
"@types/node": "^20.
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@types/node": "^22.20.4",
|
|
70
70
|
"@types/ws": "^8.18.0",
|
|
71
|
-
"eslint": "^
|
|
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": "^
|
|
77
|
-
"vitest": "^
|
|
76
|
+
"vite": "^8.3.0",
|
|
77
|
+
"vitest": "^5.0.2",
|
|
78
78
|
"ws": "^8.18.0"
|
|
79
79
|
},
|
|
80
80
|
"publishConfig": {
|