refactor: centralize domain values and enforce numeric lint

This commit is contained in:
2026-09-13 14:37:39 +03:00
parent 8ba005346d
commit 8637ad2f34
44 changed files with 543 additions and 174 deletions
@@ -1,3 +1,4 @@
import { MILLISECONDS_PER_SECOND } from '../../common/time';
import { TbankAccount } from './tbank.client';
const accountTypes = {
@@ -51,7 +52,7 @@ export function accessibleTbankAccount(
function openedOn(seconds: string | number | undefined): string | null {
if (seconds === undefined) return null;
const date = new Date(Number(seconds) * 1000);
const date = new Date(Number(seconds) * MILLISECONDS_PER_SECOND);
if (Number.isNaN(date.valueOf())) return null;
return date.toISOString().slice(0, 10);
}
+25 -8
View File
@@ -1,3 +1,4 @@
import { MILLISECONDS_PER_SECOND } from '../../common/time';
import {
ChannelCredentials,
Client,
@@ -28,6 +29,13 @@ import type {
TbankShare,
} from './tbank-market.types';
const RATE_WINDOW_MS = 1_000;
const METADATA_MAX_ATTEMPTS = 3;
const RPC_MAX_ATTEMPTS = 2;
const MAX_QUEUE_SIZE = 50;
const METADATA_RETRY_DELAY_MS = 2_000;
const RPC_RETRY_DELAY_MS = 1_000;
const requestDeadlineMs = 15_000;
type Unary<Request, Response> = (
@@ -134,7 +142,7 @@ class RateLimiter {
private drain(): void {
const now = Date.now();
if (now - this.windowStartedAt >= 1000) {
if (now - this.windowStartedAt >= RATE_WINDOW_MS) {
this.windowStartedAt = now;
this.starts = 0;
}
@@ -145,7 +153,7 @@ class RateLimiter {
if (this.waiting.length && this.starts === this.perSecond) {
setTimeout(
() => this.drain(),
Math.max(1, 1000 - (now - this.windowStartedAt)),
Math.max(1, RATE_WINDOW_MS - (now - this.windowStartedAt)),
);
}
}
@@ -422,8 +430,12 @@ export class TbankClient {
'getCandles',
{
instrumentId: uid,
from: { seconds: String(Math.floor(from.valueOf() / 1000)) },
to: { seconds: String(Math.floor(to.valueOf() / 1000)) },
from: {
seconds: String(Math.floor(from.valueOf() / MILLISECONDS_PER_SECOND)),
},
to: {
seconds: String(Math.floor(to.valueOf() / MILLISECONDS_PER_SECOND)),
},
interval: 'CANDLE_INTERVAL_DAY',
limit: 2400,
},
@@ -456,10 +468,10 @@ export class TbankClient {
request: object,
): Promise<Response> {
const metadataCall = method === 'getInstrumentBy';
const maxAttempts = metadataCall ? 3 : 2;
const maxAttempts = metadataCall ? METADATA_MAX_ATTEMPTS : RPC_MAX_ATTEMPTS;
for (let attempts = 1; attempts <= maxAttempts; attempts += 1) {
const startedAt = performance.now();
const queueFull = this.queue.size >= 50;
const queueFull = this.queue.size >= MAX_QUEUE_SIZE;
try {
if (queueFull)
throw new ServiceUnavailableException({ code: 'TBANK_UNAVAILABLE' });
@@ -492,7 +504,12 @@ export class TbankClient {
durationMs: Math.round(performance.now() - startedAt),
});
await new Promise((resolve) =>
setTimeout(resolve, metadataCall ? attempts * 2000 : 1000),
setTimeout(
resolve,
metadataCall
? attempts * METADATA_RETRY_DELAY_MS
: RPC_RETRY_DELAY_MS,
),
);
continue;
}
@@ -537,5 +554,5 @@ export class TbankClient {
}
function utcSeconds(date: string): number {
return Math.floor(Date.parse(`${date}T00:00:00Z`) / 1000);
return Math.floor(Date.parse(`${date}T00:00:00Z`) / MILLISECONDS_PER_SECOND);
}
+4 -1
View File
@@ -1,5 +1,7 @@
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
const KEY_SIZE_BYTES = 32;
const VERSION = 1;
const NONCE_SIZE = 12;
const TAG_SIZE = 16;
@@ -9,7 +11,8 @@ export class TbankCrypto {
static fromBase64(key: string): TbankCrypto {
const decoded = Buffer.from(key, 'base64');
if (decoded.length !== 32) throw new Error('Invalid token encryption key');
if (decoded.length !== KEY_SIZE_BYTES)
throw new Error('Invalid token encryption key');
return new TbankCrypto(decoded);
}