├── .eslintignore
├── .eslintrc.json
├── .github
├── FUNDING.yml
└── dependabot.yml
├── .gitignore
├── LICENSE
├── README.md
├── index.ts
├── jest.config.js
├── media
└── blaze.png
├── package-lock.json
├── package.json
├── src
├── data
│ ├── interfaces
│ │ └── blaze.ts
│ └── usecases
│ │ ├── blaze-messages-socket.ts
│ │ └── blaze-socket.ts
├── domain
│ └── usecases
│ │ ├── connectionSocket.ts
│ │ └── socket.ts
├── infra
│ ├── app
│ │ └── socket.ts
│ └── blaze
│ │ └── blaze-url.ts
└── main
│ └── index.ts
├── tests
└── impl-blaze.test.ts
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | coverage
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "standard-with-typescript",
3 | "parserOptions": {
4 | "project": "./tsconfig.json"
5 | },
6 | "rules": {
7 | "@typescript-eslint/space-before-blocks": "off",
8 | "@typescript-eslint/strict-boolean-expressions": "off",
9 | "@typescript-eslint/no-misused-promises": "off",
10 | "@typescript-eslint/no-namespace": "off"
11 | }
12 | }
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: viniciusgdr
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: viniciuswadev
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['https://wa.me/558183064666']
14 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Vinicius Silva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Blaze - TS/JS Blaze API
3 | A websocket connection for Blaze games
4 |
5 |
6 | ## Install
7 |
8 | Stable version:
9 | ```
10 | npm i @viniciusgdr/Blaze
11 | ```
12 | Or use the edge version
13 | ```
14 | npm i github:viniciusgdr/Blaze
15 | ```
16 |
17 | ## Handling Events
18 | ```ts
19 | 'close': { code: number; reconnect: boolean; }
20 |
21 | 'crash.tick'
22 | 'double.tick'
23 | 'subscriptions': string[]
24 |
25 | // On enabled cacheIgnoreRepeatedEvents, the event will be sent only once. If you want to receive the event repeatedly, you can use: (or disable the cacheIgnoreRepeatedEvents)
26 | 'CB:crash.tick'
27 | 'CB:double.tick'
28 | ```
29 |
30 | Example:
31 | ```ts
32 | const socket = makeConnection({
33 | type: 'crash', // "crash" | "doubles" | "crash_2"
34 | })
35 | socket.ev.on('crash.tick', (msg) => {
36 | console.log(msg)
37 | })
38 | ```
39 | ## Notes
40 | You can set the your token of blaze (Optional)
41 | ```ts
42 | const socket = makeConnection({
43 | token: string
44 | })
45 | ```
46 |
47 | This option declared as "true" limits you from repeating the same event several times in the round. so sending only once.
48 | ```ts
49 | const socket = makeConnection({
50 | cacheIgnoreRepeatedEvents: false
51 | // the default is true
52 | })
53 | ```
54 | ## Licence
55 |
56 | [MIT](https://choosealicense.com/licenses/mit/)
57 |
58 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | export * from './src/main'
2 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: ['/tests'],
3 | collectCoverageFrom: [
4 | '/tests/**/**.ts',
5 | '!/tests/**'
6 | ],
7 | coverageDirectory: 'coverage',
8 | testEnvironment: 'node',
9 | transform: {
10 | '.+\\.ts$': 'ts-jest'
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/media/blaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/viniciusgdr/Blaze/7e67afd2987c190b047acbe3f4f7c4c74db55be5/media/blaze.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@viniciusgdr/blaze",
3 | "version": "3.0.1",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "sucrase-node src/main/server.ts",
8 | "test": "jest --passWithNoTests --silent --noStackTrace --runInBand",
9 | "test:verbose": "jest --passWithNoTests --runInBand",
10 | "test:unit": "npm test -- --watch -c jest-unit-config.js",
11 | "test:integration": "npm test -- --watch -c jest-integration-config.js",
12 | "test:staged": "npm test -- --findRelatedTests",
13 | "test:ci": "npm test -- --coverage",
14 | "install": "tsc"
15 | },
16 | "author": "viniciusgdr",
17 | "license": "MIT",
18 | "devDependencies": {
19 | "@types/jest": "^29.5.12",
20 | "@types/node": "^22.15.3",
21 | "@types/node-fetch": "^2.6.4",
22 | "@types/ws": "^8.5.10",
23 | "@typescript-eslint/eslint-plugin": "^5.52.0",
24 | "eslint": "^8.45.0",
25 | "eslint-config-standard-with-typescript": "^37.0.0",
26 | "eslint-plugin-import": "^2.25.2",
27 | "eslint-plugin-n": "^15.0.0",
28 | "eslint-plugin-promise": "^6.0.0",
29 | "jest": "^29.3.1",
30 | "jest-mock-extended": "^3.0.4",
31 | "prisma": "^5.0.0",
32 | "ts-jest": "^29.0.3",
33 | "typescript": "^5.4.5"
34 | },
35 | "dependencies": {
36 | "ws": "^8.14.2"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/data/interfaces/blaze.ts:
--------------------------------------------------------------------------------
1 | export declare interface BlazeEventMap {
2 | 'crash.tick': {
3 | id: string
4 | updated_at: string
5 | status: string
6 | crash_point: number | null
7 | // Only on crash_2
8 | is_bonus_round: boolean
9 | }
10 | // Only on crash_2
11 | 'crash.tick-bets': {
12 | id: string
13 | roomId: number
14 | total_eur_bet: number
15 | total_bets_placed: string
16 | total_eur_won: number
17 | bets: Bet[]
18 | }
19 | 'double.tick': {
20 | id: string
21 | color: string | null
22 | roll: string | null
23 | created_at: string
24 | updated_at: string
25 | status: 'rolling' | 'waiting' | 'complete'
26 | total_red_eur_bet: number
27 | total_red_bets_placed: number
28 | total_white_eur_bet: number
29 | total_white_bets_placed: number
30 | total_black_eur_bet: number
31 | total_black_bets_placed: number
32 | bets: Bet[]
33 | }
34 | 'chat.message': {
35 | id: string
36 | text: string
37 | available: boolean
38 | created_at: string
39 | user: {
40 | id: string
41 | username: string
42 | rank: string
43 | label: string | null
44 | level: number
45 | }
46 | }
47 | 'close': {
48 | code: number
49 | reconnect: boolean
50 | }
51 | 'subscriptions': string[]
52 | }
53 |
54 | export interface Bet {
55 | id: string
56 | cashed_out_at: number | null
57 | amount: number
58 | currency_type: string
59 | user: {
60 | id: string
61 | id_str: string
62 | username: string
63 | rank: string
64 | }
65 | win_amount: string
66 | status: 'win' | 'created'
67 | }
68 |
--------------------------------------------------------------------------------
/src/data/usecases/blaze-messages-socket.ts:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'events'
2 | import { type ConnectionSocket } from '../../domain/usecases/connectionSocket'
3 | import { type Socket } from '../../domain/usecases/socket'
4 | import { type BlazeEventMap } from '../interfaces/blaze'
5 |
6 | export class BlazeMessageSocket implements Socket {
7 | interval: NodeJS.Timeout | null = null
8 | private readonly ev: EventEmitter = new EventEmitter()
9 | constructor (
10 | private readonly socket: ConnectionSocket
11 | ) {}
12 |
13 | async connect (options: Socket.Options): Promise {
14 | await this.socket.connect(options)
15 | this.initPing(options.timeoutPing ?? 10000)
16 | this.initOpen(options.token)
17 | this.onMessage()
18 | this.initClose(options)
19 | }
20 |
21 | private initPing (timeoutPing: number): void {
22 | this.interval = setInterval(() => {
23 | this.socket.send('2')
24 | }, timeoutPing)
25 | }
26 |
27 | private onMessage (): void {
28 | this.socket.on('message', (data: any) => {
29 | const msg: string = data.toString()
30 | const regex = /^\d+\["data",\s*({.*})]$/
31 |
32 | const match = msg.match(regex)
33 | if (!match) {
34 | return
35 | }
36 |
37 | const { payload, id } = JSON.parse(match[1]) ?? {}
38 | if (!payload || !id) {
39 | return
40 | }
41 | void this.ev.emit(id, payload)
42 | })
43 | }
44 |
45 | private initClose (options: Socket.Options): void {
46 | this.socket.on('close', async (code: number) => {
47 | if (this.interval) {
48 | clearInterval(this.interval)
49 | }
50 | void this.socket.disconnect()
51 | if (options.reconnect) {
52 | await new Promise(resolve => setTimeout(resolve, 100))
53 | void this.connect(options)
54 | }
55 | void this.ev.emit('close', {
56 | code,
57 | reconnect: options.reconnect
58 | })
59 | })
60 | }
61 |
62 | private initOpen (token?: string): void {
63 | this.socket.on('open', () => {
64 | const subscriptions = []
65 | this.socket.send('420["cmd",{"id":"subscribe","payload":{"room":"chat_room_2"}}]')
66 | subscriptions.push('chat_room_2')
67 | void this.ev.emit('subscriptions', subscriptions)
68 | })
69 | }
70 |
71 | on(event: T, callback: (data: BlazeEventMap[T]) => void): void {
72 | this.ev.on(event, callback)
73 | }
74 |
75 | emit (event: string, data: any): void {
76 | this.socket.emit(event, data)
77 | }
78 |
79 | async disconnect (): Promise {
80 | await this.socket.disconnect()
81 | }
82 |
83 | async send (data: any): Promise {
84 | this.socket.send(data)
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/data/usecases/blaze-socket.ts:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'events'
2 | import { type ConnectionSocket } from '../../domain/usecases/connectionSocket'
3 | import { type Socket } from '../../domain/usecases/socket'
4 | import { type BlazeEventMap } from '../interfaces/blaze'
5 |
6 | export class BlazeSocket implements Socket {
7 | interval: NodeJS.Timeout | null = null
8 | private readonly ev: EventEmitter = new EventEmitter()
9 | private cache: Record | null = null
10 | constructor (
11 | private readonly socket: ConnectionSocket,
12 | cacheIgnoreRepeatedEvents: boolean = true
13 | ) {
14 | if (cacheIgnoreRepeatedEvents) {
15 | this.cache = {}
16 | }
17 | }
18 |
19 | async connect (options: Socket.Options): Promise {
20 | await this.socket.connect(options)
21 | this.initPing(options.timeoutPing ?? 10000)
22 | this.initOpen(options.type ?? 'crash', options.token)
23 | this.onMessage()
24 | this.initClose(options)
25 | }
26 |
27 | private initPing (timeoutPing: number): void {
28 | this.interval = setInterval(() => {
29 | this.socket.send('2')
30 | }, timeoutPing)
31 | }
32 |
33 | private onMessage (): void {
34 | this.socket.on('message', (data: any) => {
35 | const msg: string = data.toString()
36 | const regex = /^\d+\["data",\s*({.*})]$/
37 |
38 | const match = msg.match(regex)
39 | if (!match) {
40 | return
41 | }
42 |
43 | const { payload, id } = JSON.parse(match[1]) ?? {}
44 | if (!payload || !id || !payload.id || !payload.status) {
45 | return
46 | }
47 | if (this.cache !== null) {
48 | void this.ev.emit(`CB:${id as string}`, payload)
49 |
50 | const cache = this.cache[payload.id]
51 | if ((cache && cache !== payload.status) || !cache) {
52 | void this.ev.emit(id, payload)
53 | this.cache[payload.id] = payload.status
54 | return
55 | }
56 | this.cache[payload.id] = payload.status
57 | return
58 | }
59 | void this.ev.emit(id, payload)
60 | })
61 | }
62 |
63 | private initClose (options: Socket.Options): void {
64 | this.socket.on('close', async (code: number) => {
65 | if (this.interval) {
66 | clearInterval(this.interval)
67 | }
68 | void this.socket.disconnect()
69 | if (options.reconnect) {
70 | await new Promise(resolve => setTimeout(resolve, 100))
71 | void this.connect(options)
72 | }
73 | void this.ev.emit('close', {
74 | code,
75 | reconnect: options.reconnect
76 | })
77 | })
78 | }
79 |
80 | private initOpen (type: string, token?: string): void {
81 | this.socket.on('open', () => {
82 | const subscriptions = []
83 | const roomMap: Record = {
84 | crash: 'crash_room_4',
85 | doubles: 'double_room_1',
86 | crash_2: 'crash_room_1',
87 | crash_neymarjr: 'crash_room_3'
88 | }
89 |
90 | const room = roomMap[type]
91 | if (!room) {
92 | throw new Error('Missing type of socket')
93 | }
94 |
95 | this.socket.send(`420["cmd",{"id":"subscribe","payload":{"room":"${room}"}}]`)
96 | subscriptions.push(room)
97 | if (token) {
98 | this.socket.send(`423["cmd",{"id":"authenticate","payload":{"token":"${token}"}}]`)
99 | this.socket.send(`422["cmd",{"id":"authenticate","payload":{"token":"${token}"}}]`)
100 | this.socket.send(`420["cmd",{"id":"authenticate","payload":{"token":"${token}"}}]`)
101 | }
102 | void this.ev.emit('subscriptions', subscriptions)
103 | })
104 | }
105 |
106 | on(event: T, callback: (data: BlazeEventMap[T]) => void): void {
107 | this.ev.on(event, callback)
108 | }
109 |
110 | emit (event: string, data: any): void {
111 | this.socket.emit(event, data)
112 | }
113 |
114 | async disconnect (): Promise {
115 | await this.socket.disconnect()
116 | }
117 |
118 | async send (data: any): Promise {
119 | this.socket.send(data)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/domain/usecases/connectionSocket.ts:
--------------------------------------------------------------------------------
1 | export interface ConnectionSocket {
2 | connect: (options: ConnectionSocket.Options) => Promise
3 | on: (event: string, callback: (data: any) => void) => void
4 | emit: (event: string, data: any) => void
5 | send: (data: any) => void
6 | disconnect: () => Promise
7 | }
8 |
9 | export namespace ConnectionSocket {
10 | export interface Options {
11 | url?: string
12 | options?: {
13 | host?: string
14 | origin?: string
15 | headers?: Record
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/domain/usecases/socket.ts:
--------------------------------------------------------------------------------
1 | export interface GenericSocket {
2 | on: (event: keyof T, callback: (data: T[keyof T]) => void) => void
3 | emit: (event: string, data: any) => void
4 | }
5 |
6 | export interface Socket extends GenericSocket {
7 | connect: (options: Socket.Options) => Promise
8 | send: (data: any) => void
9 | disconnect: () => Promise
10 | }
11 |
12 | export declare interface SocketEvents {
13 | 'subscriptions': string[]
14 | 'close': {
15 | code: number
16 | }
17 | }
18 |
19 | export namespace Socket {
20 | export interface Options {
21 | url?: string
22 | type?: string
23 | token?: string
24 | reconnect?: boolean
25 | options?: {
26 | host?: string
27 | origin?: string
28 | headers?: Record
29 | }
30 | timeoutPing?: number
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/infra/app/socket.ts:
--------------------------------------------------------------------------------
1 | import { type ConnectionSocket } from '../../domain/usecases/connectionSocket'
2 | import WebSocket from 'ws'
3 |
4 | export class NodeConnectionSocket implements ConnectionSocket {
5 | private socket: WebSocket | null = null
6 |
7 | async connect (options: ConnectionSocket.Options): Promise {
8 | if (!options.url) {
9 | throw new Error('Missing url')
10 | }
11 | this.socket = new WebSocket(options.url, options.options)
12 | }
13 |
14 | async on (event: string, callback: (data: any) => void): Promise {
15 | if (!this.socket) {
16 | throw new Error('Missing socket')
17 | }
18 | this.socket.on(event, callback)
19 | }
20 |
21 | async emit (event: string, data: any): Promise {
22 | if (!this.socket) {
23 | throw new Error('Missing socket')
24 | }
25 | this.socket.emit(event, data)
26 | }
27 |
28 | async disconnect (): Promise {
29 | if (!this.socket) {
30 | throw new Error('Missing socket')
31 | }
32 | this.socket.close()
33 | }
34 |
35 | async send (data: any): Promise {
36 | if (!this.socket) {
37 | throw new Error('Missing socket')
38 | }
39 | this.socket.send(data)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/infra/blaze/blaze-url.ts:
--------------------------------------------------------------------------------
1 | export const getBlazeUrl = (type: 'games' | 'general'): string => {
2 | switch (type) {
3 | case 'games':
4 | return 'wss://api-gaming.blaze.bet.br/replication/?EIO=3&transport=websocket'
5 | case 'general':
6 | return 'wss://api-v2.blaze.bet.br/replication/?EIO=3&transport=websocket'
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-ts-comment */
2 | /* eslint-disable @typescript-eslint/prefer-ts-expect-error */
3 | import { BlazeMessageSocket } from '../data/usecases/blaze-messages-socket'
4 | import { BlazeSocket } from '../data/usecases/blaze-socket'
5 | import { NodeConnectionSocket } from '../infra/app/socket'
6 | import { getBlazeUrl } from '../infra/blaze/blaze-url'
7 |
8 | export interface ConnectionBlaze {
9 | web: 'blaze' | 'blaze-chat'
10 | type: 'crash' | 'doubles' | 'crash_2' | 'crash_neymarjr'
11 | }
12 | export type Connection = {
13 | url?: string
14 | type?: string
15 | token?: string
16 | options?: {
17 | host?: string
18 | origin?: string
19 | headers?: Record
20 | }
21 | timeoutPing?: number
22 | cacheIgnoreRepeatedEvents?: boolean
23 | } & (ConnectionBlaze)
24 |
25 | export type ConnectionSocketResponses = BlazeSocket | BlazeMessageSocket
26 |
27 | export const makeConnection = async ({
28 | type,
29 | web,
30 | options,
31 | token,
32 | timeoutPing,
33 | url,
34 | cacheIgnoreRepeatedEvents = true
35 | }: Connection): Promise => {
36 | switch (web) {
37 | case 'blaze': {
38 | const socketOptions = {
39 | url: url ?? getBlazeUrl('games'),
40 | type,
41 | token,
42 | options: {
43 | headers: options?.headers ?? {
44 | Upgrade: 'websocket',
45 | 'Sec-Webscoket-Extensions': 'permessage-defalte; client_max_window_bits',
46 | Pragma: 'no-cache',
47 | Connection: 'Upgrade',
48 | 'Accept-Encoding': 'gzip, deflate, br',
49 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'
50 | },
51 | host: 'api-v2.blaze1.space',
52 | origin: 'https://api-gaming.blaze.com',
53 | ...options
54 | },
55 | timeoutPing
56 | }
57 | const socket = new NodeConnectionSocket()
58 | const blazeSocket = new BlazeSocket(socket, cacheIgnoreRepeatedEvents)
59 | await blazeSocket.connect(socketOptions)
60 | return blazeSocket
61 | }
62 | case 'blaze-chat': {
63 | const socketOptions = {
64 | url: url ?? getBlazeUrl('general'),
65 | type,
66 | token,
67 | options: {
68 | headers: options?.headers ?? {
69 | Upgrade: 'websocket',
70 | 'Sec-Webscoket-Extensions': 'permessage-defalte; client_max_window_bits',
71 | Pragma: 'no-cache',
72 | Connection: 'Upgrade',
73 | 'Accept-Encoding': 'gzip, deflate, br',
74 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'
75 | },
76 | host: 'api-v2.blaze1.space',
77 | origin: 'https://api-gaming.blaze.com',
78 | ...options
79 | },
80 | timeoutPing
81 | }
82 |
83 | const socketForMessages = new NodeConnectionSocket()
84 | const blazeSocketForMessages = new BlazeMessageSocket(socketForMessages)
85 | await blazeSocketForMessages.connect(socketOptions)
86 |
87 | return blazeSocketForMessages
88 | }
89 | default:
90 | throw new Error('Missing web')
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tests/impl-blaze.test.ts:
--------------------------------------------------------------------------------
1 | import { BlazeSocket } from '../src/data/usecases/blaze-socket'
2 | import { type ConnectionSocket } from '../src/domain/usecases/connectionSocket'
3 |
4 | export const makeConnectionSocket = (): ConnectionSocket => {
5 | class ConnectionSocketStub implements ConnectionSocket {
6 | async connect (options: ConnectionSocket.Options): Promise {
7 | await new Promise(resolve => { resolve() })
8 | }
9 |
10 | on (event: string, callback: (data: any) => void): void {
11 |
12 | }
13 |
14 | emit (event: string, data: any): void {
15 |
16 | }
17 |
18 | send (data: any): void {
19 |
20 | }
21 |
22 | async disconnect (): Promise {
23 | await new Promise(resolve => { resolve() })
24 | }
25 | }
26 | return new ConnectionSocketStub()
27 | }
28 |
29 | interface SutTypes {
30 | sut: BlazeSocket
31 | connectionSocketStub: ConnectionSocket
32 | }
33 |
34 | const makeSut = (cacheIgnoreRepeatedEvents: boolean = true): SutTypes => {
35 | const connectionSocketStub = makeConnectionSocket()
36 | const sut = new BlazeSocket(connectionSocketStub, cacheIgnoreRepeatedEvents)
37 | return {
38 | sut,
39 | connectionSocketStub
40 | }
41 | }
42 |
43 | describe('Implementation test (Crash)', () => {
44 | test('should receive an action waiting (repeat)', async () => {
45 | const { sut } = makeSut()
46 | await sut.connect({})
47 | sut.on('crash.tick', (data) => {
48 | expect(data.status).toBe('waiting')
49 | })
50 | sut.emit('message', '42["data", {"payload": {"id": "1", "status": "waiting"}}]')
51 | })
52 |
53 | test('should receive an action waiting (not repeat)', async () => {
54 | const { sut } = makeSut()
55 | await sut.connect({})
56 | let count = 0
57 | sut.on('crash.tick', () => {
58 | count++
59 | expect(count).toBe(1)
60 | })
61 | sut.emit('message', '42["data", {"payload": {"id": "1", "status": "waiting"}}]')
62 | sut.emit('message', '42["data", {"payload": {"id": "1", "status": "waiting"}}]')
63 | sut.emit('message', '42["data", {"payload": {"id": "1", "status": "waiting"}}]')
64 | })
65 | test('should on close, emits event', async () => {
66 | const { sut } = makeSut()
67 | await sut.connect({})
68 | sut.on('close', (data) => {
69 | expect(data).toBe('error')
70 | })
71 | sut.emit('close', 'error')
72 | })
73 | })
74 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | // "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26 |
27 | /* Modules */
28 | "module": "commonjs", /* Specify what module code is generated. */
29 | // "rootDir": "./", /* Specify the root folder within your source files. */
30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
38 | // "resolveJsonModule": true, /* Enable importing .json files. */
39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
40 |
41 | /* JavaScript Support */
42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
45 |
46 | /* Emit */
47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
51 | // "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. */
52 | // "outDir": "./", /* Specify an output folder for all emitted files. */
53 | // "removeComments": true, /* Disable emitting comments. */
54 | // "noEmit": true, /* Disable emitting files from a compilation. */
55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
63 | // "newLine": "crlf", /* Set the newline character for emitting files. */
64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
70 |
71 | /* Interop Constraints */
72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
77 |
78 | /* Type Checking */
79 | "strict": true, /* Enable all strict type-checking options. */
80 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
98 |
99 | /* Completeness */
100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
102 | }
103 | }
104 |
--------------------------------------------------------------------------------