@n1xyz/nord-ts 0.0.19 → 0.0.21

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.
Files changed (52) hide show
  1. package/dist/const.d.ts +8 -0
  2. package/dist/const.js +30 -0
  3. package/dist/gen/nord.d.ts +882 -0
  4. package/dist/gen/nord.js +6520 -0
  5. package/dist/gen/openapi.d.ts +2244 -0
  6. package/dist/gen/openapi.js +6 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/index.js +21 -0
  9. package/dist/nord/api/actions.d.ts +135 -0
  10. package/dist/nord/api/actions.js +313 -0
  11. package/dist/nord/api/core.d.ts +16 -0
  12. package/dist/nord/api/core.js +81 -0
  13. package/dist/nord/api/metrics.d.ts +67 -0
  14. package/dist/nord/api/metrics.js +229 -0
  15. package/dist/nord/client/Nord.d.ts +282 -0
  16. package/dist/nord/client/Nord.js +528 -0
  17. package/dist/nord/client/NordUser.d.ts +340 -0
  18. package/dist/nord/client/NordUser.js +652 -0
  19. package/dist/nord/index.d.ts +7 -0
  20. package/dist/nord/index.js +31 -0
  21. package/dist/nord/models/Subscriber.d.ts +37 -0
  22. package/dist/nord/models/Subscriber.js +25 -0
  23. package/dist/nord/utils/NordError.d.ts +35 -0
  24. package/dist/nord/utils/NordError.js +49 -0
  25. package/dist/types.d.ts +251 -0
  26. package/dist/types.js +101 -0
  27. package/dist/utils.d.ts +98 -0
  28. package/dist/utils.js +237 -0
  29. package/dist/websocket/NordWebSocketClient.d.ts +57 -0
  30. package/dist/websocket/NordWebSocketClient.js +251 -0
  31. package/dist/websocket/events.d.ts +19 -0
  32. package/dist/websocket/events.js +2 -0
  33. package/dist/websocket/index.d.ts +2 -0
  34. package/dist/websocket/index.js +5 -0
  35. package/package.json +8 -2
  36. package/src/gen/.gitkeep +0 -0
  37. package/src/gen/nord.ts +7593 -0
  38. package/src/gen/openapi.ts +2245 -0
  39. package/src/nord/api/core.ts +1 -16
  40. package/src/nord/client/Nord.ts +7 -11
  41. package/src/types.ts +0 -11
  42. package/src/websocket/NordWebSocketClient.ts +0 -113
  43. package/.claude/settings.local.json +0 -11
  44. package/.local/qa.ts +0 -77
  45. package/.local/test-atomic.ts +0 -112
  46. package/.prettierignore +0 -2
  47. package/check.sh +0 -4
  48. package/default.nix +0 -47
  49. package/eslint.config.mjs +0 -66
  50. package/tests/utils.spec.ts +0 -121
  51. package/tsconfig.eslint.json +0 -12
  52. package/tsconfig.json +0 -24
