chore: apply code fixes with biome

This commit is contained in:
PalmDevs
2023-11-28 22:03:41 +07:00
parent c80bd068fa
commit f2d85c32a4
32 changed files with 1384 additions and 1383 deletions

View File

@@ -1,9 +1,9 @@
{ {
"$schema": "./config.schema.json", "$schema": "./config.schema.json",
"address": "127.0.0.1", "address": "127.0.0.1",
"port": 3000, "port": 3000,
"ocrConcurrentQueues": 1, "ocrConcurrentQueues": 1,
"clientHeartbeatInterval": 5000, "clientHeartbeatInterval": 5000,
"debugLogsInProduction": false "debugLogsInProduction": false
} }

View File

@@ -1,31 +1,31 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"type": "object", "type": "object",
"properties": { "properties": {
"address": { "address": {
"description": "Address to listen on", "description": "Address to listen on",
"type": "string", "type": "string",
"default": "127.0.0.1" "default": "127.0.0.1"
}, },
"port": { "port": {
"description": "Port to listen on", "description": "Port to listen on",
"type": "integer", "type": "integer",
"default": 80 "default": 80
}, },
"ocrConcurrentQueues": { "ocrConcurrentQueues": {
"description": "Number of concurrent queues for OCR", "description": "Number of concurrent queues for OCR",
"type": "integer", "type": "integer",
"default": 1 "default": 1
}, },
"clientHeartbeatInterval": { "clientHeartbeatInterval": {
"description": "Time in milliseconds to wait for a client to send a heartbeat packet, if no packet is received, the server will wait for `clientHeartbeatExtraTime` milliseconds before disconnecting the client", "description": "Time in milliseconds to wait for a client to send a heartbeat packet, if no packet is received, the server will wait for `clientHeartbeatExtraTime` milliseconds before disconnecting the client",
"type": "integer", "type": "integer",
"default": 60000 "default": 60000
}, },
"debugLogsInProduction": { "debugLogsInProduction": {
"description": "Whether to print debug logs in production", "description": "Whether to print debug logs in production",
"type": "boolean", "type": "boolean",
"default": false "default": false
}
} }
}
} }

View File

