diff --git a/.github/workflows/js-checks.yml b/.github/workflows/js-checks.yml new file mode 100644 index 0000000..18db088 --- /dev/null +++ b/.github/workflows/js-checks.yml @@ -0,0 +1,15 @@ +name: JS Checks + +on: + pull_request: + push: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +jobs: + js-checks: + uses: hemilabs/actions/.github/workflows/js-checks.yml@main + with: + node-versions: '["16", "18", "20", "22"]' diff --git a/apps/api/.eslintrc.js b/apps/api/.eslintrc.js deleted file mode 100644 index d961833..0000000 --- a/apps/api/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended' - ], - ignorePatterns: ['dist', '.eslintrc.js', '**/*.spec.ts'], - parser: '@typescript-eslint/parser', -} diff --git a/apps/api/.eslintrc.json b/apps/api/.eslintrc.json new file mode 100644 index 0000000..96a012c --- /dev/null +++ b/apps/api/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "extends": ["bloq", "prettier"], + "ignorePatterns": ["dist", "_esm/*", "_cjs/*", "_types/*"], + "overrides": [ + { + "extends": ["bloq/typescript", "prettier"], + "files": ["src/**/*.ts"] + }, + { + "extends": ["bloq/markdown"], + "files": ["*.md"] + }, + { + "extends": ["bloq/vitest", "prettier"], + "files": ["*.spec.{js,ts}"] + } + ], + "parserOptions": { + "sourceType": "module" + }, + "root": true, + "rules": { + "arrow-body-style": "off", + "no-console": "off" + } +} diff --git a/apps/api/.prettierrc.json b/apps/api/.prettierrc.json new file mode 100644 index 0000000..21c021c --- /dev/null +++ b/apps/api/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "arrowParens": "avoid", + "quoteProps": "consistent", + "semi": false, + "singleQuote": true, + "tabWidth": 2 +} diff --git a/apps/api/README.md b/apps/api/README.md index 6348138..cb2a5b4 100644 --- a/apps/api/README.md +++ b/apps/api/README.md @@ -90,7 +90,6 @@ The environment variables are defined in the `.env` file. The following variable - `ENABLE_MAINNET`: A boolean that enables mainnet.[Hemi Docs](https://github.com/hemilabs/infrastructure/blob/main/NETWORK_INFO.md). - Example of the .env file ``` @@ -99,8 +98,10 @@ ENABLE_MAINNET=false ``` ## Contribution + If you want to contribute to this project and make it better, your help is very welcome. You can find more information about how to contribute in the [`CONTRIBUTING.md`](../../CONTRIBUTING.md) file. ## License + This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details. diff --git a/apps/api/package.json b/apps/api/package.json index 1295926..5efe6f6 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -8,15 +8,16 @@ "test:cov": "vitest run src --coverage", "test:watch": "vitest watch src", "dev": "nodemon", + "format:check": "prettier --check .", + "format:fix": "npx prettier --write .", "build": "tsc -p tsconfig.build.json", - "lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --ext ts", "start:api": "node dist/server.js" }, "author": "", "license": "MIT", "dependencies": { "@cryptochords/shared": "*", - "@typescript-eslint/eslint-plugin": "8.18.0", "dotenv": "16.4.7", "express": "4.21.2", "web3": "4.16.0" @@ -27,8 +28,12 @@ "@types/node": "22.10.2", "@types/ws": "8.5.13", "@vitest/coverage-v8": "2.1.8", - "eslint": "8.56.0", + "@typescript-eslint/eslint-plugin": "^8.18.2", + "@typescript-eslint/parser": "^8.18.2", + "eslint": "^8.57.1", + "eslint-config-bloq": "^4.4.1", "nodemon": "3.1.9", + "prettier": "^3.3.3", "ts-node": "10.9.2", "typescript": "5.3.3", "vitest": "2.1.8" diff --git a/apps/api/src/application/helpers/BroadcastToClients.ts b/apps/api/src/application/helpers/BroadcastToClients.ts index 25fb9bc..5b21c7e 100644 --- a/apps/api/src/application/helpers/BroadcastToClients.ts +++ b/apps/api/src/application/helpers/BroadcastToClients.ts @@ -1,12 +1,12 @@ -import WebSocket, { WebSocketServer } from 'ws'; -import { L2Block } from '@cryptochords/shared'; +import WebSocket, { WebSocketServer } from 'ws' +import { L2Block } from '@cryptochords/shared' -const BroadcastToClients = (wss: WebSocketServer, l2Block: L2Block): void => { +const broadcastToClients = (wss: WebSocketServer, l2Block: L2Block): void => { wss.clients.forEach((client: WebSocket) => { if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify(l2Block.toJSON())); + client.send(JSON.stringify(l2Block.toJSON())) } - }); -}; + }) +} -export default BroadcastToClients +export default broadcastToClients diff --git a/apps/api/src/application/helpers/broadcastToClients.spec.ts b/apps/api/src/application/helpers/broadcastToClients.spec.ts index ff56996..b4f3601 100644 --- a/apps/api/src/application/helpers/broadcastToClients.spec.ts +++ b/apps/api/src/application/helpers/broadcastToClients.spec.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { describe, it, expect, vi } from 'vitest'; -import WebSocket, { WebSocketServer } from 'ws'; -import BroadcastToClients from './BroadcastToClients'; +import { describe, it, expect, vi } from 'vitest' +import WebSocket, { WebSocketServer } from 'ws' +import broadcastToClients from './broadcastToClients' describe('BroadcastToClients', () => { it('should send a message to all connected clients', () => { - const mockSend = vi.fn(); - const wss: any = new WebSocketServer({ noServer: true }); + const mockSend = vi.fn() + const wss: any = new WebSocketServer({ noServer: true }) wss.clients = new Set([ { readyState: WebSocket.OPEN, @@ -16,15 +16,15 @@ describe('BroadcastToClients', () => { readyState: WebSocket.CLOSED, send: mockSend, }, - ]); + ]) const l2Block = { toJSON: () => ({ key: 'value' }), - }; + } - BroadcastToClients(wss, l2Block as any); + broadcastToClients(wss, l2Block as any) - expect(mockSend).toHaveBeenCalledTimes(1); - expect(mockSend).toHaveBeenCalledWith(JSON.stringify({ key: 'value' })); - }); -}); + expect(mockSend).toHaveBeenCalledTimes(1) + expect(mockSend).toHaveBeenCalledWith(JSON.stringify({ key: 'value' })) + }) +}) diff --git a/apps/api/src/application/polling/pollingService.spec.ts b/apps/api/src/application/polling/pollingService.spec.ts index 42682ae..6337e66 100644 --- a/apps/api/src/application/polling/pollingService.spec.ts +++ b/apps/api/src/application/polling/pollingService.spec.ts @@ -1,49 +1,49 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { WebSocketServer } from 'ws'; -import { EventEmitter } from 'events'; -import BroadcastToClients from '../helpers/BroadcastToClients'; -import { TxTypesEnum } from '@cryptochords/shared'; -import { PollingService } from './pollingService'; - -vi.mock('ws'); +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { WebSocketServer } from 'ws' +import { EventEmitter } from 'events' +import broadcastToClients from '../helpers/broadcastToClients' +import { TxTypesEnum } from '@cryptochords/shared' +import { PollingService } from './pollingService' + +vi.mock('ws') vi.mock('../helpers/BroadcastToClients', () => ({ __esModule: true, default: vi.fn(), -})); +})) class MockBlockRepository extends EventEmitter { - execute = vi.fn(); - stop = vi.fn(); + execute = vi.fn() + stop = vi.fn() } describe('PollingService', () => { - let pollingService: PollingService; - let mockBlockRepository: MockBlockRepository; - let mockWss: WebSocketServer; + let pollingService: PollingService + let mockBlockRepository: MockBlockRepository + let mockWss: WebSocketServer beforeEach(() => { - mockBlockRepository = new MockBlockRepository(); - mockWss = new WebSocketServer({ noServer: true }); - pollingService = new PollingService(mockBlockRepository); - }); + mockBlockRepository = new MockBlockRepository() + mockWss = new WebSocketServer({ noServer: true }) + pollingService = new PollingService(mockBlockRepository) + }) it('should execute and handle Block and Eth events', () => { - const websocketUrl = 'ws://test.url'; - const mockL2Block = { key: 'Value' }; + const websocketUrl = 'ws://test.url' + const mockL2Block = { key: 'Value' } - pollingService.execute(mockWss, websocketUrl); + pollingService.execute(mockWss, websocketUrl) - expect(mockBlockRepository.execute).toHaveBeenCalledWith(websocketUrl); + expect(mockBlockRepository.execute).toHaveBeenCalledWith(websocketUrl) - mockBlockRepository.emit(TxTypesEnum.Block, mockL2Block); - mockBlockRepository.emit(TxTypesEnum.Eth, mockL2Block); + mockBlockRepository.emit(TxTypesEnum.Block, mockL2Block) + mockBlockRepository.emit(TxTypesEnum.Eth, mockL2Block) - expect(BroadcastToClients).toHaveBeenCalledWith(mockWss, mockL2Block); - expect(BroadcastToClients).toHaveBeenCalledTimes(2); - }); + expect(broadcastToClients).toHaveBeenCalledWith(mockWss, mockL2Block) + expect(broadcastToClients).toHaveBeenCalledTimes(2) + }) it('should stop the repository on stop', () => { - pollingService.stop(); - expect(mockBlockRepository.stop).toHaveBeenCalled(); - }); -}); + pollingService.stop() + expect(mockBlockRepository.stop).toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/application/polling/pollingService.ts b/apps/api/src/application/polling/pollingService.ts index fcd1666..864b028 100644 --- a/apps/api/src/application/polling/pollingService.ts +++ b/apps/api/src/application/polling/pollingService.ts @@ -1,19 +1,23 @@ -import { WebSocketServer } from "ws"; -import { BlockRepository } from "../../domain/repositories/BlockRepository"; -import BroadcastToClients from "../helpers/BroadcastToClients"; -import { TxTypesEnum } from "@cryptochords/shared"; -import { L2Block } from "@cryptochords/shared"; +import { WebSocketServer } from 'ws' +import { BlockRepository } from '../../domain/repositories/BlockRepository' +import broadcastToClients from '../helpers/broadcastToClients' +import { TxTypesEnum } from '@cryptochords/shared' +import { L2Block } from '@cryptochords/shared' export class PollingService { constructor(private blockRepository: BlockRepository) {} execute(wss: WebSocketServer, url: string): void { this.blockRepository.execute(url) - this.blockRepository.on(TxTypesEnum.Block, (l2Block: L2Block) => BroadcastToClients(wss, l2Block)); - this.blockRepository.on(TxTypesEnum.Eth, (l2Block: L2Block) => BroadcastToClients(wss, l2Block)); + this.blockRepository.on(TxTypesEnum.Block, (l2Block: L2Block) => + broadcastToClients(wss, l2Block), + ) + this.blockRepository.on(TxTypesEnum.Eth, (l2Block: L2Block) => + broadcastToClients(wss, l2Block), + ) } stop() { this.blockRepository.stop() } -} \ No newline at end of file +} diff --git a/apps/api/src/domain/base/Entity.spec.ts b/apps/api/src/domain/base/Entity.spec.ts index b572ccc..e1c1caa 100644 --- a/apps/api/src/domain/base/Entity.spec.ts +++ b/apps/api/src/domain/base/Entity.spec.ts @@ -22,7 +22,7 @@ describe('src/domain/base/Entity', () => { }) it('should be an instance of ValueObject', () => { - const entity = TestEntity.create({ value: 'test'}) + const entity = TestEntity.create({ value: 'test' }) expect(entity).toBeInstanceOf(ValueObject) }) diff --git a/apps/api/src/domain/enums/BlockTypesEnum.spec.ts b/apps/api/src/domain/enums/BlockTypesEnum.spec.ts index 68d7a92..23bcbb5 100644 --- a/apps/api/src/domain/enums/BlockTypesEnum.spec.ts +++ b/apps/api/src/domain/enums/BlockTypesEnum.spec.ts @@ -1,16 +1,16 @@ -import { describe, it, expect } from 'vitest'; -import { BlockTypesEnum } from './BlockTypesEnum'; +import { describe, it, expect } from 'vitest' +import { BlockTypesEnum } from './BlockTypesEnum' describe('BlockTypesEnum', () => { it('should have a LEGACY type with value "0"', () => { - expect(BlockTypesEnum.LEGACY).toBe('0'); - }); + expect(BlockTypesEnum.LEGACY).toBe('0') + }) it('should have an EIP2930 type with value "1"', () => { - expect(BlockTypesEnum.EIP2930).toBe('1'); - }); + expect(BlockTypesEnum.EIP2930).toBe('1') + }) it('should have an EIP1559 type with value "2"', () => { - expect(BlockTypesEnum.EIP1559).toBe('2'); - }); -}); + expect(BlockTypesEnum.EIP1559).toBe('2') + }) +}) diff --git a/apps/api/src/domain/repositories/BlockRepository.ts b/apps/api/src/domain/repositories/BlockRepository.ts index 019cb6a..816c9ac 100644 --- a/apps/api/src/domain/repositories/BlockRepository.ts +++ b/apps/api/src/domain/repositories/BlockRepository.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from 'events'; +import { EventEmitter } from 'events' export interface BlockRepository extends EventEmitter { execute(url: string): void diff --git a/apps/api/src/infrastructure/repositories/blockPolling.spec.ts b/apps/api/src/infrastructure/repositories/blockPolling.spec.ts index ba45d8f..44340f0 100644 --- a/apps/api/src/infrastructure/repositories/blockPolling.spec.ts +++ b/apps/api/src/infrastructure/repositories/blockPolling.spec.ts @@ -1,9 +1,15 @@ -import Web3 from 'web3'; -import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest'; -import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum'; -import { BlockPollingRepository } from './blockPolling'; +import Web3 from 'web3' +import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest' +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum' +import { BlockPollingRepository } from './blockPolling' vi.mock('@cryptochords/shared', () => ({ + Address: { + create: vi.fn().mockReturnValue({}), + }, + L2Block: { + create: vi.fn().mockReturnValue({}), + }, TxType: { create: vi.fn().mockReturnValue({}), }, @@ -11,59 +17,60 @@ vi.mock('@cryptochords/shared', () => ({ Block: 'Block', Eth: 'Eth', }, - Address: { - create: vi.fn().mockReturnValue({}), - }, - L2Block: { - create: vi.fn().mockReturnValue({}), - }, -})); +})) describe('BlockPollingRepository', () => { - let blockPollingRepository: BlockPollingRepository; - let mockWeb3: Web3; + let blockPollingRepository: BlockPollingRepository + let mockWeb3: Web3 beforeEach(() => { - blockPollingRepository = new BlockPollingRepository(); - mockWeb3 = new Web3(); - mockWeb3.eth.getBlockNumber = vi.fn().mockResolvedValue(100); + blockPollingRepository = new BlockPollingRepository() + mockWeb3 = new Web3() + mockWeb3.eth.getBlockNumber = vi.fn().mockResolvedValue(100) mockWeb3.eth.getBlock = vi.fn().mockResolvedValue({ hash: '0xhash', transactions: [], - }); - vi.useFakeTimers(); + }) + vi.useFakeTimers() - vi.spyOn(blockPollingRepository, 'emit'); - }); + vi.spyOn(blockPollingRepository, 'emit') + }) afterEach(() => { - vi.runOnlyPendingTimers(); - vi.useRealTimers(); - vi.clearAllMocks(); - }); + vi.runOnlyPendingTimers() + vi.useRealTimers() + vi.clearAllMocks() + }) it('should start and stop polling correctly', () => { - blockPollingRepository.execute('http://localhost:8545', 1000); - expect(vi.getTimerCount()).toBe(1); - blockPollingRepository.stop(); - expect(vi.getTimerCount()).toBe(0); - }); + blockPollingRepository.execute('http://localhost:8545', 1000) + expect(vi.getTimerCount()).toBe(1) + blockPollingRepository.stop() + expect(vi.getTimerCount()).toBe(0) + }) it('should emit "Block" event for new blocks with no transactions', async () => { - await blockPollingRepository.checkNewBlocks(mockWeb3); - expect(blockPollingRepository.emit).toHaveBeenCalledWith('Block', expect.anything()); - }); + await blockPollingRepository.checkNewBlocks(mockWeb3) + expect(blockPollingRepository.emit).toHaveBeenCalledWith( + 'Block', + expect.anything(), + ) + }) it('should emit "Block" and "Eth" events for new blocks with EIP1559 transactions', async () => { mockWeb3.eth.getBlock = vi.fn().mockResolvedValue({ hash: '0xnewhash', - transactions: [ - { type: BlockTypesEnum.EIP1559, from: '0xSomeAddress' }, - ], - }); + transactions: [{ from: '0xSomeAddress', type: BlockTypesEnum.EIP1559 }], + }) - await blockPollingRepository.checkNewBlocks(mockWeb3); - expect(blockPollingRepository.emit).toHaveBeenCalledWith('Block', expect.anything()); - expect(blockPollingRepository.emit).toHaveBeenCalledWith('Eth', expect.anything()); - }); -}); + await blockPollingRepository.checkNewBlocks(mockWeb3) + expect(blockPollingRepository.emit).toHaveBeenCalledWith( + 'Block', + expect.anything(), + ) + expect(blockPollingRepository.emit).toHaveBeenCalledWith( + 'Eth', + expect.anything(), + ) + }) +}) diff --git a/apps/api/src/infrastructure/repositories/blockPolling.ts b/apps/api/src/infrastructure/repositories/blockPolling.ts index 60f144e..20c8ad0 100644 --- a/apps/api/src/infrastructure/repositories/blockPolling.ts +++ b/apps/api/src/infrastructure/repositories/blockPolling.ts @@ -1,61 +1,69 @@ -import { EventEmitter } from "stream"; -import { BlockRepository } from "../../domain/repositories/BlockRepository"; -import Web3 from "web3"; -import { TxTypesEnum } from "@cryptochords/shared"; -import { TxType } from "@cryptochords/shared"; -import { Address } from "@cryptochords/shared"; -import { BlockTypesEnum } from "../../domain/enums/BlockTypesEnum"; -import { L2Block } from "@cryptochords/shared"; - -export class BlockPollingRepository extends EventEmitter implements BlockRepository { +import { EventEmitter } from 'stream' +import { BlockRepository } from '../../domain/repositories/BlockRepository' +import Web3 from 'web3' +import { TxTypesEnum } from '@cryptochords/shared' +import { TxType } from '@cryptochords/shared' +import { Address } from '@cryptochords/shared' +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum' +import { L2Block } from '@cryptochords/shared' + +export class BlockPollingRepository + extends EventEmitter + implements BlockRepository +{ private latestBlockNumber = BigInt(0) - private pollingIntervalId: NodeJS.Timeout | null = null; + private pollingIntervalId: NodeJS.Timeout | null = null execute(rpcUrl: string, pollingInterval = 5000): void { - const web3 = new Web3(rpcUrl); + const web3 = new Web3(rpcUrl) this.poll(web3, pollingInterval) } stop() { if (this.pollingIntervalId) { - clearInterval(this.pollingIntervalId); - this.pollingIntervalId = null; + clearInterval(this.pollingIntervalId) + this.pollingIntervalId = null } - console.log('Polling stopped.'); } async checkNewBlocks(web3: Web3): Promise { - console.log('Checking for new block'); - const currentBlockNumber = await web3.eth.getBlockNumber(); + const currentBlockNumber = await web3.eth.getBlockNumber() if (currentBlockNumber > this.latestBlockNumber) { - this.latestBlockNumber = currentBlockNumber; + this.latestBlockNumber = currentBlockNumber - const block = await web3.eth.getBlock(currentBlockNumber, true); + const block = await web3.eth.getBlock(currentBlockNumber, true) if (block) { - console.log('New block header:', block.hash); - this.emit(TxTypesEnum.Block, L2Block.create({ - txType: TxType.create(TxTypesEnum.Block), - address: Address.create(block.hash ? block.hash.toString() : '') - })); + this.emit( + TxTypesEnum.Block, + L2Block.create({ + address: Address.create(block.hash ? block.hash.toString() : ''), + txType: TxType.create(TxTypesEnum.Block), + }), + ) if (block.transactions) { // eslint-disable-next-line @typescript-eslint/no-explicit-any block.transactions.forEach((tx: any) => { if (tx.type.toString() === BlockTypesEnum.EIP1559) { - console.log('New eth tx from', tx.from); - this.emit(TxTypesEnum.Eth, L2Block.create({ - txType: TxType.create(TxTypesEnum.Eth), - address: Address.create(tx.from) - })); + this.emit( + TxTypesEnum.Eth, + L2Block.create({ + address: Address.create(tx.from), + txType: TxType.create(TxTypesEnum.Eth), + }), + ) } - }); + }) } } } } poll(web3: Web3, pollingInterval: number) { - this.pollingIntervalId = setInterval(() => this.checkNewBlocks(web3), pollingInterval); + this.pollingIntervalId = setInterval( + () => this.checkNewBlocks(web3), + pollingInterval, + ) } } diff --git a/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts b/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts index 22fdd12..11db960 100644 --- a/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts +++ b/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts @@ -1,28 +1,36 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum'; -import { BlockWebsocketRepository } from './blockWebsocket'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum' +import { BlockWebsocketRepository } from './blockWebsocket' vi.mock('web3', () => ({ __esModule: true, default: vi.fn().mockImplementation(() => ({ eth: { - subscribe: vi.fn().mockImplementation(() => Promise.resolve({ - on: vi.fn((event, callback) => { - if (event === 'data') { - const fakeBlockHeader = { hash: 'fakeHash' }; - callback(fakeBlockHeader); - } - }), - unsubscribe: vi.fn(), - })), getBlock: vi.fn().mockResolvedValue({ - transactions: [{ type: BlockTypesEnum.EIP1559, from: 'fakeAddress' }], + transactions: [{ from: 'fakeAddress', type: BlockTypesEnum.EIP1559 }], }), + subscribe: vi.fn().mockImplementation(() => + Promise.resolve({ + on: vi.fn((event, callback) => { + if (event === 'data') { + const fakeBlockHeader = { hash: 'fakeHash' } + callback(fakeBlockHeader) + } + }), + unsubscribe: vi.fn(), + }), + ), }, })), -})); +})) vi.mock('@cryptochords/shared', () => ({ + Address: { + create: vi.fn().mockReturnValue({}), + }, + L2Block: { + create: vi.fn().mockReturnValue({}), + }, TxType: { create: vi.fn().mockReturnValue({}), }, @@ -30,50 +38,54 @@ vi.mock('@cryptochords/shared', () => ({ Block: 'Block', Eth: 'Eth', }, - Address: { - create: vi.fn().mockReturnValue({}), - }, - L2Block: { - create: vi.fn().mockReturnValue({}), - }, -})); +})) describe('BlockWebsocketRepository', () => { - let blockWebsocketRepository: BlockWebsocketRepository; + let blockWebsocketRepository: BlockWebsocketRepository beforeEach(() => { - blockWebsocketRepository = new BlockWebsocketRepository(); - }); + blockWebsocketRepository = new BlockWebsocketRepository() + }) afterEach(() => { - vi.clearAllMocks(); - }); + vi.clearAllMocks() + }) it('should initialize and subscribe to new block headers', async () => { - blockWebsocketRepository.execute('fakeWebsocketUrl'); + blockWebsocketRepository.execute('fakeWebsocketUrl') await vi.waitFor(() => { - expect(blockWebsocketRepository['subscription']).toBeDefined(); - expect(blockWebsocketRepository['web3']).toBeDefined(); - expect(blockWebsocketRepository['subscription'].on).toHaveBeenCalledWith('data', expect.any(Function)); - }); - }); + expect(blockWebsocketRepository['subscription']).toBeDefined() + expect(blockWebsocketRepository['web3']).toBeDefined() + expect(blockWebsocketRepository['subscription'].on).toHaveBeenCalledWith( + 'data', + expect.any(Function), + ) + }) + }) it('should stop subscription', async () => { - blockWebsocketRepository.execute('fakeWebsocketUrl'); - await vi.waitFor(() => expect(blockWebsocketRepository['subscription']).toBeDefined()); + blockWebsocketRepository.execute('fakeWebsocketUrl') + await vi.waitFor(() => + expect(blockWebsocketRepository['subscription']).toBeDefined(), + ) - blockWebsocketRepository.stop(); - expect(blockWebsocketRepository['subscription']).toBeNull(); - }); + blockWebsocketRepository.stop() + expect(blockWebsocketRepository['subscription']).toBeNull() + }) it('should emit events for new block headers and transactions', async () => { - const emitSpy = vi.spyOn(blockWebsocketRepository, 'emit'); - blockWebsocketRepository.execute('fakeWebsocketUrl'); + const emitSpy = vi.spyOn(blockWebsocketRepository, 'emit') + blockWebsocketRepository.execute('fakeWebsocketUrl') - await vi.waitFor(() => expect(emitSpy).toHaveBeenCalledWith('Block', expect.anything())); - await vi.waitFor(() => expect(emitSpy).toHaveBeenCalledWith('Eth', expect.anything()), { timeout: 500 }); + await vi.waitFor(() => + expect(emitSpy).toHaveBeenCalledWith('Block', expect.anything()), + ) + await vi.waitFor( + () => expect(emitSpy).toHaveBeenCalledWith('Eth', expect.anything()), + { timeout: 500 }, + ) - expect(emitSpy).toHaveBeenNthCalledWith(1, 'Block', expect.anything()); - expect(emitSpy).toHaveBeenNthCalledWith(2, 'Eth', expect.anything()); - }); -}); + expect(emitSpy).toHaveBeenNthCalledWith(1, 'Block', expect.anything()) + expect(emitSpy).toHaveBeenNthCalledWith(2, 'Eth', expect.anything()) + }) +}) diff --git a/apps/api/src/infrastructure/repositories/blockWebsocket.ts b/apps/api/src/infrastructure/repositories/blockWebsocket.ts index 310d690..24ed6d3 100644 --- a/apps/api/src/infrastructure/repositories/blockWebsocket.ts +++ b/apps/api/src/infrastructure/repositories/blockWebsocket.ts @@ -1,60 +1,71 @@ -import Web3, { BlockHeaderOutput } from 'web3'; -import { EventEmitter } from 'events'; -import { TxType } from '@cryptochords/shared'; -import { TxTypesEnum } from '@cryptochords/shared'; -import { Address } from '@cryptochords/shared'; -import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum'; -import { BlockRepository } from '../../domain/repositories/BlockRepository'; -import { L2Block } from '@cryptochords/shared'; +import Web3, { BlockHeaderOutput } from 'web3' +import { EventEmitter } from 'events' +import { TxType } from '@cryptochords/shared' +import { TxTypesEnum } from '@cryptochords/shared' +import { Address } from '@cryptochords/shared' +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum' +import { BlockRepository } from '../../domain/repositories/BlockRepository' +import { L2Block } from '@cryptochords/shared' -export class BlockWebsocketRepository extends EventEmitter implements BlockRepository { - private web3: Web3 | null = null; +export class BlockWebsocketRepository + extends EventEmitter + implements BlockRepository +{ + private web3: Web3 | null = null // eslint-disable-next-line @typescript-eslint/no-explicit-any - private subscription: any | null = null; + private subscription: any | null = null execute(websocketUrl: string): void { - this.web3 = new Web3(websocketUrl); - this.subscribeToNewBlockHeaders(); + this.web3 = new Web3(websocketUrl) + this.subscribeToNewBlockHeaders() } stop(): void { if (this.subscription) { - this.subscription.unsubscribe(); - this.subscription = null; + this.subscription.unsubscribe() + this.subscription = null } } private async subscribeToNewBlockHeaders(): Promise { if (!this.web3) { - console.error('Web3 is not initialized.'); - return; + return } - this.subscription = await this.web3?.eth.subscribe('newBlockHeaders'); + this.subscription = await this.web3?.eth.subscribe('newBlockHeaders') this.subscription.on('data', async (blockHeader: BlockHeaderOutput) => { - await this.handleNewBlockHeader(this.web3, blockHeader); - }); + await this.handleNewBlockHeader(this.web3, blockHeader) + }) } - private async handleNewBlockHeader(web3: Web3 | null, blockHeader: BlockHeaderOutput): Promise { - console.log('New block header:', blockHeader.hash); - this.emit(TxTypesEnum.Block, L2Block.create({ - txType: TxType.create(TxTypesEnum.Block), - address: Address.create(blockHeader.hash ? blockHeader.hash.toString() : '') - })); + private async handleNewBlockHeader( + web3: Web3 | null, + blockHeader: BlockHeaderOutput, + ): Promise { + this.emit( + TxTypesEnum.Block, + L2Block.create({ + address: Address.create( + blockHeader.hash ? blockHeader.hash.toString() : '', + ), + txType: TxType.create(TxTypesEnum.Block), + }), + ) - const block = await web3?.eth.getBlock(blockHeader.hash, true); + const block = await web3?.eth.getBlock(blockHeader.hash, true) if (block && block.transactions) { // eslint-disable-next-line @typescript-eslint/no-explicit-any block.transactions.forEach((tx: any) => { if (tx.type.toString() === BlockTypesEnum.EIP1559) { - console.log('New eth tx from', tx.from); - this.emit(TxTypesEnum.Eth, L2Block.create({ - txType: TxType.create(TxTypesEnum.Eth), - address: Address.create(tx.from) - })); + this.emit( + TxTypesEnum.Eth, + L2Block.create({ + address: Address.create(tx.from), + txType: TxType.create(TxTypesEnum.Eth), + }), + ) } - }); + }) } } } diff --git a/apps/api/src/presentation/ExpressServer.spec.ts b/apps/api/src/presentation/ExpressServer.spec.ts index 167d45e..3ac891f 100644 --- a/apps/api/src/presentation/ExpressServer.spec.ts +++ b/apps/api/src/presentation/ExpressServer.spec.ts @@ -1,69 +1,76 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import { ExpressServer } from './ExpressServer'; -import { PollingRoute } from './routes/PollingRoute'; -import express from 'express'; -import { HemiTestnet } from '@cryptochords/shared'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' +import { ExpressServer } from './ExpressServer' +import { PollingRoute } from './routes/PollingRoute' +import express from 'express' +import { HemiTestnet } from '@cryptochords/shared' vi.mock('express', () => { const listenMock = vi.fn((_port, callback) => { - callback(); + callback() return { - close: vi.fn((cb) => cb && cb()), - }; - }); + close: vi.fn(cb => cb && cb()), + } + }) return { __esModule: true, default: vi.fn(() => ({ listen: listenMock, })), - }; -}); + } +}) vi.mock('ws', () => ({ Server: vi.fn().mockImplementation(() => ({ on: vi.fn(), })), -})); +})) vi.mock('./routes/PollingRoute', () => ({ PollingRoute: vi.fn().mockImplementation(() => ({ initialize: vi.fn(), stop: vi.fn(), })), -})); +})) describe('ExpressServer', () => { - let expressServer: ExpressServer; - let pollingRouteMock: PollingRoute; + let expressServer: ExpressServer + let pollingRouteMock: PollingRoute - const MOCK_PORT = 3000; - const USE_WEBSOCKET = true; - const MOCK_WEBSOCKET_URL = HemiTestnet.rpcUrls.default.webSocket[0]; - const MOCK_RPC_URL = HemiTestnet.rpcUrls.default.http[0]; + const MOCK_PORT = 3000 + const USE_WEBSOCKET = true + const MOCK_WEBSOCKET_URL = HemiTestnet.rpcUrls.default.webSocket[0] + const MOCK_RPC_URL = HemiTestnet.rpcUrls.default.http[0] beforeEach(() => { - vi.clearAllMocks(); + vi.clearAllMocks() - pollingRouteMock = new PollingRoute(USE_WEBSOCKET, MOCK_WEBSOCKET_URL, MOCK_RPC_URL); - expressServer = new ExpressServer(pollingRouteMock, MOCK_PORT); - }); + pollingRouteMock = new PollingRoute( + USE_WEBSOCKET, + MOCK_WEBSOCKET_URL, + MOCK_RPC_URL, + ) + expressServer = new ExpressServer(pollingRouteMock, MOCK_PORT) + }) afterEach(() => { - vi.clearAllMocks(); - }); + vi.clearAllMocks() + }) it('should start the server correctly', () => { - expressServer.start(); + expressServer.start() - const mockedExpress = express() as any; - expect(mockedExpress.listen).toHaveBeenCalledWith(MOCK_PORT, expect.any(Function)); - expect(pollingRouteMock.initialize).toHaveBeenCalled(); - }); + const mockedExpress = express() + expect(mockedExpress.listen).toHaveBeenCalledWith( + MOCK_PORT, + expect.any(Function), + ) + expect(pollingRouteMock.initialize).toHaveBeenCalled() + }) it('should stop the server correctly', async () => { - expressServer.start(); - await expressServer.stop(); + expressServer.start() + await expressServer.stop() - expect(pollingRouteMock.stop).toHaveBeenCalled(); - }); -}); + expect(pollingRouteMock.stop).toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/presentation/ExpressServer.ts b/apps/api/src/presentation/ExpressServer.ts index 9d49801..f95b2a4 100644 --- a/apps/api/src/presentation/ExpressServer.ts +++ b/apps/api/src/presentation/ExpressServer.ts @@ -1,50 +1,51 @@ -import express, { Express } from 'express'; -import { Server as HTTPServer } from 'http'; -import { Server as WebSocketServer } from 'ws'; -import 'dotenv/config'; -import { PollingRoute } from './routes/PollingRoute'; +import express, { Express } from 'express' +import { Server as HTTPServer } from 'http' +import { Server as WebSocketServer } from 'ws' +import 'dotenv/config' +import { PollingRoute } from './routes/PollingRoute' export class ExpressServer { - private pollingRoute: PollingRoute; - private api: Express; - private httpServer: HTTPServer | null = null; - private wss: WebSocketServer | null = null; - private port: string | number; + private pollingRoute: PollingRoute + private api: Express + private httpServer: HTTPServer | null = null + private wss: WebSocketServer | null = null + private port: string | number constructor(pollingRoute: PollingRoute, port: number) { - this.pollingRoute = pollingRoute; - this.api = express(); - this.port = port || 3000; + this.pollingRoute = pollingRoute + this.api = express() + this.port = port || 3000 } public start(): void { this.httpServer = this.api.listen(this.port, () => { - console.log(`Express server running on port ${this.port}`); - }); + console.log(`Express server running on port ${this.port}`) + }) - this.wss = new WebSocketServer({ server: this.httpServer }); + this.wss = new WebSocketServer({ server: this.httpServer }) - this.wss.on('connection', (ws) => { - console.log('WebSocket client connected'); + this.wss.on('connection', ws => { + console.log('WebSocket client connected') ws.on('message', (message: string) => { - console.log('Message received:', message); - }); - }); + console.log('Message received:', message) + }) + }) - this.pollingRoute.initialize(this.wss); + this.pollingRoute.initialize(this.wss) } async stop(): Promise { console.info('CryptoChords API | Closing HTTP Server') - return await new Promise((resolve) => { - this.httpServer?.close((error) => { + return await new Promise(resolve => { + this.httpServer?.close(error => { if (error) { console.error( - `CryptoChords API | Error Closing HTTP Server: ${error.message}`) + `CryptoChords API | Error Closing HTTP Server: ${error.message}`, + ) } else { - this.wss = null; - console.info('CryptoChords API | WSS Server successfully closed'); + this.wss = null + console.info('CryptoChords API | WSS Server successfully closed') } this.pollingRoute.stop() resolve() diff --git a/apps/api/src/presentation/routes/PollingRoute.spec.ts b/apps/api/src/presentation/routes/PollingRoute.spec.ts index 740b668..4cc7063 100644 --- a/apps/api/src/presentation/routes/PollingRoute.spec.ts +++ b/apps/api/src/presentation/routes/PollingRoute.spec.ts @@ -1,7 +1,7 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { WebSocketServer } from 'ws'; -import { PollingRoute } from './PollingRoute'; -import { HemiTestnet } from '@cryptochords/shared'; +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { WebSocketServer } from 'ws' +import { PollingRoute } from './PollingRoute' +import { HemiTestnet } from '@cryptochords/shared' vi.mock('../../application/polling/pollingService', () => { return { @@ -9,41 +9,52 @@ vi.mock('../../application/polling/pollingService', () => { execute: vi.fn(), stop: vi.fn(), })), - }; -}); + } +}) -vi.mock('ws'); +vi.mock('ws') vi.mock('../../infrastructure/repositories/blockWebsocket', () => ({ BlockWebsocketRepository: vi.fn(), -})); +})) vi.mock('../../infrastructure/repositories/blockPolling', () => ({ BlockPollingRepository: vi.fn(), -})); +})) describe('PollingRoute', () => { - let wss: WebSocketServer; + let wss: WebSocketServer - const MOCK_WEBSOCKET_URL = HemiTestnet.rpcUrls.default.webSocket[0]; - const MOCK_RPC_URL = HemiTestnet.rpcUrls.default.http[0]; + const MOCK_WEBSOCKET_URL = HemiTestnet.rpcUrls.default.webSocket[0] + const MOCK_RPC_URL = HemiTestnet.rpcUrls.default.http[0] beforeEach(() => { - vi.clearAllMocks(); - wss = new WebSocketServer({ noServer: true }); - }); + vi.clearAllMocks() + wss = new WebSocketServer({ noServer: true }) + }) it('should initialize with WebSocket when useWebsocket is true', () => { - const pollingRoute = new PollingRoute(true, MOCK_WEBSOCKET_URL, MOCK_RPC_URL); - pollingRoute.initialize(wss); - - const mockPollingService = pollingRoute['pollingService'] as any; - expect(mockPollingService.execute).toHaveBeenCalledWith(wss, MOCK_WEBSOCKET_URL); - }); + const pollingRoute = new PollingRoute( + true, + MOCK_WEBSOCKET_URL, + MOCK_RPC_URL, + ) + pollingRoute.initialize(wss) + + const mockPollingService = pollingRoute['pollingService'] + expect(mockPollingService.execute).toHaveBeenCalledWith( + wss, + MOCK_WEBSOCKET_URL, + ) + }) it('should initialize with Polling when useWebsocket is false', () => { - const pollingRoute = new PollingRoute(false, MOCK_WEBSOCKET_URL, MOCK_RPC_URL); - pollingRoute.initialize(wss); - - const mockPollingService = pollingRoute['pollingService'] as any; - expect(mockPollingService.execute).toHaveBeenCalledWith(wss, MOCK_RPC_URL); - }); -}); + const pollingRoute = new PollingRoute( + false, + MOCK_WEBSOCKET_URL, + MOCK_RPC_URL, + ) + pollingRoute.initialize(wss) + + const mockPollingService = pollingRoute['pollingService'] + expect(mockPollingService.execute).toHaveBeenCalledWith(wss, MOCK_RPC_URL) + }) +}) diff --git a/apps/api/src/presentation/routes/PollingRoute.ts b/apps/api/src/presentation/routes/PollingRoute.ts index d605576..417e15e 100644 --- a/apps/api/src/presentation/routes/PollingRoute.ts +++ b/apps/api/src/presentation/routes/PollingRoute.ts @@ -1,30 +1,30 @@ -import { WebSocketServer } from 'ws'; -import { BlockWebsocketRepository } from '../../infrastructure/repositories/blockWebsocket'; -import { BlockPollingRepository } from '../../infrastructure/repositories/blockPolling'; -import { PollingService } from '../../application/polling/pollingService'; -import 'dotenv/config'; +import { WebSocketServer } from 'ws' +import { BlockWebsocketRepository } from '../../infrastructure/repositories/blockWebsocket' +import { BlockPollingRepository } from '../../infrastructure/repositories/blockPolling' +import { PollingService } from '../../application/polling/pollingService' +import 'dotenv/config' export class PollingRoute { - private pollingService: PollingService; - private url: string; + private pollingService: PollingService + private url: string constructor(useWebsocket: boolean, websocketUrl: string, rpcUrl: string) { if (useWebsocket) { - this.url = websocketUrl; - const blockWebsocketRepository = new BlockWebsocketRepository(); - this.pollingService = new PollingService(blockWebsocketRepository); + this.url = websocketUrl + const blockWebsocketRepository = new BlockWebsocketRepository() + this.pollingService = new PollingService(blockWebsocketRepository) } else { - this.url = rpcUrl; - const blockPollingRepository = new BlockPollingRepository(); - this.pollingService = new PollingService(blockPollingRepository); + this.url = rpcUrl + const blockPollingRepository = new BlockPollingRepository() + this.pollingService = new PollingService(blockPollingRepository) } } public initialize(wss: WebSocketServer): void { - this.pollingService.execute(wss, this.url); + this.pollingService.execute(wss, this.url) } public stop(): void { - this.pollingService.stop(); - } + this.pollingService.stop() + } } diff --git a/apps/api/src/server.spec.ts b/apps/api/src/server.spec.ts index 9d29e74..4f1dacb 100644 --- a/apps/api/src/server.spec.ts +++ b/apps/api/src/server.spec.ts @@ -1,40 +1,49 @@ -import { describe, it, expect, beforeAll, afterAll, vi, beforeEach, afterEach } from 'vitest'; -import { ExpressServer } from './presentation/ExpressServer'; - -vi.mock('./presentation/ExpressServer'); +import { + describe, + it, + expect, + beforeAll, + afterAll, + vi, + beforeEach, + afterEach, +} from 'vitest' +import { ExpressServer } from './presentation/ExpressServer' + +vi.mock('./presentation/ExpressServer') describe('server', () => { beforeEach(() => { vi.spyOn(process, 'exit').mockImplementation(() => { - return undefined as never; - }); - }); + return undefined as never + }) + }) afterEach(() => { - vi.restoreAllMocks(); - }); + vi.restoreAllMocks() + }) beforeAll(() => { - ExpressServer.prototype.start = vi.fn(); - ExpressServer.prototype.stop = vi.fn(); - }); + ExpressServer.prototype.start = vi.fn() + ExpressServer.prototype.stop = vi.fn() + }) afterAll(() => { - vi.restoreAllMocks(); - }); + vi.restoreAllMocks() + }) it('should start the server', async () => { - await import('./server'); - expect(ExpressServer.prototype.start).toHaveBeenCalled(); - }); + await import('./server') + expect(ExpressServer.prototype.start).toHaveBeenCalled() + }) it('should stop the server on SIGTERM', async () => { - process.emit('SIGTERM'); - expect(ExpressServer.prototype.stop).toHaveBeenCalled(); - }); + process.emit('SIGTERM') + expect(ExpressServer.prototype.stop).toHaveBeenCalled() + }) it('should stop the server on SIGINT', async () => { - process.emit('SIGINT'); - expect(ExpressServer.prototype.stop).toHaveBeenCalled(); - }); -}); + process.emit('SIGINT') + expect(ExpressServer.prototype.stop).toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index ebfc250..4e46305 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -1,19 +1,27 @@ -import 'dotenv/config'; -import { ExpressServer } from './presentation/ExpressServer'; -import { PollingRoute } from './presentation/routes/PollingRoute'; -import { HemiTestnet, HemiMainnet } from "@cryptochords/shared"; +import 'dotenv/config' +import { ExpressServer } from './presentation/ExpressServer' +import { PollingRoute } from './presentation/routes/PollingRoute' +import { HemiTestnet, HemiMainnet } from '@cryptochords/shared' const useWebsocket = process.env['USE_WEBSOCKET_NODE_L2'] === 'true' const useMainnet = process.env['ENABLE_MAINNET'] === 'true' // Testnet const websocketTestnet = HemiTestnet.rpcUrls.default.webSocket[0] const rpcTestnet = HemiTestnet.rpcUrls.default.http[0] -const pollingRouteTestnet = new PollingRoute(useWebsocket, websocketTestnet, rpcTestnet) +const pollingRouteTestnet = new PollingRoute( + useWebsocket, + websocketTestnet, + rpcTestnet, +) const serverTestnet = new ExpressServer(pollingRouteTestnet, 3000) // Mainnet const websocketMainnet = HemiMainnet.rpcUrls.default.webSocket[0] const rpcMainnet = HemiMainnet.rpcUrls.default.http[0] -const pollingRouteMainnet = new PollingRoute(useWebsocket, websocketMainnet, rpcMainnet) +const pollingRouteMainnet = new PollingRoute( + useWebsocket, + websocketMainnet, + rpcMainnet, +) const serverMainnet = new ExpressServer(pollingRouteMainnet, 3001) const startServer = async (): Promise => { @@ -32,12 +40,12 @@ const stopServer = async (): Promise => { process.on('SIGTERM', async () => { await stopServer() - process.exit(0); + process.exit(0) }) process.on('SIGINT', async () => { await stopServer() - process.exit(0); + process.exit(0) }) startServer() diff --git a/apps/api/tsconfig.build.json b/apps/api/tsconfig.build.json index e702d94..5417c7d 100644 --- a/apps/api/tsconfig.build.json +++ b/apps/api/tsconfig.build.json @@ -1,6 +1,4 @@ { - "extends": "./tsconfig.json", - "exclude": [ - "src/**/*.spec.ts" - ] - } \ No newline at end of file + "extends": "./tsconfig.json", + "exclude": ["src/**/*.spec.ts"] +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index fc3ad0f..b7aadeb 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -1,5 +1,5 @@ { - "include": [ "src/**/*" ], + "include": ["src/**/*"], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ @@ -12,7 +12,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ @@ -26,9 +26,9 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "./src", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "module": "commonjs" /* Specify what module code is generated. */, + "rootDir": "./src" /* Specify the root folder within your source files. */, + "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ @@ -40,17 +40,17 @@ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ - "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationMap": true /* Create sourcemaps for d.ts files. */, // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + "sourceMap": true /* Create source map files for emitted JavaScript files. */, // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ @@ -72,33 +72,33 @@ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */, + "strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */, + "strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */, + "strictBindCallApply": true /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */, + "strictPropertyInitialization": true /* Check for class properties that are declared but not set in the constructor. */, + "noImplicitThis": true /* Enable error reporting when 'this' is given the type 'any'. */, + "useUnknownInCatchVariables": true /* Default catch clause variables as 'unknown' instead of 'any'. */, + "alwaysStrict": true /* Ensure 'use strict' is always emitted. */, + "noUnusedLocals": true /* Enable error reporting when local variables aren't read. */, + "noUnusedParameters": true /* Raise an error when a function parameter isn't read. */, + "exactOptionalPropertyTypes": true /* Interpret optional property types as written, rather than adding 'undefined'. */, + "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */, + "noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */, + "noUncheckedIndexedAccess": true /* Add 'undefined' to a type when accessed using an index. */, + "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an override modifier. */, + "noPropertyAccessFromIndexSignature": true /* Enforces using indexed accessors for keys declared using an indexed type. */, + "allowUnusedLabels": true /* Disable error reporting for unused labels. */, + "allowUnreachableCode": true /* Disable error reporting for unreachable code. */, /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ } } diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts index 1c0eacf..e21acf9 100644 --- a/apps/api/vitest.config.ts +++ b/apps/api/vitest.config.ts @@ -4,8 +4,8 @@ export default defineConfig({ test: { coverage: { // instanbul excludes interfaces from coverage - provider: 'istanbul', include: ['src/**/*.ts'], + provider: 'istanbul', }, }, -}) \ No newline at end of file +}) diff --git a/apps/api/webSocketTest.html b/apps/api/webSocketTest.html index 070df56..a3c7827 100644 --- a/apps/api/webSocketTest.html +++ b/apps/api/webSocketTest.html @@ -1,29 +1,29 @@ - + - - + + WebSocket Test - - + +

Last block: waiting...

Last eth tx: waiting...

Last piano note: waiting...

- +