@@ -0,0 +1,528 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.Nord = void 0;
40
+ const proton_1 = require("@n1xyz/proton");
41
+ const web3_js_1 = require("@solana/web3.js");
42
+ const events_1 = require("events");
43
+ const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
44
+ const proto = __importStar(require("../../gen/nord"));
45
+ const types_1 = require("../../types");
46
+ const utils = __importStar(require("../../utils"));
47
+ const core = __importStar(require("../api/core"));
48
+ const metrics = __importStar(require("../api/metrics"));
49
+ const NordError_1 = require("../utils/NordError");
50
+ /**
51
+ * Main Nord client class for interacting with the Nord API
52
+ */
53
+ class Nord {
54
+ /**
55
+ * Create a new Nord client
56
+ *
57
+ * @param config - Configuration options for the Nord client
58
+ * @param config.webServerUrl - Base URL for the Nord web server
59
+ * @param config.bridgeVk - Bridge verification key
60
+ * @param config.solanaUrl - Solana cluster URL
61
+ * @throws {Error} If required configuration is missing
62
+ */
63
+ constructor({ bridgeVk, solanaUrl, webServerUrl, protonClient, }) {
64
+ /** Available markets */
65
+ this.markets = [];
66
+ /** Available tokens */
67
+ this.tokens = [];
68
+ /** Map of symbol to market_id */
69
+ this.symbolToMarketId = new Map();
70
+ this.webServerUrl = webServerUrl;
71
+ this.bridgeVk = bridgeVk;
72
+ this.solanaUrl = solanaUrl;
73
+ this.protonClient = protonClient;
74
+ this.httpClient = (0, openapi_fetch_1.default)({ baseUrl: webServerUrl });
75
+ }
76
+ /**
77
+ * Create a WebSocket client with specific subscriptions
78
+ *
79
+ * @param options - Subscription options that specify which data streams to subscribe to
80
+ * @returns A new WebSocket client with the requested subscriptions
81
+ * @throws {NordError} If invalid subscription options are provided
82
+ *
83
+ * @example
84
+ * // Create a client for trades and deltas from one market and an account
85
+ * const wsClient = nord.createWebSocketClient({
86
+ * trades: ["BTCUSDC"],
87
+ * deltas: ["BTCUSDC"],
88
+ * accounts: [123]
89
+ * });
90
+ *
91
+ * @example
92
+ * // Create a client for trades from multiple markets
93
+ * const tradesClient = nord.createWebSocketClient({
94
+ * trades: ["BTCUSDC", "ETHUSDC"]
95
+ * });
96
+ */
97
+ createWebSocketClient(options) {
98
+ const subscriptions = [];
99
+ // Add trade subscriptions
100
+ if (options.trades && options.trades.length > 0) {
101
+ options.trades.forEach((symbol) => {
102
+ subscriptions.push(`trades@${symbol}`);
103
+ });
104
+ }
105
+ // Add delta subscriptions
106
+ if (options.deltas && options.deltas.length > 0) {
107
+ options.deltas.forEach((symbol) => {
108
+ subscriptions.push(`deltas@${symbol}`);
109
+ });
110
+ }
111
+ // Add account subscriptions
112
+ if (options.accounts && options.accounts.length > 0) {
113
+ options.accounts.forEach((accountId) => {
114
+ if (isNaN(accountId) || accountId <= 0) {
115
+ throw new NordError_1.NordError(`Invalid account ID: ${accountId}. Must be a positive number.`);
116
+ }
117
+ subscriptions.push(`account@${accountId}`);
118
+ });
119
+ }
120
+ // Validate that at least one subscription was provided
121
+ if (subscriptions.length === 0) {
122
+ throw new NordError_1.NordError("At least one subscription must be provided");
123
+ }
124
+ // Create and return a new WebSocket client
125
+ return core.initWebSocketClient(this.webServerUrl, subscriptions);
126
+ }
127
+ async GET(path, options) {
128
+ const r = await this.httpClient.GET(path, options);
129
+ if (r.error) {
130
+ throw new NordError_1.NordError(`failed to GET ${path}`, { cause: r.error });
131
+ }
132
+ if (r.data === undefined) {
133
+ // this should never happen, but the type checker seems unhappy.
134
+ // if we catch this we'll need to debug accordingly.
135
+ throw new NordError_1.NordError("internal assertion violation", { cause: r });
136
+ }
137
+ return r.data;
138
+ }
139
+ /**
140
+ * Get the current timestamp from the Nord server
141
+ *
142
+ * @returns Current timestamp as a bigint
143
+ * @throws {NordError} If the request fails
144
+ */
145
+ async getTimestamp() {
146
+ return BigInt(await this.GET("/timestamp", {}));
147
+ }
148
+ /**
149
+ * Get the last event nonce from the Nord server
150
+ *
151
+ * @returns Next action nonce
152
+ * @throws {NordError} If the request fails
153
+ */
154
+ async getActionNonce() {
155
+ return await this.GET("/event/last-acked-nonce", {});
156
+ }
157
+ /**
158
+ * Fetch information about Nord markets and tokens
159
+ *
160
+ * @throws {NordError} If the request fails
161
+ */
162
+ async fetchNordInfo() {
163
+ try {
164
+ const info = await this.GET("/info", {});
165
+ this.markets = info.markets;
166
+ this.tokens = info.tokens;
167
+ // Populate the symbolToMarketId map
168
+ this.symbolToMarketId.clear();
169
+ info.markets.forEach((market) => {
170
+ this.symbolToMarketId.set(market.symbol, market.marketId);
171
+ });
172
+ }
173
+ catch (error) {
174
+ throw new NordError_1.NordError("Failed to fetch Nord info", { cause: error });
175
+ }
176
+ }
177
+ /**
178
+ * Initialize a new Nord client
179
+ *
180
+ * @param nordConfig - Configuration options for the Nord client
181
+ * @param nordConfig.webServerUrl - Base URL for the Nord web server
182
+ * @param nordConfig.bridgeVk - Bridge verification key
183
+ * @param nordConfig.solanaUrl - Solana cluster URL
184
+ * @returns Initialized Nord client
185
+ * @throws {NordError} If initialization fails
186
+ */
187
+ static async initNord({ bridgeVk: bridgeVk_, solanaUrl, webServerUrl, }) {
188
+ // TODO: we should parametrize the connectionn not have it done here.
189
+ // this is a dogshit api, only here to be compatible with the shitty
190
+ // vibecoded code and not break zero one team's workflow.
191
+ const connection = new web3_js_1.Connection(solanaUrl, { commitment: "confirmed" });
192
+ const bridgeVk = new web3_js_1.PublicKey(bridgeVk_);
193
+ const protonClient = await proton_1.ProtonClient.init({
194
+ protonUrl: webServerUrl,
195
+ bridgeVk,
196
+ solConn: connection,
197
+ });
198
+ const nord = new Nord({
199
+ bridgeVk,
200
+ protonClient,
201
+ solanaUrl,
202
+ webServerUrl,
203
+ });
204
+ await nord.init();
205
+ return nord;
206
+ }
207
+ /**
208
+ * Initialize the Nord client
209
+ * @private
210
+ */
211
+ async init() {
212
+ await this.fetchNordInfo();
213
+ }
214
+ /**
215
+ * Query a specific action
216
+ *
217
+ * @param query - Action query parameters
218
+ * @returns Action response
219
+ * @throws {NordError} If the request fails
220
+ */
221
+ async queryAction({ action_id, }) {
222
+ return ((await this.queryRecentActions({
223
+ from: action_id,
224
+ to: action_id,
225
+ }))[0] ?? null);
226
+ }
227
+ /**
228
+ * Query recent actions
229
+ *
230
+ * @param from - Starting action index
231
+ * @param to - Ending action index
232
+ * @returns Actions response
233
+ * @throws {NordError} If the request fails
234
+ */
235
+ async queryRecentActions(query) {
236
+ const xs = await this.GET("/action", {
237
+ params: {
238
+ query,
239
+ },
240
+ });
241
+ return xs.map((x) => ({
242
+ actionId: x.actionId,
243
+ action: utils.decodeLengthDelimited(Buffer.from(x.payload, "base64"), proto.Action),
244
+ physicalExecTime: new Date(x.physicalTime * 1000),
245
+ }));
246
+ }
247
+ /**
248
+ * Get the last action ID
249
+ *
250
+ * @returns Last action ID
251
+ * @throws {NordError} If the request fails
252
+ */
253
+ async getLastActionId() {
254
+ return await this.GET("/action/last-executed-id", {});
255
+ }
256
+ /**
257
+ * Fetch aggregate metrics from the Nord API
258
+ *
259
+ * @param txPeakTpsPeriod - Period for peak TPS calculation
260
+ * @param txPeakTpsPeriodUnit - Unit for peak TPS period
261
+ * @returns Aggregate metrics
262
+ * @throws {NordError} If the request fails
263
+ */
264
+ async aggregateMetrics(txPeakTpsPeriod = 1, txPeakTpsPeriodUnit = types_1.PeakTpsPeriodUnit.Day) {
265
+ return metrics.aggregateMetrics(this.webServerUrl, txPeakTpsPeriod, txPeakTpsPeriodUnit);
266
+ }
267
+ /**
268
+ * Get current transactions per second
269
+ *
270
+ * @param period - Time period for the query
271
+ * @returns Current TPS value
272
+ * @throws {NordError} If the request fails
273
+ */
274
+ async getCurrentTps(period = "1m") {
275
+ return metrics.getCurrentTps(this.webServerUrl, period);
276
+ }
277
+ /**
278
+ * Get peak transactions per second
279
+ *
280
+ * @param period - Time period for the query
281
+ * @returns Peak TPS value
282
+ * @throws {NordError} If the request fails
283
+ */
284
+ async getPeakTps(period = "24h") {
285
+ return metrics.getPeakTps(this.webServerUrl, period);
286
+ }
287
+ /**
288
+ * Get median transaction latency
289
+ *
290
+ * @param period - Time period for the query
291
+ * @returns Median latency in milliseconds
292
+ * @throws {NordError} If the request fails
293
+ */
294
+ async getMedianLatency(period = "1m") {
295
+ return metrics.getMedianLatency(this.webServerUrl, period);
296
+ }
297
+ /**
298
+ * Get total transaction count
299
+ *
300
+ * @returns Total transaction count
301
+ * @throws {NordError} If the request fails
302
+ */
303
+ async getTotalTransactions() {
304
+ return metrics.getTotalTransactions(this.webServerUrl);
305
+ }
306
+ /**
307
+ * Query Prometheus metrics
308
+ *
309
+ * @param params - Prometheus query parameters
310
+ * @returns Query result as a number
311
+ * @throws {NordError} If the request fails
312
+ */
313
+ async queryPrometheus(params) {
314
+ return metrics.queryPrometheus(this.webServerUrl, params);
315
+ }
316
+ /**
317
+ * Subscribe to orderbook updates for a market
318
+ *
319
+ * @param symbol - Market symbol
320
+ * @returns Orderbook subscription
321
+ * @throws {NordError} If symbol is invalid
322
+ */
323
+ subscribeOrderbook(symbol) {
324
+ if (!symbol || typeof symbol !== "string") {
325
+ throw new NordError_1.NordError("Invalid market symbol");
326
+ }
327
+ const subscription = new events_1.EventEmitter();
328
+ const wsClient = this.createWebSocketClient({
329
+ deltas: [symbol],
330
+ });
331
+ const handleDelta = (update) => {
332
+ if (update.symbol !== symbol) {
333
+ return;
334
+ }
335
+ subscription.emit("message", update);
336
+ };
337
+ wsClient.on("delta", handleDelta);
338
+ subscription.close = () => {
339
+ wsClient.removeListener("delta", handleDelta);
340
+ subscription.removeAllListeners();
341
+ };
342
+ return subscription;
343
+ }
344
+ /**
345
+ * Subscribe to trade updates for a market
346
+ *
347
+ * @param symbol - Market symbol
348
+ * @returns Trade subscription
349
+ * @throws {NordError} If symbol is invalid
350
+ */
351
+ subscribeTrades(symbol) {
352
+ if (!symbol || typeof symbol !== "string") {
353
+ throw new NordError_1.NordError("Invalid market symbol");
354
+ }
355
+ const subscription = new events_1.EventEmitter();
356
+ const wsClient = this.createWebSocketClient({
357
+ trades: [symbol],
358
+ });
359
+ const handleTrade = (update) => {
360
+ if (update.symbol !== symbol) {
361
+ return;
362
+ }
363
+ subscription.emit("message", update);
364
+ };
365
+ wsClient.on("trades", handleTrade);
366
+ subscription.close = () => {
367
+ wsClient.removeListener("trades", handleTrade);
368
+ subscription.removeAllListeners();
369
+ };
370
+ return subscription;
371
+ }
372
+ /**
373
+ * Subscribe to account updates
374
+ *
375
+ * @param accountId - Account ID to subscribe to
376
+ * @returns User subscription
377
+ * @throws {NordError} If accountId is invalid
378
+ */
379
+ subscribeAccount(accountId) {
380
+ if (isNaN(accountId) || accountId <= 0) {
381
+ throw new NordError_1.NordError("Invalid account ID");
382
+ }
383
+ const subscription = new events_1.EventEmitter();
384
+ const wsClient = this.createWebSocketClient({
385
+ accounts: [accountId],
386
+ });
387
+ const handleAccountUpdate = (update) => {
388
+ if (update.account_id !== accountId) {
389
+ return;
390
+ }
391
+ subscription.emit("message", update);
392
+ };
393
+ wsClient.on("account", handleAccountUpdate);
394
+ subscription.close = () => {
395
+ wsClient.removeListener("account", handleAccountUpdate);
396
+ subscription.removeAllListeners();
397
+ };
398
+ return subscription;
399
+ }
400
+ /**
401
+ * Get trades for a market
402
+ *
403
+ * @param query - Trades query parameters
404
+ * @returns Trades response
405
+ * @throws {NordError} If the request fails
406
+ */
407
+ async getTrades(query) {
408
+ if (query.sinceRcf3339 && !utils.isRfc3339(query.sinceRcf3339)) {
409
+ throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.sinceRcf3339}`);
410
+ }
411
+ if (query.untilRfc3339 && !utils.isRfc3339(query.untilRfc3339)) {
412
+ throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.untilRfc3339}`);
413
+ }
414
+ return await this.GET("/trades", {
415
+ params: {
416
+ query: {
417
+ takerId: query.takerId,
418
+ makerId: query.makerId,
419
+ marketId: query.marketId,
420
+ pageSize: query.pageSize,
421
+ takerSide: query.takerSide,
422
+ since: query.sinceRcf3339,
423
+ until: query.untilRfc3339,
424
+ startInclusive: query.pageId,
425
+ },
426
+ },
427
+ });
428
+ }
429
+ /**
430
+ * Get user account IDs
431
+ *
432
+ * @param query - User account IDs query parameters
433
+ * @returns User account IDs response
434
+ * @throws {NordError} If the request fails
435
+ */
436
+ async getUser(query) {
437
+ const r = await this.httpClient.GET("/user/{pubkey}", {
438
+ params: {
439
+ path: { pubkey: query.pubkey.toString() },
440
+ },
441
+ });
442
+ if (r.response.status === 404) {
443
+ return null;
444
+ }
445
+ return r.data;
446
+ }
447
+ /**
448
+ * Get orderbook for a market
449
+ *
450
+ * @param query - Orderbook query parameters (either market_id or symbol must be provided)
451
+ * @returns Orderbook response
452
+ * @throws {NordError} If the request fails or if the market symbol is unknown
453
+ * @remarks It's recommended to initialize the Nord client using the static `initNord` method
454
+ * to ensure market information is properly loaded before calling this method.
455
+ */
456
+ async getOrderbook(query) {
457
+ // If only symbol is provided, convert it to market_id
458
+ let marketId;
459
+ if (query.symbol && query.market_id === undefined) {
460
+ // If the map is empty, try to fetch market information first
461
+ if (this.symbolToMarketId.size === 0) {
462
+ await this.fetchNordInfo();
463
+ }
464
+ const id = this.symbolToMarketId.get(query.symbol);
465
+ if (id === undefined) {
466
+ throw new NordError_1.NordError(`Unknown market symbol: ${query.symbol}`);
467
+ }
468
+ marketId = id;
469
+ }
470
+ else if (query.market_id !== undefined) {
471
+ marketId = query.market_id;
472
+ }
473
+ else {
474
+ throw new NordError_1.NordError("Either symbol or market_id must be provided for orderbook query");
475
+ }
476
+ return await this.GET("/market/{market_id}/orderbook", {
477
+ params: {
478
+ path: { market_id: marketId },
479
+ },
480
+ });
481
+ }
482
+ /**
483
+ * Get information about the Nord server
484
+ *
485
+ * @returns Information about markets and tokens
486
+ * @throws {NordError} If the request fails
487
+ */
488
+ async getInfo() {
489
+ return await this.GET("/info", {});
490
+ }
491
+ /**
492
+ * Get account information
493
+ *
494
+ * @param accountId - Account ID to get information for
495
+ * @returns Account information
496
+ * @throws {NordError} If the request fails
497
+ */
498
+ async getAccount(accountId) {
499
+ return await this.GET("/account/{account_id}", {
500
+ params: {
501
+ path: { account_id: accountId },
502
+ },
503
+ });
504
+ }
505
+ /**
506
+ * Get market statistics (alias for marketsStats for backward compatibility)
507
+ *
508
+ * @returns Market statistics response
509
+ */
510
+ async getMarketStats({ marketId, }) {
511
+ return await this.GET("/market/{market_id}/stats", {
512
+ params: {
513
+ path: { market_id: marketId },
514
+ },
515
+ });
516
+ }
517
+ /**
518
+ * Check if an account exists for the given address
519
+ *
520
+ * @param address - The public key address to check
521
+ * @returns True if the account exists, false otherwise
522
+ * @deprecated use getUser instead
523
+ */
524
+ async accountExists(pubkey) {
525
+ return !!(await this.getUser({ pubkey }));
526
+ }
527
+ }
528
+ exports.Nord = Nord;