@@ -1,3 +1,4 @@
import { EventEmitter } from 'node:events'
import { import {
ClientOperation, ClientOperation,
DisconnectReason, DisconnectReason,
@@ -8,7 +9,6 @@ import {
serializePacket, serializePacket,
uncapitalize, uncapitalize,
} from '@revanced/bot-shared' } from '@revanced/bot-shared'
import { EventEmitter } from 'node:events'
import type TypedEmitter from 'typed-emitter' import type TypedEmitter from 'typed-emitter'
import type { RawData, WebSocket } from 'ws' import type { RawData, WebSocket } from 'ws'
@@ -16,7 +16,7 @@ import type { RawData, WebSocket } from 'ws'
export default class Client { export default class Client {
id: string id: string
disconnected: DisconnectReason | false = false disconnected: DisconnectReason | false = false
ready: boolean = false ready = false
lastHeartbeat: number = null! lastHeartbeat: number = null!
heartbeatInterval: number heartbeatInterval: number
@@ -55,21 +55,21 @@ export default class Client {
on<TOpName extends keyof ClientEventHandlers>( on<TOpName extends keyof ClientEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientEventHandlers[typeof name] handler: ClientEventHandlers[typeof name],
) { ) {
this.#emitter.on(name, handler) this.#emitter.on(name, handler)
} }
once<TOpName extends keyof ClientEventHandlers>( once<TOpName extends keyof ClientEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientEventHandlers[typeof name] handler: ClientEventHandlers[typeof name],
) { ) {
this.#emitter.once(name, handler) this.#emitter.once(name, handler)
} }
off<TOpName extends keyof ClientEventHandlers>( off<TOpName extends keyof ClientEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientEventHandlers[typeof name] handler: ClientEventHandlers[typeof name],
) { ) {
this.#emitter.off(name, handler) this.#emitter.off(name, handler)
} }
@@ -78,11 +78,11 @@ export default class Client {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
try { try {
this.#throwIfDisconnected( this.#throwIfDisconnected(
'Cannot send packet to client that has already disconnected' 'Cannot send packet to client that has already disconnected',
) )
this.#socket.send(serializePacket(packet), err => this.#socket.send(serializePacket(packet), err =>
err ? reject(err) : resolve() err ? reject(err) : resolve(),
) )
} catch (e) { } catch (e) {
reject(e) reject(e)
@@ -92,14 +92,14 @@ export default class Client {
async disconnect(reason: DisconnectReason = DisconnectReason.Generic) { async disconnect(reason: DisconnectReason = DisconnectReason.Generic) {
this.#throwIfDisconnected( this.#throwIfDisconnected(
'Cannot disconnect client that has already disconnected' 'Cannot disconnect client that has already disconnected',
) )
try { try {
await this.send({ op: ServerOperation.Disconnect, d: { reason } }) await this.send({ op: ServerOperation.Disconnect, d: { reason } })
} catch (err) { } catch (err) {
throw new Error( throw new Error(
`Cannot send disconnect reason to client ${this.id}: ${err}` `Cannot send disconnect reason to client ${this.id}: ${err}`,
) )
} finally { } finally {
this.forceDisconnect(reason) this.forceDisconnect(reason)
@@ -142,7 +142,7 @@ export default class Client {
this.#emitter.emit( this.#emitter.emit(
uncapitalize(ClientOperation[packet.op] as ClientEventName), uncapitalize(ClientOperation[packet.op] as ClientEventName),
// @ts-expect-error TypeScript doesn't know that the above line will negate the type enough // @ts-expect-error TypeScript doesn't know that the above line will negate the type enough
packet packet,
) )
} catch (e) { } catch (e) {
// TODO: add error fields to sent packet so we can log what went wrong // TODO: add error fields to sent packet so we can log what went wrong
@@ -175,7 +175,7 @@ export default class Client {
// 5000 is extra time to account for latency // 5000 is extra time to account for latency
const interval = setTimeout( const interval = setTimeout(
() => this.disconnect(DisconnectReason.TimedOut), () => this.disconnect(DisconnectReason.TimedOut),
5000 5000,
) )
this.once('heartbeat', () => clearTimeout(interval)) this.once('heartbeat', () => clearTimeout(interval))
@@ -207,12 +207,12 @@ export type ClientEventName = keyof typeof ClientOperation
export type ClientEventHandlers = { export type ClientEventHandlers = {
[K in Uncapitalize<ClientEventName>]: ( [K in Uncapitalize<ClientEventName>]: (
packet: ClientPacketObject<(typeof ClientOperation)[Capitalize<K>]> packet: ClientPacketObject<typeof ClientOperation[Capitalize<K>]>,
) => Promise<void> | void ) => Promise<void> | void
} & { } & {
ready: () => Promise<void> | void ready: () => Promise<void> | void
packet: ( packet: (
packet: ClientPacketObject<ClientOperation> packet: ClientPacketObject<ClientOperation>,
) => Promise<void> | void ) => Promise<void> | void
disconnect: (reason: DisconnectReason) => Promise<void> | void disconnect: (reason: DisconnectReason) => Promise<void> | void
} }

View File

@@ -1,16 +1,16 @@
import type { ClientOperation } from '@revanced/bot-shared' import type { ClientOperation } from '@revanced/bot-shared'
import type { Wit } from 'node-wit' import type { Wit } from 'node-wit'
import type { Worker as TesseractWorker } from 'tesseract.js'
import { ClientPacketObject } from '../classes/Client.js' import { ClientPacketObject } from '../classes/Client.js'
import type { Config } from '../utils/getConfig.js' import type { Config } from '../utils/getConfig.js'
import type { Logger } from '../utils/logger.js' import type { Logger } from '../utils/logger.js'
import type { Worker as TesseractWorker } from 'tesseract.js'
export { default as parseTextEventHandler } from './parseText.js' export { default as parseTextEventHandler } from './parseText.js'
export { default as parseImageEventHandler } from './parseImage.js' export { default as parseImageEventHandler } from './parseImage.js'
export type EventHandler<POp extends ClientOperation> = ( export type EventHandler<POp extends ClientOperation> = (
packet: ClientPacketObject<POp>, packet: ClientPacketObject<POp>,
context: EventContext context: EventContext,
) => void | Promise<void> ) => void | Promise<void>
export type EventContext = { export type EventContext = {
witClient: Wit witClient: Wit

View File

@@ -7,7 +7,7 @@ const queue = new AsyncQueue()
const parseImageEventHandler: EventHandler<ClientOperation.ParseImage> = async ( const parseImageEventHandler: EventHandler<ClientOperation.ParseImage> = async (
packet, packet,
{ tesseractWorker, logger, config } { tesseractWorker, logger, config },
) => { ) => {
const { const {
client, client,
@@ -16,10 +16,10 @@ const parseImageEventHandler: EventHandler<ClientOperation.ParseImage> = async (
logger.debug( logger.debug(
`Client ${client.id} requested to parse image from URL:`, `Client ${client.id} requested to parse image from URL:`,
imageUrl imageUrl,
) )
logger.debug( logger.debug(
`Queue currently has ${queue.remaining}/${config.ocrConcurrentQueues} items in it` `Queue currently has ${queue.remaining}/${config.ocrConcurrentQueues} items in it`,
) )
if (queue.remaining < config.ocrConcurrentQueues) queue.shift() if (queue.remaining < config.ocrConcurrentQueues) queue.shift()
@@ -32,7 +32,7 @@ const parseImageEventHandler: EventHandler<ClientOperation.ParseImage> = async (
logger.debug( logger.debug(
`Recognized image from URL for client ${client.id} (job ${jobId}):`, `Recognized image from URL for client ${client.id} (job ${jobId}):`,
data.text data.text,
) )
await client.send({ await client.send({
op: ServerOperation.ParsedImage, op: ServerOperation.ParsedImage,
@@ -44,7 +44,7 @@ const parseImageEventHandler: EventHandler<ClientOperation.ParseImage> = async (
} catch { } catch {
logger.error( logger.error(
`Failed to parse image from URL for client ${client.id}:`, `Failed to parse image from URL for client ${client.id}:`,
imageUrl imageUrl,
) )
await client.send({ await client.send({
op: ServerOperation.ParseImageFailed, op: ServerOperation.ParseImageFailed,
@@ -55,7 +55,7 @@ const parseImageEventHandler: EventHandler<ClientOperation.ParseImage> = async (
} finally { } finally {
queue.shift() queue.shift()
logger.debug( logger.debug(
`Finished processing image from URL for client ${client.id}, queue has ${queue.remaining}/${config.ocrConcurrentQueues} remaining items in it` `Finished processing image from URL for client ${client.id}, queue has ${queue.remaining}/${config.ocrConcurrentQueues} remaining items in it`,
) )
} }
} }

View File

@@ -6,7 +6,7 @@ import type { EventHandler } from './index.js'
const parseTextEventHandler: EventHandler<ClientOperation.ParseText> = async ( const parseTextEventHandler: EventHandler<ClientOperation.ParseText> = async (
packet, packet,
{ witClient, logger } { witClient, logger },
) => { ) => {
const { const {
client, client,

View File

@@ -1,8 +1,8 @@
import { fastify } from 'fastify'
import fastifyWebsocket from '@fastify/websocket' import fastifyWebsocket from '@fastify/websocket'
import { fastify } from 'fastify'
import { createWorker as createTesseractWorker } from 'tesseract.js'
import witPkg from 'node-wit' import witPkg from 'node-wit'
import { createWorker as createTesseractWorker } from 'tesseract.js'
const { Wit } = witPkg const { Wit } = witPkg
import { inspect as inspectObject } from 'node:util' import { inspect as inspectObject } from 'node:util'
@@ -15,12 +15,12 @@ import {
parseTextEventHandler, parseTextEventHandler,
} from './events/index.js' } from './events/index.js'
import { getConfig, checkEnv, logger } from './utils/index.js'
import { WebSocket } from 'ws'
import { import {
DisconnectReason, DisconnectReason,
HumanizedDisconnectReason, HumanizedDisconnectReason,
} from '@revanced/bot-shared' } from '@revanced/bot-shared'
import { WebSocket } from 'ws'
import { checkEnv, getConfig, logger } from './utils/index.js'
// Check environment variables and load config // Check environment variables and load config
const environment = checkEnv(logger) const environment = checkEnv(logger)
@@ -73,22 +73,22 @@ const server = fastify()
logger.debug(`Client ${client.id}'s instance has been added`) logger.debug(`Client ${client.id}'s instance has been added`)
logger.info( logger.info(
`New client connected (now ${clients.size} clients) with ID:`, `New client connected (now ${clients.size} clients) with ID:`,
client.id client.id,
) )
client.on('disconnect', reason => { client.on('disconnect', reason => {
clients.delete(client) clients.delete(client)
logger.info( logger.info(
`Client ${client.id} disconnected because client ${HumanizedDisconnectReason[reason]}` `Client ${client.id} disconnected because client ${HumanizedDisconnectReason[reason]}`,
) )
}) })
client.on('parseText', async packet => client.on('parseText', async packet =>
parseTextEventHandler(packet, eventContext) parseTextEventHandler(packet, eventContext),
) )
client.on('parseImage', async packet => client.on('parseImage', async packet =>
parseImageEventHandler(packet, eventContext) parseImageEventHandler(packet, eventContext),
) )
if ( if (
@@ -96,20 +96,20 @@ const server = fastify()
!config.debugLogsInProduction !config.debugLogsInProduction
) { ) {
logger.debug( logger.debug(
'Running development mode or debug logs in production is enabled, attaching debug events...' 'Running development mode or debug logs in production is enabled, attaching debug events...',
) )
client.on('packet', ({ client, ...rawPacket }) => client.on('packet', ({ client, ...rawPacket }) =>
logger.debug( logger.debug(
`Packet received from client ${client.id}:`, `Packet received from client ${client.id}:`,
inspectObject(rawPacket) inspectObject(rawPacket),
) ),
) )
client.on('heartbeat', () => client.on('heartbeat', () =>
logger.debug( logger.debug(
'Heartbeat received from client', 'Heartbeat received from client',
client.id client.id,
) ),
) )
} }
} catch (e) { } catch (e) {
@@ -120,7 +120,7 @@ const server = fastify()
if (!client) { if (!client) {
logger.error( logger.error(
'Missing client instance when encountering an error. If the instance still exists in memory, it will NOT be removed!' 'Missing client instance when encountering an error. If the instance still exists in memory, it will NOT be removed!',
) )
return connection.socket.terminate() return connection.socket.terminate()
} }
@@ -132,7 +132,7 @@ const server = fastify()
clients.delete(client) clients.delete(client)
logger.debug( logger.debug(
`Client ${client.id} disconnected because of an internal error` `Client ${client.id} disconnected because of an internal error`,
) )
} }
}) })
@@ -153,5 +153,5 @@ if (!addressInfo || typeof addressInfo !== 'object')
else else
logger.info( logger.info(
'Server started at:', 'Server started at:',
`${addressInfo.address}:${addressInfo.port}` `${addressInfo.address}:${addressInfo.port}`,
) )

View File

@@ -8,7 +8,7 @@ export default function checkEnv(logger: Logger) {
if (!['development', 'production'].includes(environment)) { if (!['development', 'production'].includes(environment)) {
logger.error( logger.error(
'NODE_ENV is neither `development` nor `production`, unable to determine environment' 'NODE_ENV is neither `development` nor `production`, unable to determine environment',
) )
logger.info('Set NODE_ENV to blank to use `development` mode') logger.info('Set NODE_ENV to blank to use `development` mode')
process.exit(1) process.exit(1)
@@ -18,7 +18,7 @@ export default function checkEnv(logger: Logger) {
if (environment === 'production' && process.env['IS_USING_DOT_ENV']) { if (environment === 'production' && process.env['IS_USING_DOT_ENV']) {
logger.warn( logger.warn(
'You seem to be using .env files, this is generally not a good idea in production...' 'You seem to be using .env files, this is generally not a good idea in production...',
) )
} }

View File

@@ -17,10 +17,10 @@ const userConfig: Partial<Config> = existsSync(configPath)
type BaseTypeOf<T> = T extends (infer U)[] type BaseTypeOf<T> = T extends (infer U)[]
? U[] ? U[]
: T extends (...args: unknown[]) => infer U : T extends (...args: unknown[]) => infer U
? (...args: unknown[]) => U ? (...args: unknown[]) => U
: T extends object : T extends object
? { [K in keyof T]: T[K] } ? { [K in keyof T]: T[K] }
: T : T
export type Config = Omit< export type Config = Omit<
BaseTypeOf<typeof import('../../config.json')>, BaseTypeOf<typeof import('../../config.json')>,

View File

@@ -8,12 +8,12 @@ const logger = {
warn: (...args) => warn: (...args) =>
console.warn( console.warn(
chalk.bgYellow.blackBright.bold(' WARN '), chalk.bgYellow.blackBright.bold(' WARN '),
chalk.yellowBright(...args) chalk.yellowBright(...args),
), ),
error: (...args) => error: (...args) =>
console.error( console.error(
chalk.bgRed.whiteBright.bold(' ERROR '), chalk.bgRed.whiteBright.bold(' ERROR '),
chalk.redBright(...args) chalk.redBright(...args),
), ),
log: console.log, log: console.log,
} satisfies Logger } satisfies Logger

View File

@@ -5,9 +5,9 @@ import ClientGateway, { ClientGatewayEventHandlers } from './ClientGateway.js'
* The client that connects to the API. * The client that connects to the API.
*/ */
export default class Client { export default class Client {
ready: boolean = false ready = false
gateway: ClientGateway gateway: ClientGateway
#parseId: number = 0 #parseId = 0
constructor(options: ClientOptions) { constructor(options: ClientOptions) {
this.gateway = new ClientGateway({ this.gateway = new ClientGateway({
@@ -63,7 +63,7 @@ export default class Client {
} }
const parseTextFailedListener = ( const parseTextFailedListener = (
packet: Packet<ServerOperation.ParseTextFailed> packet: Packet<ServerOperation.ParseTextFailed>,
) => { ) => {
if (packet.d.id !== currentId) return if (packet.d.id !== currentId) return
this.gateway.off('parseTextFailed', parseTextFailedListener) this.gateway.off('parseTextFailed', parseTextFailedListener)
@@ -105,7 +105,7 @@ export default class Client {
} }
const parseImageFailedListener = ( const parseImageFailedListener = (
packet: Packet<ServerOperation.ParseImageFailed> packet: Packet<ServerOperation.ParseImageFailed>,
) => { ) => {
if (packet.d.id !== currentId) return if (packet.d.id !== currentId) return
this.gateway.off('parseImageFailed', parseImageFailedListener) this.gateway.off('parseImageFailed', parseImageFailedListener)
@@ -127,7 +127,7 @@ export default class Client {
*/ */
on<TOpName extends keyof ClientGatewayEventHandlers>( on<TOpName extends keyof ClientGatewayEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientGatewayEventHandlers[TOpName] handler: ClientGatewayEventHandlers[TOpName],
) { ) {
this.gateway.on(name, handler) this.gateway.on(name, handler)
return handler return handler
@@ -141,7 +141,7 @@ export default class Client {
*/ */
off<TOpName extends keyof ClientGatewayEventHandlers>( off<TOpName extends keyof ClientGatewayEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientGatewayEventHandlers[TOpName] handler: ClientGatewayEventHandlers[TOpName],
) { ) {
this.gateway.off(name, handler) this.gateway.off(name, handler)
return handler return handler
@@ -155,7 +155,7 @@ export default class Client {
*/ */
once<TOpName extends keyof ClientGatewayEventHandlers>( once<TOpName extends keyof ClientGatewayEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientGatewayEventHandlers[TOpName] handler: ClientGatewayEventHandlers[TOpName],
) { ) {
this.gateway.once(name, handler) this.gateway.once(name, handler)
return handler return handler

View File

@@ -1,5 +1,4 @@
import { type RawData, WebSocket } from 'ws' import { EventEmitter } from 'events'
import type TypedEmitter from 'typed-emitter'
import { import {
ClientOperation, ClientOperation,
DisconnectReason, DisconnectReason,
@@ -10,7 +9,8 @@ import {
serializePacket, serializePacket,
uncapitalize, uncapitalize,
} from '@revanced/bot-shared' } from '@revanced/bot-shared'
import { EventEmitter } from 'events' import type TypedEmitter from 'typed-emitter'
import { type RawData, WebSocket } from 'ws'
/** /**
* The class that handles the WebSocket connection to the server. * The class that handles the WebSocket connection to the server.
@@ -18,7 +18,7 @@ import { EventEmitter } from 'events'
*/ */
export default class ClientGateway { export default class ClientGateway {
readonly url: string readonly url: string
ready: boolean = false ready = false
disconnected: boolean | DisconnectReason = DisconnectReason.NeverConnected disconnected: boolean | DisconnectReason = DisconnectReason.NeverConnected
config: Readonly<Packet<ServerOperation.Hello>['d']> | null = null! config: Readonly<Packet<ServerOperation.Hello>['d']> | null = null!
@@ -45,7 +45,7 @@ export default class ClientGateway {
}) })
this.#socket.on('close', () => this.#socket.on('close', () =>
this.#handleDisconnect(DisconnectReason.Generic) this.#handleDisconnect(DisconnectReason.Generic),
) )
this.#listen() this.#listen()
@@ -65,7 +65,7 @@ export default class ClientGateway {
*/ */
on<TOpName extends keyof ClientGatewayEventHandlers>( on<TOpName extends keyof ClientGatewayEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientGatewayEventHandlers[typeof name] handler: ClientGatewayEventHandlers[typeof name],
) { ) {
this.#emitter.on(name, handler) this.#emitter.on(name, handler)
} }
@@ -78,7 +78,7 @@ export default class ClientGateway {
*/ */
off<TOpName extends keyof ClientGatewayEventHandlers>( off<TOpName extends keyof ClientGatewayEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientGatewayEventHandlers[typeof name] handler: ClientGatewayEventHandlers[typeof name],
) { ) {
this.#emitter.off(name, handler) this.#emitter.off(name, handler)
} }
@@ -91,7 +91,7 @@ export default class ClientGateway {
*/ */
once<TOpName extends keyof ClientGatewayEventHandlers>( once<TOpName extends keyof ClientGatewayEventHandlers>(
name: TOpName, name: TOpName,
handler: ClientGatewayEventHandlers[typeof name] handler: ClientGatewayEventHandlers[typeof name],
) { ) {
this.#emitter.once(name, handler) this.#emitter.once(name, handler)
} }
@@ -103,13 +103,13 @@ export default class ClientGateway {
*/ */
send<TOp extends ClientOperation>(packet: Packet<TOp>) { send<TOp extends ClientOperation>(packet: Packet<TOp>) {
this.#throwIfDisconnected( this.#throwIfDisconnected(
'Cannot send a packet when already disconnected from the server' 'Cannot send a packet when already disconnected from the server',
) )
return new Promise<void>((resolve, reject) => return new Promise<void>((resolve, reject) =>
this.#socket.send(serializePacket(packet), err => this.#socket.send(serializePacket(packet), err =>
err ? reject(err) : resolve() err ? reject(err) : resolve(),
) ),
) )
} }
@@ -118,7 +118,7 @@ export default class ClientGateway {
*/ */
disconnect() { disconnect() {
this.#throwIfDisconnected( this.#throwIfDisconnected(
'Cannot disconnect when already disconnected from the server' 'Cannot disconnect when already disconnected from the server',
) )
this.#handleDisconnect(DisconnectReason.Generic) this.#handleDisconnect(DisconnectReason.Generic)
@@ -142,28 +142,29 @@ export default class ClientGateway {
this.#emitter.emit('packet', packet) this.#emitter.emit('packet', packet)
switch (packet.op) { switch (packet.op) {
case ServerOperation.Hello: case ServerOperation.Hello: {
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
const data = Object.freeze( const data = Object.freeze(
(packet as Packet<ServerOperation.Hello>).d (packet as Packet<ServerOperation.Hello>).d,
) )
this.config = data this.config = data
this.#emitter.emit('hello', data) this.#emitter.emit('hello', data)
this.#startHeartbeating() this.#startHeartbeating()
break break
}
case ServerOperation.Disconnect: case ServerOperation.Disconnect:
return this.#handleDisconnect( return this.#handleDisconnect(
(packet as Packet<ServerOperation.Disconnect>).d.reason (packet as Packet<ServerOperation.Disconnect>).d.reason,
) )
default: default:
return this.#emitter.emit( return this.#emitter.emit(
uncapitalize( uncapitalize(
ServerOperation[ ServerOperation[
packet.op packet.op
] as ClientGatewayServerEventName ] as ClientGatewayServerEventName,
), ),
// @ts-expect-error TypeScript doesn't know that the lines above negate the type enough // @ts-expect-error TypeScript doesn't know that the lines above negate the type enough
packet packet,
) )
} }
}) })
@@ -218,11 +219,11 @@ export type ClientGatewayServerEventName = keyof typeof ServerOperation
export type ClientGatewayEventHandlers = { export type ClientGatewayEventHandlers = {
[K in Uncapitalize<ClientGatewayServerEventName>]: ( [K in Uncapitalize<ClientGatewayServerEventName>]: (
packet: Packet<(typeof ServerOperation)[Capitalize<K>]> packet: Packet<typeof ServerOperation[Capitalize<K>]>,
) => Promise<void> | void ) => Promise<void> | void
} & { } & {
hello: ( hello: (
config: NonNullable<ClientGateway['config']> config: NonNullable<ClientGateway['config']>,
) => Promise<void> | void ) => Promise<void> | void
ready: () => Promise<void> | void ready: () => Promise<void> | void
packet: (packet: Packet<ServerOperation>) => Promise<void> | void packet: (packet: Packet<ServerOperation>) => Promise<void> | void

View File

@@ -9,19 +9,19 @@ enum DisconnectReason {
/** /**
* The client did not respond in time * The client did not respond in time
*/ */
TimedOut, TimedOut = 2,
/** /**
* The client sent an invalid packet (unserializable or invalid JSON) * The client sent an invalid packet (unserializable or invalid JSON)
*/ */
InvalidPacket, InvalidPacket = 3,
/** /**
* The server has encountered an internal error * The server has encountered an internal error
*/ */
ServerError, ServerError = 4,
/** /**
* The client had never connected to the server (**CLIENT-ONLY**) * The client had never connected to the server (**CLIENT-ONLY**)
*/ */
NeverConnected, NeverConnected = 5,
} }
export default DisconnectReason export default DisconnectReason

View File

@@ -14,7 +14,7 @@ export enum ClientOperation {
/** /**
* Client's request to parse image * Client's request to parse image
*/ */
ParseImage, ParseImage = 111,
} }
/** /**
@@ -28,7 +28,7 @@ export enum ServerOperation {
/** /**
* Server's initial response to a client's connection * Server's initial response to a client's connection
*/ */
Hello, Hello = 2,
/** /**
* Server's response to client's request to parse text * Server's response to client's request to parse text
@@ -37,15 +37,15 @@ export enum ServerOperation {
/** /**
* Server's response to client's request to parse image * Server's response to client's request to parse image
*/ */
ParsedImage, ParsedImage = 11,
/** /**
* Server's failure response to client's request to parse text * Server's failure response to client's request to parse text
*/ */
ParseTextFailed, ParseTextFailed = 12,
/** /**
* Server's failure response to client's request to parse image * Server's failure response to client's request to parse image
*/ */
ParseImageFailed, ParseImageFailed = 13,
/** /**
* Server's disconnect message * Server's disconnect message

View File

@@ -1,25 +1,25 @@
import {
url,
AnySchema,
NullSchema,
ObjectSchema,
Output,
array,
enum_,
null_,
number,
object,
parse,
special,
string,
// merge
} from 'valibot'
import DisconnectReason from '../constants/DisconnectReason.js' import DisconnectReason from '../constants/DisconnectReason.js'
import { import {
ClientOperation, ClientOperation,
Operation, Operation,
ServerOperation, ServerOperation,
} from '../constants/Operation.js' } from '../constants/Operation.js'
import {
object,
enum_,
special,
ObjectSchema,
number,
string,
Output,
AnySchema,
null_,
NullSchema,
array,
url,
parse,
// merge
} from 'valibot'
/** /**
* Schema to validate packets * Schema to validate packets
@@ -61,9 +61,9 @@ export const PacketDataSchemas = {
name: string(), name: string(),
confidence: special<number>( confidence: special<number>(
input => input =>
typeof input === 'number' && input >= 0 && input <= 1 typeof input === 'number' && input >= 0 && input <= 1,
), ),
}) }),
), ),
}), }),
[ServerOperation.ParsedImage]: object({ [ServerOperation.ParsedImage]: object({
@@ -91,11 +91,11 @@ export const PacketDataSchemas = {
}), }),
} as const satisfies Record< } as const satisfies Record<
Operation, Operation,
// eslint-disable-next-line @typescript-eslint/no-explicit-any // biome-ignore lint/suspicious/noExplicitAny: This is a schema, it's not possible to type it
ObjectSchema<any> | AnySchema | NullSchema ObjectSchema<any> | AnySchema | NullSchema
> >
export type Packet<TOp extends Operation = Operation> = { export type Packet<TOp extends Operation = Operation> = {
op: TOp op: TOp
d: Output<(typeof PacketDataSchemas)[TOp]> d: Output<typeof PacketDataSchemas[TOp]>
} }

View File

@@ -1,9 +1,9 @@
import { Packet } from '../schemas/Packet.js'
import { import {
ClientOperation, ClientOperation,
Operation, Operation,
ServerOperation, ServerOperation,
} from '../constants/Operation.js' } from '../constants/Operation.js'
import { Packet } from '../schemas/Packet.js'
/** /**
* Checks whether a packet is trying to do the given operation * Checks whether a packet is trying to do the given operation
@@ -13,7 +13,7 @@ import {
*/ */
export function packetMatchesOperation<TOp extends Operation>( export function packetMatchesOperation<TOp extends Operation>(
op: TOp, op: TOp,
packet: Packet packet: Packet,
): packet is Packet<TOp> { ): packet is Packet<TOp> {
return packet.op === op return packet.op === op
} }
@@ -24,7 +24,7 @@ export function packetMatchesOperation<TOp extends Operation>(
* @returns Whether this packet is a client packet * @returns Whether this packet is a client packet
*/ */
export function isClientPacket( export function isClientPacket(
packet: Packet packet: Packet,
): packet is Packet<ClientOperation> { ): packet is Packet<ClientOperation> {
return packet.op in ClientOperation return packet.op in ClientOperation
} }
@@ -35,7 +35,7 @@ export function isClientPacket(
* @returns Whether this packet is a server packet * @returns Whether this packet is a server packet
*/ */
export function isServerPacket( export function isServerPacket(
packet: Packet packet: Packet,
): packet is Packet<ServerOperation> { ): packet is Packet<ServerOperation> {
return packet.op in ServerOperation return packet.op in ServerOperation
} }

View File

@@ -1,7 +1,7 @@
import * as BSON from 'bson' import * as BSON from 'bson'
import { Packet, PacketSchema } from '../schemas/index.js'
import { Operation } from '../constants/index.js'
import { parse } from 'valibot' import { parse } from 'valibot'
import { Operation } from '../constants/index.js'
import { Packet, PacketSchema } from '../schemas/index.js'
/** /**
* Compresses a packet into a buffer * Compresses a packet into a buffer

View File

@@ -1,3 +1,3 @@
{ {
"extends": "./tsconfig.base.json" "extends": "./tsconfig.base.json"
} }

View File

@@ -1,15 +1,15 @@
{ {
"extends": "./tsconfig.base.json", "extends": "./tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"declaration": true, "declaration": true,
"declarationMap": true "declarationMap": true
},
"references": [
{
"path": "./packages/shared"
}, },
"references": [ {
{ "path": "./packages/api"
"path": "./packages/shared" }
}, ]
{
"path": "./packages/api"
}
]
} }

View File

@@ -1,14 +1,14 @@
{ {
"$schema": "https://turbo.build/schema.json", "$schema": "https://turbo.build/schema.json",
"pipeline": { "pipeline": {
"build": { "build": {
"dependsOn": ["^build"], "dependsOn": ["^build"],
"outputs": ["dist/**"], "outputs": ["dist/**"],
"outputMode": "errors-only" "outputMode": "errors-only"
}, },
"watch": { "watch": {
"dependsOn": ["^watch"], "dependsOn": ["^watch"],
"outputMode": "errors-only" "outputMode": "errors-only"
}
} }
}
} }